{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceId":112899,"databundleVersionId":13449579,"sourceType":"competition"},{"sourceId":259225999,"sourceType":"kernelVersion"}],"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# -*- coding: utf-8 -*-\n\"\"\"\nKaggle Chest X-ray Submission Notebook\n--------------------------------------\nChỉ load model đã được huấn luyện và tạo file submission.\n\"\"\"\nimport numpy as np\nimport pandas as pd\nimport cv2\nimport os\nimport torch\nimport torch.nn as nn\nfrom torch.utils.data import Dataset, DataLoader\nfrom torchvision import models, transforms\nfrom tqdm.auto import tqdm\nimport warnings\nwarnings.filterwarnings('ignore')\n\n# Cấu hình cơ bản\n# =============================================================================\n# !!! QUAN TRỌNG: CẬP NHẬT ĐƯỜNG DẪN NÀY CHO ĐÚNG !!!\n# Thay 'grand-x-ray-slam-baseline' bằng tên dataset chứa file model của bạn\nMODEL_PATH = '/kaggle/input/grand-x-ray-slam-baseline/best_model_optimized.pth'\n# =============================================================================\n\nIMG_SIZE = 320\nBATCH_SIZE = 32  # Có thể tăng batch size nếu GPU cho phép để chạy nhanh hơn\nNUM_CLASSES = 14\nDEVICE = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n\nprint(f\"Using device: {DEVICE}\")\nprint(f\"Model path: {MODEL_PATH}\")\n\n# =============================================================================\n# 1. ĐỊNH NGHĨA LẠI KIẾN TRÚC MODEL\n# (Phải giống hệt với kiến trúc lúc huấn luyện để load trọng số)\n# =============================================================================\nclass EfficientNetModel(nn.Module):\n    def __init__(self, num_classes=14):\n        super(EfficientNetModel, self).__init__()\n        self.backbone = models.efficientnet_b3(weights=None) # Không cần weights vì sẽ load từ file\n        backbone_dim = self.backbone.classifier[1].in_features\n        self.backbone.classifier = nn.Identity()\n        \n        self.attention = nn.Sequential(\n            nn.Linear(backbone_dim, backbone_dim // 8),\n            nn.ReLU(),\n            nn.Linear(backbone_dim // 8, backbone_dim),\n            nn.Sigmoid()\n        )\n        \n        self.classifier = nn.Sequential(\n            nn.BatchNorm1d(backbone_dim),\n            nn.Dropout(0.5),\n            nn.Linear(backbone_dim, backbone_dim // 2),\n            nn.ReLU(),\n            nn.BatchNorm1d(backbone_dim // 2),\n            nn.Dropout(0.3),\n            nn.Linear(backbone_dim // 2, num_classes)\n        )\n    \n    def forward(self, x):\n        features = self.backbone(x)\n        attention_weights = self.attention(features)\n        attended_features = features * attention_weights\n        output = self.classifier(attended_features)\n        return output\n\n# =============================================================================\n# 2. CÁC PHÉP BIẾN ĐỔI DỮ LIỆU (AUGMENTATION) CHO TEST\n# =============================================================================\ndef get_tta_transforms(img_size=320):\n    \"\"\"Lấy các phép biến đổi cho Test Time Augmentation (TTA).\"\"\"\n    \n    # Transform cơ bản\n    base_transform = transforms.Compose([\n        transforms.ToPILImage(),\n        transforms.Resize((img_size, img_size)),\n        transforms.ToTensor(),\n        transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])\n    ])\n    \n    # Transform lật ngang\n    flipped_transform = transforms.Compose([\n        transforms.ToPILImage(),\n        transforms.Resize((img_size, img_size)),\n        transforms.RandomHorizontalFlip(p=1.0),\n        transforms.ToTensor(),\n        transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])\n    ])\n    \n    return [base_transform, flipped_transform]\n\n# =============================================================================\n# 3. DATASET CHO DỮ LIỆU TEST\n# =============================================================================\nclass ChestXRayDataset(Dataset):\n    def __init__(self, df, img_size, tta_transforms):\n        self.df = df.reset_index(drop=True)\n        self.img_size = img_size\n        self.tta_transforms = tta_transforms\n        self.image_dir = '/kaggle/input/grand-xray-slam-division-a/test1/'\n\n    def __len__(self):\n        return len(self.df)\n    \n    def load_and_preprocess_image(self, img_path):\n        img = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE)\n        \n        if img is None:\n            img = np.zeros((self.img_size, self.img_size), dtype=np.uint8)\n        else:\n            clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8))\n            img = clahe.apply(img)\n        \n        img = cv2.resize(img, (self.img_size, self.img_size))\n        img = cv2.cvtColor(img, cv2.COLOR_GRAY2RGB)\n        return img\n\n    def __getitem__(self, idx):\n        row = self.df.iloc[idx]\n        img_path = os.path.join(self.image_dir, row['Image_name'])\n        img = self.load_and_preprocess_image(img_path)\n\n        images = []\n        for transform in self.tta_transforms:\n            images.append(transform(img.copy()))\n        return torch.stack(images)\n\n# =============================================================================\n# 4. HÀM DỰ ĐOÁN VỚI TTA\n# =============================================================================\ndef predict_with_tta(model, test_loader, device):\n    model.eval()\n    model.to(device)\n    predictions = []\n    \n    with torch.no_grad():\n        for tta_images in tqdm(test_loader, desc='Generating predictions'):\n            tta_images = tta_images.to(device)\n            batch_size, tta_count = tta_images.shape[:2]\n            \n            tta_images = tta_images.view(-1, *tta_images.shape[2:])\n            \n            outputs = model(tta_images)\n            outputs = torch.sigmoid(outputs)\n            \n            outputs = outputs.view(batch_size, tta_count, -1)\n            tta_averaged = outputs.mean(dim=1)\n            \n            predictions.append(tta_averaged.cpu().numpy())\n    \n    return np.vstack(predictions)\n\n# =============================================================================\n# 5. HÀM MAIN: LOAD MODEL VÀ TẠO SUBMISSION\n# =============================================================================\ndef main():\n    # 1. Khởi tạo model và load trọng số\n    print(\"Loading model...\")\n    model = EfficientNetModel(num_classes=NUM_CLASSES)\n    \n    # Thêm `weights_only=False` để tương thích với cách lưu checkpoint\n    checkpoint = torch.load(MODEL_PATH, map_location=DEVICE, weights_only=False)\n    model.load_state_dict(checkpoint['model_state_dict'])\n    print(\"✓ Model loaded successfully.\")\n\n    # 2. Chuẩn bị dữ liệu test\n    print(\"Preparing test data...\")\n    try:\n        sample_submission = pd.read_csv('/kaggle/input/grand-xray-slam-division-a/sample_submission_1.csv')\n    except FileNotFoundError:\n        print(\"Error: sample_submission_1.csv not found!\")\n        return\n    \n    tta_transforms = get_tta_transforms(IMG_SIZE)\n    test_dataset = ChestXRayDataset(sample_submission, IMG_SIZE, tta_transforms)\n    test_loader = DataLoader(\n        test_dataset, batch_size=BATCH_SIZE, shuffle=False, num_workers=2\n    )\n    print(f\"Test data ready: {len(test_dataset)} samples.\")\n    \n    # 3. Chạy dự đoán\n    predictions = predict_with_tta(model, test_loader, DEVICE)\n    \n    # 4. Tạo file submission\n    print(\"Creating submission file...\")\n    label_columns = [\n        'Atelectasis', 'Cardiomegaly', 'Consolidation', 'Edema', \n        'Enlarged Cardiomediastinum', 'Fracture', 'Lung Lesion', \n        'Lung Opacity', 'No Finding', 'Pleural Effusion',\n        'Pleural Other', 'Pneumonia', 'Pneumothorax', 'Support Devices'\n    ]\n    \n    # Đảm bảo shape chính xác\n    predictions = predictions[:len(sample_submission)]\n    \n    submission_df = sample_submission.copy()\n    submission_df[label_columns] = predictions\n    submission_df.to_csv('submission.csv', index=False)\n    \n    print(\"\\n🎉 Submission created successfully: submission.csv\")\n    print(f\"Shape: {submission_df.shape}\")\n    print(\"Submission head:\")\n    print(submission_df.head())\n\nif __name__ == \"__main__\":\n    main()","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"outputs":[],"execution_count":null}]}