{"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":99552,"databundleVersionId":13851420,"sourceType":"competition"},{"sourceId":13866621,"sourceType":"datasetVersion","datasetId":8834739},{"sourceId":13913205,"sourceType":"datasetVersion","datasetId":8834618},{"sourceId":13927856,"sourceType":"datasetVersion","datasetId":8858929},{"sourceId":13994688,"sourceType":"datasetVersion","datasetId":8918748},{"sourceId":672835,"sourceType":"modelInstanceVersion","modelInstanceId":509823,"modelId":524485}],"dockerImageVersionId":31193,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"#Libraries from Demo\nimport os\nimport shutil\nfrom collections import defaultdict\n\nimport pandas as pd\nimport polars as pl\nimport pydicom as dicom\n\n\n#Libraries from attempt\nimport numpy as np\nimport matplotlib.pyplot as plt\nimport scipy.ndimage as ndi\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nimport torch.optim as optim\n\nfrom collections import Counter\nfrom scipy import ndimage\n\nfrom scipy.ndimage import zoom as ndi_zoom\nfrom sklearn.model_selection import train_test_split, StratifiedShuffleSplit\nfrom sklearn.metrics import roc_auc_score\nfrom torch.utils.data import Dataset, DataLoader, Subset\nfrom typing import Tuple, List\n\n\nfrom sklearn.preprocessing import StandardScaler","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-12-05T01:33:37.492736Z","iopub.execute_input":"2025-12-05T01:33:37.493466Z","iopub.status.idle":"2025-12-05T01:33:43.89867Z","shell.execute_reply.started":"2025-12-05T01:33:37.493438Z","shell.execute_reply":"2025-12-05T01:33:43.897986Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"ID_COL = 'SeriesInstanceUID'","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-05T01:33:43.899834Z","iopub.execute_input":"2025-12-05T01:33:43.900254Z","iopub.status.idle":"2025-12-05T01:33:43.90379Z","shell.execute_reply.started":"2025-12-05T01:33:43.900234Z","shell.execute_reply":"2025-12-05T01:33:43.902989Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"DEVICE = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\nTRAIN_CSV = \"/kaggle/input/rsna-intracranial-aneurysm-detection/train.csv\"\ntest_frac = 0.2\nval_frac = 0.1\nval_frac_within_trainval = val_frac / (1 - test_frac)\nseed = 42  ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-05T01:33:43.904536Z","iopub.execute_input":"2025-12-05T01:33:43.904776Z","iopub.status.idle":"2025-12-05T01:33:44.003553Z","shell.execute_reply.started":"2025-12-05T01:33:43.90475Z","shell.execute_reply":"2025-12-05T01:33:44.002656Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"LABEL_COLS = [\n    'Left Infraclinoid Internal Carotid Artery',\n    'Right Infraclinoid Internal Carotid Artery',\n    'Left Supraclinoid Internal Carotid Artery',\n    'Right Supraclinoid Internal Carotid Artery',\n    'Left Middle Cerebral Artery',\n    'Right Middle Cerebral Artery',\n    'Anterior Communicating Artery',\n    'Left Anterior Cerebral Artery',\n    'Right Anterior Cerebral Artery',\n    'Left Posterior Communicating Artery',\n    'Right Posterior Communicating Artery',\n    'Basilar Tip',\n    'Other Posterior Circulation',\n    'Aneurysm Present',\n]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-05T01:33:44.004458Z","iopub.execute_input":"2025-12-05T01:33:44.004733Z","iopub.status.idle":"2025-12-05T01:33:44.02066Z","shell.execute_reply.started":"2025-12-05T01:33:44.004705Z","shell.execute_reply":"2025-12-05T01:33:44.019744Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train = pd.read_csv(TRAIN_CSV)\nprint(f\"On the original Dataset, the percentage of aneurysms is: {100 * sum(train['Aneurysm Present'])/len(train)}%\")\nprint(f\"The original dataset has {len(train)} samples.\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-05T01:33:44.023039Z","iopub.execute_input":"2025-12-05T01:33:44.023375Z","iopub.status.idle":"2025-12-05T01:33:44.078506Z","shell.execute_reply.started":"2025-12-05T01:33:44.023356Z","shell.execute_reply":"2025-12-05T01:33:44.077864Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"excluded = np.load('/kaggle/input/succesful/usefull.npz')[\"lst\"]\nprint(len(excluded))\ntrain = train[train['SeriesInstanceUID'].isin(excluded)]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-05T01:33:44.079277Z","iopub.execute_input":"2025-12-05T01:33:44.079668Z","iopub.status.idle":"2025-12-05T01:33:44.113007Z","shell.execute_reply.started":"2025-12-05T01:33:44.079641Z","shell.execute_reply":"2025-12-05T01:33:44.112461Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"y = train[\"Aneurysm Present\"].astype(int).values\nsss = StratifiedShuffleSplit(n_splits=1, test_size=test_frac, random_state=seed)\n(trainval_idx, test_idx), = sss.split(np.zeros(len(y)), y)\ny_trainval = y[trainval_idx]\n\nsss_2 = StratifiedShuffleSplit(n_splits=1, test_size=val_frac_within_trainval, random_state=seed)\n(train_rel_idx, val_rel_idx), = sss_2.split(np.zeros(len(y_trainval)), y_trainval)\ntrain_idx = trainval_idx[train_rel_idx]\nval_idx   = trainval_idx[val_rel_idx]\n\ntrain_ds = Subset(train, train_idx.tolist())\nval_ds   = Subset(train, val_idx.tolist())\ntest_ds  = Subset(train, test_idx.tolist())\n\nprint(f\"Train/Val/Test sizes: {len(train_ds)} / {len(val_ds)} / {len(test_ds)}\")\nprint(len(train.iloc[train_idx]), len(train.iloc[val_idx]), len(train.iloc[test_idx]))\nprint(\"Train positive rate:\", train.iloc[train_idx][\"Aneurysm Present\"].mean())\nprint(\"Val   positive rate:\", train.iloc[val_idx][\"Aneurysm Present\"].mean())\nprint(\"Test   positive rate:\", train.iloc[test_idx][\"Aneurysm Present\"].mean())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-05T01:33:44.113673Z","iopub.execute_input":"2025-12-05T01:33:44.113885Z","iopub.status.idle":"2025-12-05T01:33:44.130005Z","shell.execute_reply.started":"2025-12-05T01:33:44.113868Z","shell.execute_reply":"2025-12-05T01:33:44.129247Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Helper Function","metadata":{}},{"cell_type":"code","source":"def build_roi_from_pred_mask(vol_tensor, mask_logits, padding=5, size=(64,64,64)):\n    probs = torch.sigmoid(mask_logits)\n    mask  = (probs > 0.5).float()  # [1,1,D,H,W]\n\n    # Fallback: center crop if empty mask\n    if mask.sum() == 0:\n        _,_,D,H,W = vol_tensor.shape\n        Dz,Dy,Dx = size\n        cz,cy,cx = D//2, H//2, W//2\n        z_min = max(0, cz - Dz//2); z_max = min(D, z_min + Dz)\n        y_min = max(0, cy - Dy//2); y_max = min(H, y_min + Dy)\n        x_min = max(0, cx - Dx//2); x_max = min(W, x_min + Dx)\n        crop = vol_tensor[:, :, z_min:z_max, y_min:y_max, x_min:x_max]\n        return F.interpolate(crop, size=size, mode='trilinear', align_corners=False)\n\n    idx = mask.nonzero(as_tuple=False)  # [N,5] (B,C,D,H,W)\n    d_min = max(0, idx[:,2].min().item() - padding)\n    d_max = idx[:,2].max().item() + padding + 1\n    h_min = max(0, idx[:,3].min().item() - padding)\n    h_max = idx[:,3].max().item() + padding + 1\n    w_min = max(0, idx[:,4].min().item() - padding)\n    w_max = idx[:,4].max().item() + padding + 1\n\n    _,_,D,H,W = vol_tensor.shape\n    d_max = min(D, d_max)\n    h_max = min(H, h_max)\n    w_max = min(W, w_max)\n\n    crop = vol_tensor[:, :, d_min:d_max, h_min:h_max, w_min:w_max]\n    roi  = F.interpolate(crop, size=size, mode='trilinear', align_corners=False)\n    return roi","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-05T01:33:44.130721Z","iopub.execute_input":"2025-12-05T01:33:44.130948Z","iopub.status.idle":"2025-12-05T01:33:44.139407Z","shell.execute_reply.started":"2025-12-05T01:33:44.130931Z","shell.execute_reply":"2025-12-05T01:33:44.138762Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Datasets","metadata":{}},{"cell_type":"code","source":"from torch.utils.data import Dataset\nfrom pathlib import Path\nimport os, numpy as np, torch\n\nfrom torch.utils.data import Dataset\nimport os, numpy as np, torch\n\nclass CachedROIDataset(Dataset):\n    def __init__(self, df, id_col, label_cols, transform=None):\n        # Get the classification label from the dataframe\n        self.base_dataset = df\n        self.label_cols = label_cols \n        self.df = df.reset_index(drop=True).copy()\n        self.id_col = id_col\n        self.transform = transform\n\n    def __len__(self):\n        return len(self.df)\n\n    def __getitem__(self, idx):\n        # GGet sid to get the roi\n        sid = str(self.df[self.id_col].iloc[idx])\n\n        # Get the classification label from the dataframe\n        labels = self.df[self.label_cols].iloc[idx].values\n        labels = labels.astype(np.float32)\n\n        rois_path = '/kaggle/input/u-net-generated-rois/generated_rois/'\n        roi_path = rois_path + sid + '.npz'\n\n        # Load files\n        roi  = np.load(roi_path)[\"vol\"].astype(np.float32)      # [1, 1, D, H, W]\n\n\n        # Convert to PyTorch tensors with channel dimension\n        roi = torch.from_numpy(roi)    # [1, 1, D, H, W]\n        return roi[0], torch.from_numpy(labels)\n\n    def verify(self):\n        missing = []\n        for sid in self.df[self.id_col].astype(str):\n            if self._find_files(sid) is None:\n                missing.append(sid)\n        return missing\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-05T01:33:44.140048Z","iopub.execute_input":"2025-12-05T01:33:44.140367Z","iopub.status.idle":"2025-12-05T01:33:44.154416Z","shell.execute_reply.started":"2025-12-05T01:33:44.140343Z","shell.execute_reply":"2025-12-05T01:33:44.15377Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_ds = CachedROIDataset(\n    df=train.iloc[train_idx],\n    id_col=ID_COL,\n    label_cols=LABEL_COLS,\n)\n\nvalid_ds = CachedROIDataset(\n    df=train.iloc[val_idx],\n    id_col=ID_COL,\n    label_cols=LABEL_COLS,\n)\n\ntest_ds = CachedROIDataset(\n    df=train.iloc[test_idx],\n    id_col=ID_COL,\n    label_cols=LABEL_COLS,\n)\nprint(len(train_ds), len(valid_ds), len(test_ds))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-05T01:33:44.155115Z","iopub.execute_input":"2025-12-05T01:33:44.155412Z","iopub.status.idle":"2025-12-05T01:33:44.174649Z","shell.execute_reply.started":"2025-12-05T01:33:44.155389Z","shell.execute_reply":"2025-12-05T01:33:44.173764Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Reviewing X=volume, Y=mask","metadata":{}},{"cell_type":"code","source":"x, y = train_ds[2]\nprint(\"Volume:\", x.shape)\n\nplt.figure(figsize=(10,4))\nplt.imshow(x[0, 32].numpy())\nplt.title(\"Volume slice\")\n\nprint(y)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-05T01:33:44.175617Z","iopub.execute_input":"2025-12-05T01:33:44.175996Z","iopub.status.idle":"2025-12-05T01:33:44.538981Z","shell.execute_reply.started":"2025-12-05T01:33:44.175973Z","shell.execute_reply":"2025-12-05T01:33:44.538229Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Model","metadata":{}},{"cell_type":"code","source":"import torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom torch.autograd import Variable\nimport math\nfrom functools import partial\n\nclass AneurysmClassifier3D(nn.Module):\n    def __init__(self, in_channels=1, num_classes=14):\n        super().__init__()\n        \n        # A simple stack of Conv3D -> BN -> ReLU -> MaxPool\n        self.features = nn.Sequential(\n            self._conv_block(in_channels, 16),\n            nn.MaxPool3d(2), # 64 -> 32\n            \n            self._conv_block(16, 32),\n            nn.MaxPool3d(2), # 32 -> 16\n            \n            self._conv_block(32, 64),\n            nn.MaxPool3d(2), # 16 -> 8\n            \n            self._conv_block(64, 128),\n            nn.MaxPool3d(2), # 8 -> 4\n        )\n        \n        # Classification Head\n        self.classifier = nn.Sequential(\n            nn.AdaptiveAvgPool3d(1), # Flattens [128, 4, 4, 4] -> [128, 1, 1, 1]\n            nn.Flatten(),\n            nn.Linear(128, 64),\n            nn.ReLU(),\n            nn.Dropout(0.5),\n            nn.Linear(64, num_classes)\n        )\n\n    def _conv_block(self, in_c, out_c):\n        return nn.Sequential(\n            nn.Conv3d(in_c, out_c, kernel_size=3, padding=1),\n            nn.BatchNorm3d(out_c),\n            nn.ReLU(inplace=True)\n        )\n\n    def forward(self, x):\n        x = self.features(x)\n        x = self.classifier(x)\n        return x","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-05T01:33:44.539772Z","iopub.execute_input":"2025-12-05T01:33:44.540032Z","iopub.status.idle":"2025-12-05T01:33:44.546876Z","shell.execute_reply.started":"2025-12-05T01:33:44.539993Z","shell.execute_reply":"2025-12-05T01:33:44.546229Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Training","metadata":{}},{"cell_type":"markdown","source":"## AUC","metadata":{}},{"cell_type":"code","source":"def auc_metrics(model, loader, device):\n    model.eval()\n    all_probs = []\n    all_labels = []\n\n    with torch.no_grad():\n        for img, labels in loader:\n            img = img.to(device)\n            probs = torch.sigmoid(model(img))\n            all_probs.append(probs.cpu())\n            all_labels.append(labels)\n\n    all_probs = torch.cat(all_probs).numpy()\n    all_labels = torch.cat(all_labels).numpy()\n    auc = roc_auc_score(all_labels[:, -1], all_probs[:, -1])\n    print(f\"Aneurysm Present AUC: {auc:.4f}\")\n    return auc","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Training Loop","metadata":{}},{"cell_type":"code","source":"def train_roi_classifier(\n    num_epochs=10,\n    batch_size=8, # We can use larger batches now because crops are smaller (64^3)\n    lr=1e-3,\n    wd=1e-3,\n    save_path='aneurysm_roi_classifier_3DCNN.pth',\n    device=DEVICE):\n    \n    train_ds = CachedROIDataset(df=train.iloc[train_idx],\n                                id_col=ID_COL,\n                                label_cols=LABEL_COLS,)\n\n    valid_ds = CachedROIDataset(df=train.iloc[val_idx],\n                                id_col=ID_COL,\n                                label_cols=LABEL_COLS,)\n\n    test_ds = CachedROIDataset(df=train.iloc[test_idx],\n                                id_col=ID_COL,\n                                label_cols=LABEL_COLS,)\n\n    train_loader = DataLoader(train_ds, batch_size=batch_size, shuffle=True, num_workers=2)\n    val_loader = DataLoader(valid_ds, batch_size=batch_size, shuffle=False, num_workers=2)\n\n    model = AneurysmClassifier3D(num_classes=len(LABEL_COLS)).to(device)\n\n    train_labels_df = train.iloc[train_idx][LABEL_COLS]\n    label_means = train_labels_df.mean().values \n\n    pos_weight_np = (1.0 - label_means) / (label_means)\n    max_weight = 10.0\n    pos_weight_np = np.clip(pos_weight_np, 1.0, max_weight)\n    pos_weight = torch.tensor(pos_weight_np, dtype=torch.float32).to(device)\n    criterion = nn.BCEWithLogitsLoss(pos_weight=pos_weight)\n    optimizer = optim.Adam(model.parameters(), lr=lr, weight_decay=wd)\n    \n    best_auc = 0.0\n    print(\"Starting ROI Classification Training...\")\n    \n    for epoch in range(num_epochs):\n        model.train()\n        train_loss = 0\n        correct = 0\n        total = 0\n        \n        for imgs, labels in train_loader:\n            imgs, labels = imgs.to(device), labels.to(device)\n            \n            optimizer.zero_grad()\n            outputs = model(imgs)\n            loss = criterion(outputs, labels)\n            loss.backward()\n            optimizer.step()\n            \n            train_loss += loss.item() * imgs.size(0)\n            \n            # Accuracy calc\n            preds = (torch.sigmoid(outputs) > 0.5).float()\n            correct += (preds == labels).sum().item()\n            total += labels.numel() \n\n        epoch_loss = train_loss / len(train_ds)\n\n        # --- Validation ---\n        model.eval()\n        val_loss = 0\n        val_correct = 0\n        val_total = 0\n        \n        with torch.no_grad():\n            for imgs, labels in val_loader:\n                imgs, labels = imgs.to(device), labels.to(device)\n                outputs = model(imgs)\n                loss = criterion(outputs, labels)\n                val_loss += loss.item() * imgs.size(0)\n                \n                preds = (torch.sigmoid(outputs) > 0.5).float()\n                val_correct += (preds == labels).sum().item()\n                val_total += labels.numel()\n        \n        val_epoch_loss = val_loss / len(valid_ds)\n        val_auc = auc_metrics(model, val_loader, device)\n        \n        print(f\"Epoch {epoch+1}/{num_epochs} | \"\n              f\"Train Loss: {epoch_loss:.4f}| \"\n              f\"Val Loss: {val_epoch_loss:.4f} AUC: {val_auc:.4f}\")\n        \n        if val_auc > best_auc:\n            best_auc = val_auc\n            torch.save(model.state_dict(), save_path)\n            print('Best AUC so far saved to: ', save_path)\n\n    return model","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-05T01:33:44.54761Z","iopub.execute_input":"2025-12-05T01:33:44.547857Z","iopub.status.idle":"2025-12-05T01:33:44.567357Z","shell.execute_reply.started":"2025-12-05T01:33:44.547833Z","shell.execute_reply":"2025-12-05T01:33:44.566535Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"classifier_model = train_roi_classifier(\n    num_epochs=30,\n    batch_size=8,\n    lr=1e-4,\n    save_path='aneurysm_roi_classifier_3DCNN.pth',\n    device=DEVICE)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-05T01:33:44.569445Z","iopub.execute_input":"2025-12-05T01:33:44.569737Z","iopub.status.idle":"2025-12-05T01:37:58.960992Z","shell.execute_reply.started":"2025-12-05T01:33:44.569719Z","shell.execute_reply":"2025-12-05T01:37:58.960166Z"}},"outputs":[],"execution_count":null}]}