{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceId":29653,"databundleVersionId":2420395,"sourceType":"competition"}],"dockerImageVersionId":31154,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import os\nimport random\nfrom glob import glob\nfrom tqdm import tqdm\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\n\n\nimport pydicom\nfrom skimage.transform import resize\n\n\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom torch.utils.data import Dataset, DataLoader\nfrom sklearn.metrics import roc_auc_score","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-15T19:20:37.719807Z","iopub.execute_input":"2025-10-15T19:20:37.7204Z","iopub.status.idle":"2025-10-15T19:20:37.725436Z","shell.execute_reply.started":"2025-10-15T19:20:37.720375Z","shell.execute_reply":"2025-10-15T19:20:37.724838Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"DATA_ROOT = \"/kaggle/input/rsna-miccai-brain-tumor-radiogenomic-classification\"","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-15T19:20:39.451674Z","iopub.execute_input":"2025-10-15T19:20:39.451975Z","iopub.status.idle":"2025-10-15T19:20:39.456292Z","shell.execute_reply.started":"2025-10-15T19:20:39.451953Z","shell.execute_reply":"2025-10-15T19:20:39.455363Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"TRAIN_DIR = os.path.join(DATA_ROOT, 'train')\nTRAIN_CSV = os.path.join(DATA_ROOT, 'train_labels.csv')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-15T19:20:40.756436Z","iopub.execute_input":"2025-10-15T19:20:40.75714Z","iopub.status.idle":"2025-10-15T19:20:40.76079Z","shell.execute_reply.started":"2025-10-15T19:20:40.757115Z","shell.execute_reply":"2025-10-15T19:20:40.75999Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"labels_df = pd.read_csv(TRAIN_CSV)\nlabels_df['BraTS21ID_str'] = labels_df['BraTS21ID'].apply(lambda x: str(x).zfill(5))\nprint(labels_df.head())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-15T19:20:42.591369Z","iopub.execute_input":"2025-10-15T19:20:42.592112Z","iopub.status.idle":"2025-10-15T19:20:42.628699Z","shell.execute_reply.started":"2025-10-15T19:20:42.592086Z","shell.execute_reply":"2025-10-15T19:20:42.628078Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"sample_patient = labels_df['BraTS21ID_str'].iloc[0]\nprint('sample patient id', sample_patient)\nprint('sequences:', os.listdir(os.path.join(TRAIN_DIR, sample_patient)))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-15T19:20:44.684234Z","iopub.execute_input":"2025-10-15T19:20:44.684909Z","iopub.status.idle":"2025-10-15T19:20:44.695755Z","shell.execute_reply.started":"2025-10-15T19:20:44.684883Z","shell.execute_reply":"2025-10-15T19:20:44.695069Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def load_dicom_volume(seq_folder, sort_by='InstanceNumber'):\n\n    files = [os.path.join(seq_folder, f) for f in os.listdir(seq_folder) if f.lower().endswith('.dcm')]\n    if len(files) == 0:\n        raise FileNotFoundError(f'No DICOM files in {seq_folder}')\n\n\n# read headers\n    slices = []\n    for f in files:\n        try:\n            d = pydicom.dcmread(f, force=True)\n            slices.append((f, d))\n        except Exception as e:\n# skip unreadable\n            continue\n\n\n# try to sort by InstanceNumber or ImagePositionPatient z coord\n    def sort_key(x):\n        _, d = x\n        if hasattr(d, 'InstanceNumber'):\n            return int(d.InstanceNumber)\n        if hasattr(d, 'ImagePositionPatient'):\n            return float(d.ImagePositionPatient[2])\n        return x[0]\n\n\n    slices = sorted(slices, key=sort_key)\n    arrays = [s[1].pixel_array for s in slices]\n    volume = np.stack(arrays, axis=-1) # H,W,D\n    return volume","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-15T19:20:46.519587Z","iopub.execute_input":"2025-10-15T19:20:46.51989Z","iopub.status.idle":"2025-10-15T19:20:46.526644Z","shell.execute_reply.started":"2025-10-15T19:20:46.519869Z","shell.execute_reply":"2025-10-15T19:20:46.525994Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"seq = os.listdir(os.path.join(TRAIN_DIR, sample_patient))[0]\nprint('example sequence', seq)\nvol = load_dicom_volume(os.path.join(TRAIN_DIR, sample_patient, seq))\nprint('volume shape', vol.shape)\nplt.imshow(vol[:, :, vol.shape[2]//2], cmap='gray')\nplt.title('Middle slice')\nplt.axis('off')\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-15T19:21:32.940714Z","iopub.execute_input":"2025-10-15T19:21:32.941368Z","iopub.status.idle":"2025-10-15T19:21:38.917379Z","shell.execute_reply.started":"2025-10-15T19:21:32.941343Z","shell.execute_reply":"2025-10-15T19:21:38.916567Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Cell 3: Preprocessing: make 4-channel 3D volume\n","metadata":{}},{"cell_type":"code","source":"TARGET_SHAPE = (128, 128, 64) # (H, W, D) — choose according to memory\nSEQUENCES = ['T1w', 'T1wCE', 'T2', 'FLAIR']","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-15T19:21:42.472415Z","iopub.execute_input":"2025-10-15T19:21:42.472919Z","iopub.status.idle":"2025-10-15T19:21:42.476697Z","shell.execute_reply.started":"2025-10-15T19:21:42.472894Z","shell.execute_reply":"2025-10-15T19:21:42.476114Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def preprocess_patient(pid, root=TRAIN_DIR, sequences=SEQUENCES, target_shape=TARGET_SHAPE):\n    \"Return numpy array shape (C, H, W, D) with channels for sequences.\"\n    channels = []\n    patient_dir = os.path.join(root, pid)\n    for seq in sequences:\n        seq_path = os.path.join(patient_dir, seq)\n        if not os.path.exists(seq_path):\n# fallback: channel of zeros\n            vol = np.zeros(target_shape, dtype=np.float32)\n        else:\n            try:\n                vol = load_dicom_volume(seq_path)\n            except Exception as e:\n                vol = np.zeros((target_shape), dtype=np.float32)\n\n\n# normalize intensities (min-max) per volume\n    vol = vol.astype(np.float32)\n    if vol.max() > vol.min():\n        vol = (vol - vol.min()) / (vol.max() - vol.min())\n    else:\n        vol = vol * 0.0\n\n\n# resize to target_shape\n# skimage.transform.resize operates on (H,W,D) volumes\n    vol = resize(vol, target_shape, order=1, preserve_range=True, mode='constant')\n\n\n    channels.append(vol)\n\n\n    arr = np.stack(channels, axis=0) # C, H, W, D\n    return arr","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-15T19:21:46.25426Z","iopub.execute_input":"2025-10-15T19:21:46.254506Z","iopub.status.idle":"2025-10-15T19:21:46.260944Z","shell.execute_reply.started":"2025-10-15T19:21:46.25449Z","shell.execute_reply":"2025-10-15T19:21:46.2601Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"sample_arr = preprocess_patient(sample_patient)\nprint('preprocessed shape:', sample_arr.shape)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-15T19:21:49.003351Z","iopub.execute_input":"2025-10-15T19:21:49.003844Z","iopub.status.idle":"2025-10-15T19:22:01.77581Z","shell.execute_reply.started":"2025-10-15T19:21:49.003823Z","shell.execute_reply":"2025-10-15T19:22:01.775111Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"fig, axes = plt.subplots(1, 4, figsize=(12,3))\nfor i in range(4):\n    ax = axes[i]\n    ax.imshow(sample_arr[i,:,:,sample_arr.shape[3]//2], cmap='gray')\n    ax.set_title(SEQUENCES[i])\n    ax.axis('off')\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-15T19:22:24.582438Z","iopub.execute_input":"2025-10-15T19:22:24.582695Z","iopub.status.idle":"2025-10-15T19:22:25.004841Z","shell.execute_reply.started":"2025-10-15T19:22:24.582679Z","shell.execute_reply":"2025-10-15T19:22:25.003803Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Dataset and DataLoader","metadata":{}},{"cell_type":"code","source":"class BrainMRIDataset(Dataset):\n    def __init__(self, df, root=TRAIN_DIR, sequences=SEQUENCES, target_shape=TARGET_SHAPE, transforms=None):\n        self.df = df.reset_index(drop=True)\n        self.root = root\n        self.sequences = sequences\n        self.target_shape = target_shape\n        self.transforms = transforms\n\n\n    def __len__(self):\n        return len(self.df)\n\n\n    def __getitem__(self, idx):\n        pid = self.df.loc[idx, 'BraTS21ID_str']\n        label = self.df.loc[idx, 'MGMT_value']\n        arr = preprocess_patient(pid, root=self.root, sequences=self.sequences, target_shape=self.target_shape)\n# optionally add transforms (random flip/rotate)\n        if self.transforms:\n            arr = self.transforms(arr)\n\n\n# to tensor\n        x = torch.from_numpy(arr).float()\n        y = torch.tensor(label).long()\n        return x, y","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-15T19:22:27.456668Z","iopub.execute_input":"2025-10-15T19:22:27.45698Z","iopub.status.idle":"2025-10-15T19:22:27.463606Z","shell.execute_reply.started":"2025-10-15T19:22:27.456959Z","shell.execute_reply":"2025-10-15T19:22:27.462902Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"create small train/val split for demo","metadata":{}},{"cell_type":"code","source":"from sklearn.model_selection import train_test_split\ntrain_df, val_df = train_test_split(labels_df, test_size=0.2, random_state=42, stratify=labels_df['MGMT_value'])\n\n\ntrain_ds = BrainMRIDataset(train_df)\nval_ds = BrainMRIDataset(val_df)\n\n\ntrain_loader = DataLoader(train_ds, batch_size=2, shuffle=True, num_workers=2)\nval_loader = DataLoader(val_ds, batch_size=2, shuffle=False, num_workers=2)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-15T19:22:29.900772Z","iopub.execute_input":"2025-10-15T19:22:29.90111Z","iopub.status.idle":"2025-10-15T19:22:29.932995Z","shell.execute_reply.started":"2025-10-15T19:22:29.901086Z","shell.execute_reply":"2025-10-15T19:22:29.932404Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Simple 3D CNN model","metadata":{}},{"cell_type":"code","source":"class Simple3DCNN(nn.Module):\n    def __init__(self, in_channels=4, num_classes=2, base_filters=16):\n        super().__init__()\n        self.conv1 = nn.Conv3d(in_channels=1, out_channels=16, kernel_size=3, padding=1)\n        self.bn1 = nn.BatchNorm3d(base_filters)\n        self.conv2 = nn.Conv3d(base_filters, base_filters*2, kernel_size=3, padding=1)\n        self.bn2 = nn.BatchNorm3d(base_filters*2)\n        self.conv3 = nn.Conv3d(base_filters*2, base_filters*4, kernel_size=3, padding=1)\n        self.bn3 = nn.BatchNorm3d(base_filters*4)\n        self.pool = nn.MaxPool3d(2)\n# compute flatten size for final fc\n# we will use adaptive pooling to a fixed small shape then flatten\n        self.adaptive_pool = nn.AdaptiveAvgPool3d((4,4,4))\n        self.fc1 = nn.Linear(base_filters*4*4*4*4, 128)\n        self.dropout = nn.Dropout(0.4)\n        self.fc2 = nn.Linear(128, num_classes)\n\n\n    def forward(self, x):\n# x: B, C, H, W, D\n        x = F.relu(self.bn1(self.conv1(x)))\n        x = self.pool(x)\n        x = F.relu(self.bn2(self.conv2(x)))\n        x = self.pool(x)\n        x = F.relu(self.bn3(self.conv3(x)))\n        x = self.adaptive_pool(x)\n        x = torch.flatten(x, 1)\n        x = F.relu(self.fc1(x))\n        x = self.dropout(x)\n        x = self.fc2(x)\n        return x","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-15T19:22:32.495647Z","iopub.execute_input":"2025-10-15T19:22:32.496226Z","iopub.status.idle":"2025-10-15T19:22:32.503612Z","shell.execute_reply.started":"2025-10-15T19:22:32.496199Z","shell.execute_reply":"2025-10-15T19:22:32.502697Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Training utilities","metadata":{}},{"cell_type":"code","source":"device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\nmodel = Simple3DCNN().to(device)\noptimizer = torch.optim.Adam(model.parameters(), lr=1e-4)\ncriterion = nn.CrossEntropyLoss()\n\n\nprint(model)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-15T19:22:35.977746Z","iopub.execute_input":"2025-10-15T19:22:35.97823Z","iopub.status.idle":"2025-10-15T19:22:40.076097Z","shell.execute_reply.started":"2025-10-15T19:22:35.978208Z","shell.execute_reply":"2025-10-15T19:22:40.075264Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Training loop (lightweight demo)","metadata":{}},{"cell_type":"code","source":"def train_one_epoch(model, loader, optimizer, criterion, device):\n    model.train()\n    losses = []\n    for x, y in tqdm(loader):\n        x = x.to(device)\n        y = y.to(device)\n        optimizer.zero_grad()\n        logits = model(x)\n        loss = criterion(logits, y)\n        loss.backward()\n        optimizer.step()\n        losses.append(loss.item())\n    return np.mean(losses)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-15T19:22:42.441083Z","iopub.execute_input":"2025-10-15T19:22:42.441543Z","iopub.status.idle":"2025-10-15T19:22:42.446338Z","shell.execute_reply.started":"2025-10-15T19:22:42.441521Z","shell.execute_reply":"2025-10-15T19:22:42.445629Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def evaluate(model, loader, device):\n    model.eval()\n    preds = []\n    targets = []\n    losses = []\n    with torch.no_grad():\n        for x, y in loader:\n            x = x.to(device)\n            y = y.to(device)\n            logits = model(x)\n            probs = F.softmax(logits, dim=1)[:,1].cpu().numpy()\n            preds.extend(probs.tolist())\n            targets.extend(y.cpu().numpy().tolist())\n    try:\n        auc = roc_auc_score(targets, preds)\n    except Exception:\n        auc = float('nan')\n    return auc","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-15T19:22:44.776881Z","iopub.execute_input":"2025-10-15T19:22:44.777447Z","iopub.status.idle":"2025-10-15T19:22:44.782686Z","shell.execute_reply.started":"2025-10-15T19:22:44.777424Z","shell.execute_reply":"2025-10-15T19:22:44.78207Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"quick train for a few epochs (demo)","metadata":{}},{"cell_type":"code","source":"EPOCHS = 1\nbest_auc = 0.0\nfor epoch in range(EPOCHS):\n    print(f'Epoch {epoch+1}/{EPOCHS}')\n    train_loss = train_one_epoch(model, train_loader, optimizer, criterion, device)\n    val_auc = evaluate(model, val_loader, device)\n    print(f' train_loss: {train_loss:.4f} | val_auc: {val_auc:.4f}')\n# save best\nif val_auc > best_auc:\n    best_auc = val_auc\n    torch.save(model.state_dict(), '/kaggle/working/best_simple3d.pth')\n\n\nprint('Done training. Best AUC:', best_auc)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-15T19:22:54.610898Z","iopub.execute_input":"2025-10-15T19:22:54.611205Z","iopub.status.idle":"2025-10-15T19:53:35.119947Z","shell.execute_reply.started":"2025-10-15T19:22:54.611185Z","shell.execute_reply":"2025-10-15T19:53:35.11789Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"submission_df = pd.DataFrame()\nsubmission_df['BraTS21ID'] = val_df['BraTS21ID']\nmodel.load_state_dict(torch.load('/kaggle/working/best_simple3d.pth'))\nmodel.eval()\nprobs = []\nwith torch.no_grad():\n    for x, y in DataLoader(val_ds, batch_size=2):\n        x = x.to(device)\n        logits = model(x)\n        p = F.softmax(logits, dim=1)[:,1].cpu().numpy()\n        probs.extend(p.tolist())\n\n\nsubmission_df['MGMT_value'] = probs\nsubmission_df.to_csv('/kaggle/working/submission_demo.csv', index=False)\nprint('Saved /kaggle/working/submission_demo.csv')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-15T19:54:06.454417Z","iopub.execute_input":"2025-10-15T19:54:06.455408Z","iopub.status.idle":"2025-10-15T19:54:06.491981Z","shell.execute_reply.started":"2025-10-15T19:54:06.455367Z","shell.execute_reply":"2025-10-15T19:54:06.491Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sklearn.metrics import roc_auc_score, f1_score\n\ndef evaluate_model(model, val_loader, device):\n    model.eval()\n    all_labels = []\n    all_preds = []\n    all_probs = []\n    total_correct = 0\n    total_samples = 0\n\n    with torch.no_grad():\n        for images, labels in val_loader:\n            images, labels = images.to(device), labels.to(device)\n\n            outputs = model(images)\n            probs = torch.sigmoid(outputs).squeeze()\n\n            preds = (probs > 0.5).float()\n            total_correct += (preds == labels).float().sum().item()\n            total_samples += labels.size(0)\n\n            all_labels.extend(labels.cpu().numpy())\n            all_preds.extend(preds.cpu().numpy())\n            all_probs.extend(probs.cpu().numpy())\n\n    accuracy = total_correct / total_samples\n    auc = roc_auc_score(all_labels, all_probs)\n    f1 = f1_score(all_labels, all_preds)\n\n    print(f\"Validation Accuracy: {accuracy:.4f}\")\n    print(f\"Validation AUC: {auc:.4f}\")\n    print(f\"Validation F1-score: {f1:.4f}\")\n\n    return accuracy, auc, f1","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-15T19:54:15.627561Z","iopub.execute_input":"2025-10-15T19:54:15.627852Z","iopub.status.idle":"2025-10-15T19:54:15.634522Z","shell.execute_reply.started":"2025-10-15T19:54:15.627831Z","shell.execute_reply":"2025-10-15T19:54:15.633896Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"val_acc, val_auc, val_f1 = evaluate_model(model, val_loader, device)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-15T19:54:18.419357Z","iopub.execute_input":"2025-10-15T19:54:18.41964Z","iopub.status.idle":"2025-10-15T19:54:30.575476Z","shell.execute_reply.started":"2025-10-15T19:54:18.41962Z","shell.execute_reply":"2025-10-15T19:54:30.574034Z"}},"outputs":[],"execution_count":null}]}