{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.11","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":29653,"databundleVersionId":2420395,"sourceType":"competition"}],"dockerImageVersionId":31040,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"!pip install pydicom ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T16:27:20.654088Z","iopub.execute_input":"2025-06-26T16:27:20.654456Z","iopub.status.idle":"2025-06-26T16:27:27.077237Z","shell.execute_reply.started":"2025-06-26T16:27:20.654428Z","shell.execute_reply":"2025-06-26T16:27:27.072162Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Basic imports\nimport os\nimport glob\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nimport pydicom\nimport cv2\n\n# PyTorch\nimport torch\nimport torch.nn as nn\nfrom torch.utils.data import Dataset, DataLoader\nimport torch.nn.functional as F\nfrom torchvision.transforms import Resize\nfrom sklearn.model_selection import StratifiedKFold \n\n# Fix random seed\ntorch.manual_seed(42)\nnp.random.seed(42)\n\n# Path to your dataset\nBASE_PATH = '/kaggle/input/rsna-miccai-brain-tumor-radiogenomic-classification/train'\nLABEL_CSV = '/kaggle/input/rsna-miccai-brain-tumor-radiogenomic-classification/train_labels.csv'\n\ntest_path = '/kaggle/input/rsna-miccai-brain-tumor-radiogenomic-classification/test'\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-27T03:23:38.637785Z","iopub.execute_input":"2025-06-27T03:23:38.638081Z","iopub.status.idle":"2025-06-27T03:23:38.648980Z","shell.execute_reply.started":"2025-06-27T03:23:38.638055Z","shell.execute_reply":"2025-06-27T03:23:38.648344Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T16:34:48.665167Z","iopub.execute_input":"2025-06-26T16:34:48.665930Z","iopub.status.idle":"2025-06-26T16:34:48.669506Z","shell.execute_reply.started":"2025-06-26T16:34:48.665904Z","shell.execute_reply":"2025-06-26T16:34:48.668746Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# import torch\n# import torch_xla\n# import torch_xla.core.xla_model as xm\n\n# # Instead of 'cuda' use:\n# device = xm.xla_device()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T16:34:49.811355Z","iopub.execute_input":"2025-06-26T16:34:49.811654Z","iopub.status.idle":"2025-06-26T16:34:49.815211Z","shell.execute_reply.started":"2025-06-26T16:34:49.811635Z","shell.execute_reply":"2025-06-26T16:34:49.814391Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(device)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T16:34:51.366964Z","iopub.execute_input":"2025-06-26T16:34:51.367501Z","iopub.status.idle":"2025-06-26T16:34:51.371669Z","shell.execute_reply.started":"2025-06-26T16:34:51.367472Z","shell.execute_reply":"2025-06-26T16:34:51.370863Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Load CSV with patient IDs and labels\nlabel_df = pd.read_csv(LABEL_CSV)\nprint(label_df.head())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T16:34:52.909719Z","iopub.execute_input":"2025-06-26T16:34:52.910404Z","iopub.status.idle":"2025-06-26T16:34:52.933305Z","shell.execute_reply.started":"2025-06-26T16:34:52.910378Z","shell.execute_reply":"2025-06-26T16:34:52.932502Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"label_dict = {\n    f\"{int(row.BraTS21ID  ):05d}\":int(row.MGMT_value) for _, row in label_df.iterrows()\n    if row.BraTS21ID   not in [109, 123, 709]\n\n}\nlen(label_dict.keys())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T16:34:53.403359Z","iopub.execute_input":"2025-06-26T16:34:53.403634Z","iopub.status.idle":"2025-06-26T16:34:53.435814Z","shell.execute_reply.started":"2025-06-26T16:34:53.403615Z","shell.execute_reply":"2025-06-26T16:34:53.435141Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"1. Sort DICOMs by InstanceNumber\n2. Normalize slices\n3. If number of slices < 18:\n4. Pad zero slices equally before and after\n5. Resize every slice to 128 × 128","metadata":{}},{"cell_type":"code","source":"class BrDataset(Dataset):\n    def __init__(self,base_path,patient_ids,label_dict,resize_size = (128,128)):\n        self.base_path = base_path \n        self.patient_ids = patient_ids \n        self.label_dict = label_dict \n        self.modalities = ['FLAIR','T1w']\n        self.resize = Resize(resize_size)\n    def load_slices(self,case_id,modality):\n        paths = sorted(\n            glob.glob(os.path.join(self.base_path,case_id,modality,\"*.dcm\")),\n            key = lambda x:int(pydicom.dcmread(x).InstanceNumber)\n        )\n\n        slices = []\n        for path in paths:\n            dcm = pydicom.dcmread(path)\n            img = dcm.pixel_array.astype(np.float32)\n\n            # Normalize if not blank \n            if np.max(img)>0:\n                img = (img-np.min(img))/(np.max(img)-np.min(img)+1e5)\n                slices.append(img)\n        if len(slices)==0:\n            return None\n\n        num_slices = len(slices)\n        if num_slices<18:\n            pad_needed = 18 - num_slices\n            pad_start = pad_needed//2 \n            pad_end = num_slices - pad_start \n            h,w = slices[0].shape \n            zero_slice = np.zeros((h,w),dtype = np.float32)\n            slices = [zero_slice] * pad_start + slices + [zero_slice] * (18 - len(slices) - pad_start)\n\n        else:\n            mid = num_slices//2 \n            start = max(0, mid - 9)\n            slices = slices[start:start+18]\n        resized = [self.resize(torch.tensor(s)[None,...]) for s in slices]\n        volume = torch.stack(resized).squeeze(1)\n        return volume \n    def __len__(self):\n        return len(self.patient_ids)\n\n    def __getitem__(self,idx):\n        case_id = str(self.patient_ids[idx]).zfill(5)\n        vols = []\n        for modality in self.modalities:\n            v = self.load_slices(case_id, modality)\n            if v is None:\n                # Try next sample if something failed\n                return self.__getitem__((idx + 1) % len(self))\n            vols.append(v)\n        x = torch.stack(vols)\n        y = torch.tensor(self.label_dict[case_id], dtype=torch.long)\n        return x,y\n            ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T16:34:54.165509Z","iopub.execute_input":"2025-06-26T16:34:54.165998Z","iopub.status.idle":"2025-06-26T16:34:54.175359Z","shell.execute_reply.started":"2025-06-26T16:34:54.165957Z","shell.execute_reply":"2025-06-26T16:34:54.174522Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# dataset = BrDataset(BASE_PATH, patient_ids, label_dict)\n# loader = DataLoader(dataset, batch_size=4, shuffle=True)\n\n# tens,y = next(iter(loader))\n# tens[0].shape\n# y","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T16:34:54.447019Z","iopub.execute_input":"2025-06-26T16:34:54.447320Z","iopub.status.idle":"2025-06-26T16:34:54.450732Z","shell.execute_reply.started":"2025-06-26T16:34:54.447299Z","shell.execute_reply":"2025-06-26T16:34:54.449945Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class BrTestDataset(Dataset):\n    def __init__(self, base_path, patient_ids, resize_size=(128, 128)):\n        self.base_path = base_path\n        self.patient_ids = patient_ids\n        self.modalities = [\"FLAIR\", \"T1w\"]\n        self.resize = Resize(resize_size)\n\n    def load_slices(self, case_id, modality):\n        paths = sorted(\n            glob.glob(os.path.join(self.base_path, case_id, modality, \"*.dcm\")),\n            key=lambda x: int(pydicom.dcmread(x).InstanceNumber)\n        )\n\n        slices = []\n        for path in paths:\n            dcm = pydicom.dcmread(path)\n            img = dcm.pixel_array.astype(np.float32)\n            if np.max(img) > 0:\n                img = (img - np.min(img)) / (np.max(img) - np.min(img) + 1e-5)\n                slices.append(img)\n\n        if len(slices) == 0:\n            return None\n        num_slices = len(slices)\n        # Pad or take center 18\n        if len(slices) < 18:\n            pad_needed = 18 - len(slices)\n            pad_start = pad_needed // 2\n            pad_end = pad_needed - pad_start\n            h, w = slices[0].shape\n            zero_slice = np.zeros((h, w), dtype=np.float32)\n            slices = [zero_slice] * pad_start + slices + [zero_slice] * pad_end\n        else:\n            mid = num_slices // 2\n            start = mid - 9\n            slices = slices[start:start + 18]\n\n        resized = [self.resize(torch.tensor(s)[None, ...]) for s in slices]\n        volume = torch.stack(resized).squeeze(1)  # [18, H, W]\n        return volume\n\n    def __len__(self):\n        return len(self.patient_ids)\n\n    def __getitem__(self, idx):\n        case_id = str(self.patient_ids[idx]).zfill(5)\n        vols = []\n\n        for modality in self.modalities:\n            v = self.load_slices(case_id, modality)\n            if v is None:\n                return self.__getitem__((idx + 1) % len(self))\n            vols.append(v)\n\n        x = torch.stack(vols)  # [2, 18, 128, 128]\n        return x, case_id\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T17:40:05.016335Z","iopub.execute_input":"2025-06-26T17:40:05.016639Z","iopub.status.idle":"2025-06-26T17:40:05.026161Z","shell.execute_reply.started":"2025-06-26T17:40:05.016618Z","shell.execute_reply":"2025-06-26T17:40:05.025535Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class TumorClassifier3D(nn.Module):\n    def __init__(self):\n        super().__init__()\n        self.conv1 = nn.Conv3d(in_channels = 2,out_channels = 16,kernel_size=3,padding=1)\n        self.pool1 = nn.MaxPool3d(kernel_size = 2)\n\n        self.conv2 = nn.Conv3d(16, 32, kernel_size=3, padding=1)                            # → [B, 32, 9, 64, 64]\n        self.pool2 = nn.MaxPool3d(2)                                                        # → [B, 32, 4, 32, 32]\n\n        self.conv3 = nn.Conv3d(32, 64, kernel_size=3, padding=1)                            # → [B, 64, 4, 32, 32]\n        self.pool3 = nn.AdaptiveAvgPool3d(1)                                                # → [B, 64, 1, 1, 1]\n\n        self.fc = nn.Linear(64, 2)  # 2 output classes: 0 or 1\n\n    def forward(self,x):\n        x = F.relu(self.conv1(x))\n        x = self.pool1(x)\n        x = F.relu(self.conv2(x))  # → [B, 32, 9, 64, 64]\n        x = self.pool2(x)          # → [B, 32, 4, 32, 32]\n\n        x = F.relu(self.conv3(x))  # → [B, 64, 4, 32, 32]\n        x = self.pool3(x)   \n        x = x.view(x.size(0), -1)  # → [B, 64]\n        x = self.fc(x)  \n        return x \n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T17:40:10.065673Z","iopub.execute_input":"2025-06-26T17:40:10.065940Z","iopub.status.idle":"2025-06-26T17:40:10.072326Z","shell.execute_reply.started":"2025-06-26T17:40:10.065920Z","shell.execute_reply":"2025-06-26T17:40:10.071465Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sklearn.metrics import accuracy_score, roc_auc_score\nfrom tqdm import tqdm\nimport torch.nn.functional as F\n\ndef train_one_fold(model, train_loader, val_loader, optimizer, criterion, device, num_epochs=2):\n    best_auc = 0\n\n    for epoch in range(num_epochs):\n        print(f\"\\nEpoch {epoch + 1}/{num_epochs}\")\n\n        # ---------------- Train ----------------\n        model.train()\n        train_loss = 0\n\n        for inputs, labels in tqdm(train_loader):\n            inputs = inputs.to(device)         # [B, 2, 18, 128, 128]\n            labels = labels.to(device).long()  # [B]\n\n            optimizer.zero_grad()\n            outputs = model(inputs)            # [B, 2]\n            loss = criterion(outputs, labels)\n\n            loss.backward()\n            optimizer.step()\n            train_loss += loss.item()\n\n        print(f\"Train Loss: {train_loss / len(train_loader):.4f}\")\n\n        # ---------------- Validation ----------------\n        model.eval()\n        val_preds = []\n        val_labels = []\n\n        with torch.no_grad():\n            for inputs, labels in val_loader:\n                inputs = inputs.to(device)\n                labels = labels.to(device).long()\n\n                outputs = model(inputs)\n                probs = F.softmax(outputs, dim=1)[:, 1]  # Prob of class 1 (MGMT=1)\n\n                val_preds.extend(probs.cpu().numpy())\n                val_labels.extend(labels.cpu().numpy())\n\n        val_auc = roc_auc_score(val_labels, val_preds)\n        val_preds_cls = (np.array(val_preds) > 0.5).astype(int)\n        val_acc = accuracy_score(val_labels, val_preds_cls)\n\n        print(f\"Val Accuracy: {val_acc:.4f}  |  Val AUC: {val_auc:.4f}\")\n\n        if val_auc > best_auc:\n            best_auc = val_auc\n            # Optional: save best model\n            # torch.save(model.state_dict(), f\"best_model_fold{fold}.pt\")\n\n    return best_auc\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T17:42:43.125840Z","iopub.execute_input":"2025-06-26T17:42:43.126133Z","iopub.status.idle":"2025-06-26T17:42:43.133585Z","shell.execute_reply.started":"2025-06-26T17:42:43.126114Z","shell.execute_reply":"2025-06-26T17:42:43.132999Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"skf = StratifiedKFold(n_splits=2, shuffle=True, random_state=42)\ntest_ids = sorted(os.listdir(test_path))\ntest_dataset = BrTestDataset(base_path=test_path, patient_ids=test_ids)\ntest_loader = DataLoader(test_dataset, batch_size=1, shuffle=False, num_workers=2)\n\nall_fold_preds = []\n\nfor fold, (train_idx, val_idx) in enumerate(skf.split(label_df['BraTS21ID'], label_df['MGMT_value'])):\n    print(f\"\\n========== Fold {fold + 1} ==========\")\n\n    train_ids = label_df.iloc[train_idx]['BraTS21ID'].values\n    val_ids = label_df.iloc[val_idx]['BraTS21ID'].values\n\n    train_dataset = BrDataset(BASE_PATH, train_ids, label_dict)\n    val_dataset = BrDataset(BASE_PATH, val_ids, label_dict)\n\n    train_loader = DataLoader(train_dataset, batch_size=6, shuffle=True, num_workers=2)\n    val_loader = DataLoader(val_dataset, batch_size=6, shuffle=False, num_workers=2)\n\n    model = TumorClassifier3D().to(device)\n    optimizer = torch.optim.Adam(model.parameters(), lr=1e-4)\n    criterion = nn.CrossEntropyLoss()\n\n    _ = train_one_fold(model, train_loader, val_loader, optimizer, criterion, device)\n\n    # Inference on test set for this fold\n    model.eval()\n    fold_preds = []\n\n    with torch.no_grad():\n        for inputs, case_id in tqdm(test_loader):\n            inputs = inputs.to(device)\n            outputs = model(inputs)\n            probs = F.softmax(outputs, dim=1)[:, 1]  # class 1\n            fold_preds.extend(probs.cpu().numpy())\n\n    all_fold_preds.append(fold_preds)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T17:42:47.305640Z","iopub.execute_input":"2025-06-26T17:42:47.306204Z","execution_failed":"2025-06-26T19:14:49.804Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Average predictions from 5 folds\navg_preds = np.mean(all_fold_preds, axis=0)\n\n# Save submission\nsubmission = pd.DataFrame({\n    \"BraTS21ID\": [int(pid) for pid in test_ids],\n    \"MGMT_value\": avg_preds\n})\n\nsubmission.to_csv(\"submission.csv\", index=False)\nprint(\"Saved final submission.\")","metadata":{"trusted":true,"execution":{"execution_failed":"2025-06-26T19:14:49.805Z"}},"outputs":[],"execution_count":null}]}