{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.12.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceId":52254,"databundleVersionId":9674523,"sourceType":"competition"}],"dockerImageVersionId":31260,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import os\n\nfor root, dirs, files in os.walk('/kaggle/input/rsna-2023-abdominal-trauma-detection'):\n    print(root)\n    break\n","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-02-16T10:38:52.179894Z","iopub.execute_input":"2026-02-16T10:38:52.180115Z","iopub.status.idle":"2026-02-16T10:38:52.186907Z","shell.execute_reply.started":"2026-02-16T10:38:52.180093Z","shell.execute_reply":"2026-02-16T10:38:52.186221Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\n\nprint(os.listdir(\"/kaggle/input/competitions\"))\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-16T10:43:27.706344Z","iopub.execute_input":"2026-02-16T10:43:27.706641Z","iopub.status.idle":"2026-02-16T10:43:27.711259Z","shell.execute_reply.started":"2026-02-16T10:43:27.706616Z","shell.execute_reply":"2026-02-16T10:43:27.710371Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(os.listdir(\"/kaggle/input/competitions/rsna-2023-abdominal-trauma-detection\"))\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-16T10:43:47.471576Z","iopub.execute_input":"2026-02-16T10:43:47.472341Z","iopub.status.idle":"2026-02-16T10:43:47.477494Z","shell.execute_reply.started":"2026-02-16T10:43:47.472302Z","shell.execute_reply":"2026-02-16T10:43:47.476749Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"BASE_PATH = \"/kaggle/input/competitions/rsna-2023-abdominal-trauma-detection\"\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-16T10:45:19.161431Z","iopub.execute_input":"2026-02-16T10:45:19.161926Z","iopub.status.idle":"2026-02-16T10:45:19.165288Z","shell.execute_reply.started":"2026-02-16T10:45:19.1619Z","shell.execute_reply":"2026-02-16T10:45:19.164528Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pandas as pd\nimport random\n\ndf = pd.read_csv(f\"{BASE_PATH}/train_2024.csv\")\n\npatient_ids = df[\"patient_id\"].unique()\nprint(\"Total patients:\", len(patient_ids))\n\nselected_patients = random.sample(list(patient_ids), 50)\nprint(\"Selected 50 patients.\")\n\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-16T11:04:45.193677Z","iopub.execute_input":"2026-02-16T11:04:45.194427Z","iopub.status.idle":"2026-02-16T11:04:45.215771Z","shell.execute_reply.started":"2026-02-16T11:04:45.194396Z","shell.execute_reply":"2026-02-16T11:04:45.214971Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!rm -rf /kaggle/working/*\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-16T11:05:41.493474Z","iopub.execute_input":"2026-02-16T11:05:41.493758Z","iopub.status.idle":"2026-02-16T11:05:44.615841Z","shell.execute_reply.started":"2026-02-16T11:05:41.493732Z","shell.execute_reply":"2026-02-16T11:05:44.614822Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!du -sh /kaggle/working\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-16T11:05:59.099677Z","iopub.execute_input":"2026-02-16T11:05:59.100029Z","iopub.status.idle":"2026-02-16T11:05:59.225716Z","shell.execute_reply.started":"2026-02-16T11:05:59.099973Z","shell.execute_reply":"2026-02-16T11:05:59.225055Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport pydicom\nimport cv2\nimport numpy as np\nfrom tqdm import tqdm\n\nBASE_PATH = \"/kaggle/input/competitions/rsna-2023-abdominal-trauma-detection\"\nsource_base = f\"{BASE_PATH}/train_images\"\nsave_base = \"/kaggle/working/processed_images\"\n\nos.makedirs(save_base, exist_ok=True)\n\nfor pid in tqdm(selected_patients):\n    patient_path = os.path.join(source_base, str(pid))\n    \n    for series in os.listdir(patient_path):\n        series_path = os.path.join(patient_path, series)\n        \n        for file in os.listdir(series_path):\n            if file.endswith(\".dcm\"):\n                dcm_path = os.path.join(series_path, file)\n                \n                ds = pydicom.dcmread(dcm_path)\n                img = ds.pixel_array.astype(np.float32)\n                \n                # Normalize\n                img = (img - np.min(img)) / (np.max(img) - np.min(img) + 1e-6)\n                img = (img * 255).astype(np.uint8)\n                \n                # Resize\n                img = cv2.resize(img, (256, 256))\n                \n                save_dir = os.path.join(save_base, str(pid))\n                os.makedirs(save_dir, exist_ok=True)\n                \n                save_path = os.path.join(save_dir, file.replace(\".dcm\", \".png\"))\n                cv2.imwrite(save_path, img)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-16T11:06:03.419029Z","iopub.execute_input":"2026-02-16T11:06:03.419631Z","iopub.status.idle":"2026-02-16T11:14:28.2259Z","shell.execute_reply.started":"2026-02-16T11:06:03.419597Z","shell.execute_reply":"2026-02-16T11:14:28.225188Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!du -sh /kaggle/working/processed_images\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-16T11:17:43.795066Z","iopub.execute_input":"2026-02-16T11:17:43.7958Z","iopub.status.idle":"2026-02-16T11:17:43.9729Z","shell.execute_reply.started":"2026-02-16T11:17:43.795773Z","shell.execute_reply":"2026-02-16T11:17:43.972041Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"RSNA Segmentation Structure\n\nIn\n/kaggle/input/competitions/rsna-2023-abdominal-trauma-detection/segmentations\n\nSegmentation masks are provided per patient / per series.","metadata":{}},{"cell_type":"code","source":"os.listdir(f\"{BASE_PATH}/segmentations\")[:5]\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-16T11:19:08.375845Z","iopub.execute_input":"2026-02-16T11:19:08.376601Z","iopub.status.idle":"2026-02-16T11:19:08.40061Z","shell.execute_reply.started":"2026-02-16T11:19:08.376568Z","shell.execute_reply":"2026-02-16T11:19:08.400049Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!pip install nibabel\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-16T11:19:43.393972Z","iopub.execute_input":"2026-02-16T11:19:43.394706Z","iopub.status.idle":"2026-02-16T11:19:49.6307Z","shell.execute_reply.started":"2026-02-16T11:19:43.394671Z","shell.execute_reply":"2026-02-16T11:19:49.630011Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Important Understanding\n\nYou processed:\n\n50 patients\n\nSampled 40 slices per patient\n\nSaved PNG slices\n\nBut masks are:\n\nFull 3D volumes\n\nContain segmentation labels for entire CT\n\nSo we must:\n\nLoad .nii\n\nSample SAME slice indices\n\nResize masks to 256×256\n\nSave corresponding mask PNG","metadata":{}},{"cell_type":"code","source":"mask_files = os.listdir(f\"{BASE_PATH}/segmentations\")\n\nmask_patient_ids = [int(f.replace(\".nii\", \"\")) for f in mask_files]\n\nprint(\"Patients with masks:\", len(mask_patient_ids))\nprint(\"Sample:\", mask_patient_ids[:10])\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-16T11:22:59.428032Z","iopub.execute_input":"2026-02-16T11:22:59.428347Z","iopub.status.idle":"2026-02-16T11:22:59.436072Z","shell.execute_reply.started":"2026-02-16T11:22:59.428322Z","shell.execute_reply":"2026-02-16T11:22:59.435389Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import random\n\nselected_patients = random.sample(mask_patient_ids, 30)\n\nprint(\"Selected masked patients:\", selected_patients)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-16T11:24:06.080695Z","iopub.execute_input":"2026-02-16T11:24:06.081355Z","iopub.status.idle":"2026-02-16T11:24:06.085847Z","shell.execute_reply.started":"2026-02-16T11:24:06.081325Z","shell.execute_reply":"2026-02-16T11:24:06.085051Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!rm -rf /kaggle/working/processed_images\n!rm -rf /kaggle/working/processed_masks\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-16T11:24:21.795892Z","iopub.execute_input":"2026-02-16T11:24:21.7962Z","iopub.status.idle":"2026-02-16T11:24:22.484801Z","shell.execute_reply.started":"2026-02-16T11:24:21.796173Z","shell.execute_reply":"2026-02-16T11:24:22.483817Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\n\ntrain_image_patients = os.listdir(f\"{BASE_PATH}/train_images\")\n\nvalid_mask_patients = [\n    pid for pid in mask_patient_ids \n    if str(pid) in train_image_patients\n]\n\nprint(\"Patients with both images & masks:\", len(valid_mask_patients))\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-16T11:25:30.447596Z","iopub.execute_input":"2026-02-16T11:25:30.448289Z","iopub.status.idle":"2026-02-16T11:25:30.504636Z","shell.execute_reply.started":"2026-02-16T11:25:30.448243Z","shell.execute_reply":"2026-02-16T11:25:30.504055Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df = pd.read_csv(f\"{BASE_PATH}/train_2024.csv\")\ndf.head()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-16T11:27:30.074915Z","iopub.execute_input":"2026-02-16T11:27:30.075714Z","iopub.status.idle":"2026-02-16T11:27:30.109506Z","shell.execute_reply.started":"2026-02-16T11:27:30.075686Z","shell.execute_reply":"2026-02-16T11:27:30.108894Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Create Overall Trauma Label\n\nWe define trauma if ANY injury column = 1.","metadata":{}},{"cell_type":"code","source":"print(df.columns)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-16T11:30:16.936705Z","iopub.execute_input":"2026-02-16T11:30:16.937304Z","iopub.status.idle":"2026-02-16T11:30:16.941128Z","shell.execute_reply.started":"2026-02-16T11:30:16.937275Z","shell.execute_reply":"2026-02-16T11:30:16.940256Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df[\"trauma\"] = df[\"any_injury\"]\n\ndf[\"trauma\"].value_counts()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-16T11:30:48.349514Z","iopub.execute_input":"2026-02-16T11:30:48.350032Z","iopub.status.idle":"2026-02-16T11:30:48.384121Z","shell.execute_reply.started":"2026-02-16T11:30:48.349973Z","shell.execute_reply":"2026-02-16T11:30:48.383552Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Dataset is imbalenced so take 100 patients","metadata":{}},{"cell_type":"code","source":"import random\n\npositive = df[df[\"trauma\"] == 1][\"patient_id\"].tolist()\nnegative = df[df[\"trauma\"] == 0][\"patient_id\"].tolist()\n\nselected_positive = random.sample(positive, 50)\nselected_negative = random.sample(negative, 50)\n\nselected_patients = selected_positive + selected_negative\n\nprint(\"Selected:\", len(selected_patients))\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-16T11:31:38.623232Z","iopub.execute_input":"2026-02-16T11:31:38.623519Z","iopub.status.idle":"2026-02-16T11:31:38.640479Z","shell.execute_reply.started":"2026-02-16T11:31:38.623495Z","shell.execute_reply":"2026-02-16T11:31:38.639875Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"cleaning prev data","metadata":{}},{"cell_type":"code","source":"!rm -rf /kaggle/working/processed_images\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-16T11:32:01.054338Z","iopub.execute_input":"2026-02-16T11:32:01.054918Z","iopub.status.idle":"2026-02-16T11:32:01.174774Z","shell.execute_reply.started":"2026-02-16T11:32:01.05489Z","shell.execute_reply":"2026-02-16T11:32:01.173913Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport pydicom\nimport cv2\nimport numpy as np\nfrom tqdm import tqdm\n\nBASE_PATH = \"/kaggle/input/competitions/rsna-2023-abdominal-trauma-detection\"\nsource_base = f\"{BASE_PATH}/train_images\"\nsave_base = \"/kaggle/working/processed_images\"\n\nos.makedirs(save_base, exist_ok=True)\n\nMAX_SLICES = 30\n\nfor pid in tqdm(selected_patients):\n    patient_path = os.path.join(source_base, str(pid))\n    \n    if not os.path.exists(patient_path):\n        continue\n    \n    for series in os.listdir(patient_path):\n        series_path = os.path.join(patient_path, series)\n        \n        dcm_files = sorted([f for f in os.listdir(series_path) if f.endswith(\".dcm\")])\n        \n        if len(dcm_files) > MAX_SLICES:\n            indices = np.linspace(0, len(dcm_files)-1, MAX_SLICES).astype(int)\n        else:\n            indices = range(len(dcm_files))\n        \n        for idx in indices:\n            dcm_path = os.path.join(series_path, dcm_files[idx])\n            \n            ds = pydicom.dcmread(dcm_path)\n            img = ds.pixel_array.astype(np.float32)\n            \n            img = (img - np.min(img)) / (np.max(img) - np.min(img) + 1e-6)\n            img = (img * 255).astype(np.uint8)\n            img = cv2.resize(img, (256, 256))\n            \n            save_dir = os.path.join(save_base, str(pid))\n            os.makedirs(save_dir, exist_ok=True)\n            \n            save_path = os.path.join(save_dir, dcm_files[idx].replace(\".dcm\", \".png\"))\n            cv2.imwrite(save_path, img)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-16T11:32:14.719272Z","iopub.execute_input":"2026-02-16T11:32:14.719911Z","iopub.status.idle":"2026-02-16T11:33:39.660092Z","shell.execute_reply.started":"2026-02-16T11:32:14.719876Z","shell.execute_reply":"2026-02-16T11:33:39.659391Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!du -sh /kaggle/working/processed_images\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-16T11:33:57.911875Z","iopub.execute_input":"2026-02-16T11:33:57.9127Z","iopub.status.idle":"2026-02-16T11:33:58.04573Z","shell.execute_reply.started":"2026-02-16T11:33:57.912669Z","shell.execute_reply":"2026-02-16T11:33:58.045045Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"label_dict = dict(zip(df[\"patient_id\"], df[\"trauma\"]))\n\nprint(\"Example:\", list(label_dict.items())[:5])\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-16T11:34:22.529147Z","iopub.execute_input":"2026-02-16T11:34:22.529472Z","iopub.status.idle":"2026-02-16T11:34:22.536666Z","shell.execute_reply.started":"2026-02-16T11:34:22.529442Z","shell.execute_reply":"2026-02-16T11:34:22.535893Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Proper Patient-Level Split","metadata":{}},{"cell_type":"code","source":"import glob\nimport os\nfrom sklearn.model_selection import train_test_split\n\n# Get unique patient IDs from processed images\nall_image_paths = glob.glob(\"/kaggle/working/processed_images/*/*.png\")\n\nall_patients = list(set([\n    int(os.path.basename(os.path.dirname(p)))\n    for p in all_image_paths\n]))\n\nprint(\"Total patients:\", len(all_patients))\n\ntrain_patients, val_patients = train_test_split(\n    all_patients, test_size=0.2, random_state=42\n)\n\nprint(\"Train patients:\", len(train_patients))\nprint(\"Val patients:\", len(val_patients))\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-16T11:35:17.694283Z","iopub.execute_input":"2026-02-16T11:35:17.694916Z","iopub.status.idle":"2026-02-16T11:35:18.796684Z","shell.execute_reply.started":"2026-02-16T11:35:17.694887Z","shell.execute_reply":"2026-02-16T11:35:18.795955Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import glob\nimport os\nimport cv2\nimport numpy as np\nimport torch\nfrom torch.utils.data import Dataset\n\nclass RSNAClassificationDataset(Dataset):\n    def __init__(self, image_dir, label_dict, patient_list):\n        all_paths = glob.glob(image_dir + \"/*/*.png\")\n        \n        self.image_paths = [\n            p for p in all_paths\n            if int(os.path.basename(os.path.dirname(p))) in patient_list\n        ]\n        \n        self.label_dict = label_dict\n\n    def __len__(self):\n        return len(self.image_paths)\n\n    def __getitem__(self, idx):\n        img_path = self.image_paths[idx]\n        \n        img = cv2.imread(img_path, 0)\n        img = img / 255.0\n        img = np.expand_dims(img, 0)\n        img = torch.tensor(img, dtype=torch.float32)\n\n        patient_id = int(os.path.basename(os.path.dirname(img_path)))\n        label = torch.tensor(self.label_dict[patient_id], dtype=torch.float32)\n\n        return img, label\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-16T11:39:15.166449Z","iopub.execute_input":"2026-02-16T11:39:15.166955Z","iopub.status.idle":"2026-02-16T11:39:15.173221Z","shell.execute_reply.started":"2026-02-16T11:39:15.166921Z","shell.execute_reply":"2026-02-16T11:39:15.172465Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sklearn.model_selection import train_test_split\nfrom torch.utils.data import DataLoader\n\nall_image_paths = glob.glob(\"/kaggle/working/processed_images/*/*.png\")\n\nall_patients = list(set([\n    int(os.path.basename(os.path.dirname(p)))\n    for p in all_image_paths\n]))\n\ntrain_patients, val_patients = train_test_split(\n    all_patients, test_size=0.2, random_state=42\n)\n\ntrain_dataset = RSNAClassificationDataset(\n    \"/kaggle/working/processed_images\",\n    label_dict,\n    train_patients\n)\n\nval_dataset = RSNAClassificationDataset(\n    \"/kaggle/working/processed_images\",\n    label_dict,\n    val_patients\n)\n\ntrain_loader = DataLoader(train_dataset, batch_size=4, shuffle=True)\nval_loader = DataLoader(val_dataset, batch_size=4, shuffle=False)\n\nprint(\"Train slices:\", len(train_dataset))\nprint(\"Val slices:\", len(val_dataset))\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-16T11:39:24.760506Z","iopub.execute_input":"2026-02-16T11:39:24.761321Z","iopub.status.idle":"2026-02-16T11:39:24.813169Z","shell.execute_reply.started":"2026-02-16T11:39:24.761288Z","shell.execute_reply":"2026-02-16T11:39:24.812548Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sklearn.metrics import accuracy_score\nimport torch\n\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\nmodel = model.to(device)\n\nepochs = 3\n\nfor epoch in range(epochs):\n    \n    # ================= TRAIN =================\n    model.train()\n    train_loss = 0\n    \n    for imgs, labels in train_loader:\n        imgs = imgs.to(device)\n        labels = labels.to(device).unsqueeze(1)\n        \n        optimizer.zero_grad()\n        outputs = model(imgs)\n        loss = loss_fn(outputs, labels)\n        loss.backward()\n        optimizer.step()\n        \n        train_loss += loss.item()\n    \n    \n    # ================= VALIDATION =================\n    model.eval()\n    val_loss = 0\n    all_preds = []\n    all_labels = []\n    \n    with torch.no_grad():\n        for imgs, labels in val_loader:\n            imgs = imgs.to(device)\n            labels = labels.to(device).unsqueeze(1)\n            \n            outputs = model(imgs)\n            loss = loss_fn(outputs, labels)\n            val_loss += loss.item()\n            \n            probs = torch.sigmoid(outputs)\n            preds = (probs > 0.5).float()\n            \n            all_preds.extend(preds.cpu().numpy())\n            all_labels.extend(labels.cpu().numpy())\n    \n    val_acc = accuracy_score(all_labels, all_preds)\n    \n    print(f\"Epoch {epoch+1}\")\n    print(f\"Train Loss: {train_loss/len(train_loader):.4f}\")\n    print(f\"Val Loss: {val_loss/len(val_loader):.4f}\")\n    print(f\"Val Accuracy: {val_acc:.4f}\")\n    print(\"-\"*40)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-16T11:40:52.283873Z","iopub.execute_input":"2026-02-16T11:40:52.284421Z","iopub.status.idle":"2026-02-16T11:42:40.377519Z","shell.execute_reply.started":"2026-02-16T11:40:52.284388Z","shell.execute_reply":"2026-02-16T11:42:40.376772Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"target_cols = [\n    \"bowel_injury\",\n    \"extravasation_injury\",\n    \"kidney_low\",\n    \"kidney_high\",\n    \"liver_low\",\n    \"liver_high\",\n    \"spleen_low\",\n    \"spleen_high\"\n]\n\nmulti_label_dict = {}\n\nfor _, row in df.iterrows():\n    multi_label_dict[row[\"patient_id\"]] = row[target_cols].values.astype(\"float32\")\n\nprint(\"Example label:\", list(multi_label_dict.items())[0])\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-16T11:46:16.773517Z","iopub.execute_input":"2026-02-16T11:46:16.774086Z","iopub.status.idle":"2026-02-16T11:46:17.814737Z","shell.execute_reply.started":"2026-02-16T11:46:16.774046Z","shell.execute_reply":"2026-02-16T11:46:17.813853Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class RSNAMultiLabelDataset(Dataset):\n    def __init__(self, image_dir, label_dict, patient_list):\n        all_paths = glob.glob(image_dir + \"/*/*.png\")\n        \n        self.image_paths = [\n            p for p in all_paths\n            if int(os.path.basename(os.path.dirname(p))) in patient_list\n        ]\n        \n        self.label_dict = label_dict\n\n    def __len__(self):\n        return len(self.image_paths)\n\n    def __getitem__(self, idx):\n        img_path = self.image_paths[idx]\n        \n        img = cv2.imread(img_path, 0)\n        img = img / 255.0\n        img = np.expand_dims(img, 0)\n        img = torch.tensor(img, dtype=torch.float32)\n\n        patient_id = int(os.path.basename(os.path.dirname(img_path)))\n        label = torch.tensor(self.label_dict[patient_id], dtype=torch.float32)\n\n        return img, label\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-16T11:46:28.112434Z","iopub.execute_input":"2026-02-16T11:46:28.112726Z","iopub.status.idle":"2026-02-16T11:46:28.118865Z","shell.execute_reply.started":"2026-02-16T11:46:28.1127Z","shell.execute_reply":"2026-02-16T11:46:28.118184Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_dataset = RSNAMultiLabelDataset(\n    \"/kaggle/working/processed_images\",\n    multi_label_dict,\n    train_patients\n)\n\nval_dataset = RSNAMultiLabelDataset(\n    \"/kaggle/working/processed_images\",\n    multi_label_dict,\n    val_patients\n)\n\ntrain_loader = DataLoader(train_dataset, batch_size=4, shuffle=True)\nval_loader = DataLoader(val_dataset, batch_size=4, shuffle=False)\n\nprint(\"Train slices:\", len(train_dataset))\nprint(\"Val slices:\", len(val_dataset))\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-16T11:46:37.965334Z","iopub.execute_input":"2026-02-16T11:46:37.965616Z","iopub.status.idle":"2026-02-16T11:46:38.001559Z","shell.execute_reply.started":"2026-02-16T11:46:37.965592Z","shell.execute_reply":"2026-02-16T11:46:38.000907Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model = models.efficientnet_b0(pretrained=True)\n\nmodel.features[0][0] = nn.Conv2d(\n    1, 32, kernel_size=3, stride=2, padding=1, bias=False\n)\n\nmodel.classifier[1] = nn.Linear(\n    model.classifier[1].in_features, 8  # 8 outputs now\n)\n\nmodel = model.to(device)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-16T11:46:47.321802Z","iopub.execute_input":"2026-02-16T11:46:47.322509Z","iopub.status.idle":"2026-02-16T11:46:47.458754Z","shell.execute_reply.started":"2026-02-16T11:46:47.322481Z","shell.execute_reply":"2026-02-16T11:46:47.457971Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import numpy as np\n\nall_labels = []\n\nfor _, labels in train_loader:\n    all_labels.append(labels.numpy())\n\nall_labels = np.concatenate(all_labels, axis=0)\n\nprint(\"Mean per class:\", all_labels.mean(axis=0))\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-16T11:51:51.360259Z","iopub.execute_input":"2026-02-16T11:51:51.36089Z","iopub.status.idle":"2026-02-16T11:51:55.043395Z","shell.execute_reply.started":"2026-02-16T11:51:51.360861Z","shell.execute_reply":"2026-02-16T11:51:55.042648Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"pos_counts = all_labels.sum(axis=0)\nneg_counts = len(all_labels) - pos_counts\n\npos_weight = torch.tensor(neg_counts / (pos_counts + 1e-6), dtype=torch.float32).to(device)\n\nprint(\"Pos weight:\", pos_weight)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-16T11:52:29.498974Z","iopub.execute_input":"2026-02-16T11:52:29.499596Z","iopub.status.idle":"2026-02-16T11:52:29.909224Z","shell.execute_reply.started":"2026-02-16T11:52:29.499568Z","shell.execute_reply":"2026-02-16T11:52:29.908454Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"loss_fn = nn.BCEWithLogitsLoss(pos_weight=pos_weight)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-16T11:53:11.40914Z","iopub.execute_input":"2026-02-16T11:53:11.409813Z","iopub.status.idle":"2026-02-16T11:53:11.413253Z","shell.execute_reply.started":"2026-02-16T11:53:11.409773Z","shell.execute_reply":"2026-02-16T11:53:11.412453Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torch\nimport torch.nn as nn\nimport torch.optim as optim\n\n# Loss with class imbalance handling\nloss_fn = nn.BCEWithLogitsLoss(pos_weight=pos_weight)\n\n# Optimizer\noptimizer = optim.Adam(model.parameters(), lr=1e-4)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-16T11:54:44.255285Z","iopub.execute_input":"2026-02-16T11:54:44.255604Z","iopub.status.idle":"2026-02-16T11:54:44.263424Z","shell.execute_reply.started":"2026-02-16T11:54:44.255578Z","shell.execute_reply":"2026-02-16T11:54:44.262696Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"epochs = 3\n\nfor epoch in range(epochs):\n    print(f\"\\nEpoch {epoch+1}\")\n    \n    # -------- TRAIN --------\n    model.train()\n    train_loss = 0\n    \n    for imgs, labels in train_loader:\n        imgs = imgs.to(device)\n        labels = labels.to(device)\n        \n        optimizer.zero_grad()\n        outputs = model(imgs)\n        loss = loss_fn(outputs, labels)\n        loss.backward()\n        optimizer.step()\n        \n        train_loss += loss.item()\n    \n    train_loss /= len(train_loader)\n    \n    \n    # -------- VALIDATION --------\n    model.eval()\n    val_loss = 0\n    \n    with torch.no_grad():\n        for imgs, labels in val_loader:\n            imgs = imgs.to(device)\n            labels = labels.to(device)\n            \n            outputs = model(imgs)\n            loss = loss_fn(outputs, labels)\n            val_loss += loss.item()\n    \n    val_loss /= len(val_loader)\n    \n    print(f\"Train Loss: {train_loss:.4f}\")\n    print(f\"Val Loss: {val_loss:.4f}\")\n    print(\"-\"*40)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-16T12:00:43.025652Z","iopub.execute_input":"2026-02-16T12:00:43.026214Z","iopub.status.idle":"2026-02-16T12:01:35.994037Z","shell.execute_reply.started":"2026-02-16T12:00:43.026184Z","shell.execute_reply":"2026-02-16T12:01:35.99327Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"for param in model.features.parameters():\n    param.requires_grad = False\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-16T12:00:18.850174Z","iopub.execute_input":"2026-02-16T12:00:18.850851Z","iopub.status.idle":"2026-02-16T12:00:18.854949Z","shell.execute_reply.started":"2026-02-16T12:00:18.850823Z","shell.execute_reply":"2026-02-16T12:00:18.854238Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"optimizer = torch.optim.Adam(\n    filter(lambda p: p.requires_grad, model.parameters()),\n    lr=1e-5\n)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-16T12:00:30.937364Z","iopub.execute_input":"2026-02-16T12:00:30.93795Z","iopub.status.idle":"2026-02-16T12:00:30.944392Z","shell.execute_reply.started":"2026-02-16T12:00:30.937922Z","shell.execute_reply":"2026-02-16T12:00:30.943676Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from torchvision.models import efficientnet_b0, EfficientNet_B0_Weights\nimport torch.nn as nn\n\nweights = EfficientNet_B0_Weights.DEFAULT\nbackbone = efficientnet_b0(weights=weights)\n\n# Modify first layer for 1-channel input\nbackbone.features[0][0] = nn.Conv2d(\n    1, 32, kernel_size=3, stride=2, padding=1, bias=False\n)\n\n# Remove classifier\nbackbone.classifier = nn.Identity()\n\nbackbone = backbone.to(device)\nbackbone.eval()  # Freeze backbone\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-16T12:04:28.861211Z","iopub.execute_input":"2026-02-16T12:04:28.861746Z","iopub.status.idle":"2026-02-16T12:04:29.029527Z","shell.execute_reply.started":"2026-02-16T12:04:28.861717Z","shell.execute_reply":"2026-02-16T12:04:29.028822Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"for param in backbone.parameters():\n    param.requires_grad = False\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-16T12:05:28.222224Z","iopub.execute_input":"2026-02-16T12:05:28.222997Z","iopub.status.idle":"2026-02-16T12:05:28.2276Z","shell.execute_reply.started":"2026-02-16T12:05:28.22295Z","shell.execute_reply":"2026-02-16T12:05:28.227008Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from collections import defaultdict\nimport torch\n\ndef extract_features(loader):\n    feature_dict = defaultdict(list)\n    label_dict = {}\n    \n    backbone.eval()\n    idx = 0\n    \n    with torch.no_grad():\n        for imgs, labels in loader:\n            imgs = imgs.to(device)\n            feats = backbone(imgs)  # [batch, 1280]\n            \n            batch_size = imgs.size(0)\n            \n            for i in range(batch_size):\n                img_path = loader.dataset.image_paths[idx]\n                patient_id = int(os.path.basename(os.path.dirname(img_path)))\n                \n                feature_dict[patient_id].append(feats[i].cpu())\n                label_dict[patient_id] = labels[i]\n                \n                idx += 1\n    \n    return feature_dict, label_dict\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-16T12:05:41.448437Z","iopub.execute_input":"2026-02-16T12:05:41.449164Z","iopub.status.idle":"2026-02-16T12:05:41.45483Z","shell.execute_reply.started":"2026-02-16T12:05:41.449134Z","shell.execute_reply":"2026-02-16T12:05:41.454057Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_features, train_labels = extract_features(train_loader)\nval_features, val_labels = extract_features(val_loader)\n\nprint(\"Train patients:\", len(train_features))\nprint(\"Val patients:\", len(val_features))\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-16T12:05:51.340107Z","iopub.execute_input":"2026-02-16T12:05:51.34065Z","iopub.status.idle":"2026-02-16T12:06:06.103882Z","shell.execute_reply.started":"2026-02-16T12:05:51.340619Z","shell.execute_reply":"2026-02-16T12:06:06.103157Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def aggregate_features(feature_dict):\n    agg = {}\n    for pid in feature_dict:\n        stacked = torch.stack(feature_dict[pid])  # [num_slices, 1280]\n        agg[pid] = stacked.mean(dim=0)  # [1280]\n    return agg\n\ntrain_agg = aggregate_features(train_features)\nval_agg = aggregate_features(val_features)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-16T12:06:35.706333Z","iopub.execute_input":"2026-02-16T12:06:35.70707Z","iopub.status.idle":"2026-02-16T12:06:35.726925Z","shell.execute_reply.started":"2026-02-16T12:06:35.70704Z","shell.execute_reply":"2026-02-16T12:06:35.726239Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class PatientDataset(torch.utils.data.Dataset):\n    def __init__(self, feature_dict, label_dict):\n        self.pids = list(feature_dict.keys())\n        self.features = feature_dict\n        self.labels = label_dict\n\n    def __len__(self):\n        return len(self.pids)\n\n    def __getitem__(self, idx):\n        pid = self.pids[idx]\n        feat = self.features[pid]\n        label = self.labels[pid].float()\n        return feat, label\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-16T12:06:45.729618Z","iopub.execute_input":"2026-02-16T12:06:45.730392Z","iopub.status.idle":"2026-02-16T12:06:45.734858Z","shell.execute_reply.started":"2026-02-16T12:06:45.730362Z","shell.execute_reply":"2026-02-16T12:06:45.734207Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_patient_dataset = PatientDataset(train_agg, train_labels)\nval_patient_dataset = PatientDataset(val_agg, val_labels)\n\ntrain_patient_loader = DataLoader(train_patient_dataset, batch_size=8, shuffle=True)\nval_patient_loader = DataLoader(val_patient_dataset, batch_size=8, shuffle=False)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-16T12:06:54.450519Z","iopub.execute_input":"2026-02-16T12:06:54.45107Z","iopub.status.idle":"2026-02-16T12:06:54.455168Z","shell.execute_reply.started":"2026-02-16T12:06:54.451039Z","shell.execute_reply":"2026-02-16T12:06:54.454432Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"patient_model = nn.Sequential(\n    nn.Linear(1280, 512),\n    nn.ReLU(),\n    nn.Dropout(0.3),\n    nn.Linear(512, 8)\n).to(device)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-16T12:07:15.707385Z","iopub.execute_input":"2026-02-16T12:07:15.70795Z","iopub.status.idle":"2026-02-16T12:07:15.718556Z","shell.execute_reply.started":"2026-02-16T12:07:15.707916Z","shell.execute_reply":"2026-02-16T12:07:15.718028Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"loss_fn = nn.BCEWithLogitsLoss(pos_weight=pos_weight)\noptimizer = torch.optim.Adam(patient_model.parameters(), lr=1e-4)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-16T12:07:25.61756Z","iopub.execute_input":"2026-02-16T12:07:25.618282Z","iopub.status.idle":"2026-02-16T12:07:25.62235Z","shell.execute_reply.started":"2026-02-16T12:07:25.618248Z","shell.execute_reply":"2026-02-16T12:07:25.621659Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"epochs = 10\n\nfor epoch in range(epochs):\n\n    patient_model.train()\n    train_loss = 0\n\n    for feats, labels in train_patient_loader:\n        feats = feats.to(device)\n        labels = labels.to(device)\n\n        optimizer.zero_grad()\n        outputs = patient_model(feats)\n        loss = loss_fn(outputs, labels)\n        loss.backward()\n        optimizer.step()\n\n        train_loss += loss.item()\n\n    train_loss /= len(train_patient_loader)\n\n    # Validation\n    patient_model.eval()\n    val_loss = 0\n\n    with torch.no_grad():\n        for feats, labels in val_patient_loader:\n            feats = feats.to(device)\n            labels = labels.to(device)\n\n            outputs = patient_model(feats)\n            loss = loss_fn(outputs, labels)\n            val_loss += loss.item()\n\n    val_loss /= len(val_patient_loader)\n\n    print(f\"Epoch {epoch+1}\")\n    print(f\"Train Loss: {train_loss:.4f}\")\n    print(f\"Val Loss: {val_loss:.4f}\")\n    print(\"-\"*40)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-16T12:07:36.759006Z","iopub.execute_input":"2026-02-16T12:07:36.759331Z","iopub.status.idle":"2026-02-16T12:07:37.12006Z","shell.execute_reply.started":"2026-02-16T12:07:36.759302Z","shell.execute_reply":"2026-02-16T12:07:37.119351Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sklearn.metrics import roc_auc_score\nimport numpy as np\n\npatient_model.eval()\n\nall_preds = []\nall_labels = []\n\nwith torch.no_grad():\n    for feats, labels in val_patient_loader:\n        feats = feats.to(device)\n        outputs = patient_model(feats)\n        \n        probs = torch.sigmoid(outputs).cpu().numpy()\n        all_preds.append(probs)\n        all_labels.append(labels.numpy())\n\nall_preds = np.vstack(all_preds)\nall_labels = np.vstack(all_labels)\n\naucs = []\n\nfor i in range(8):\n    try:\n        auc = roc_auc_score(all_labels[:, i], all_preds[:, i])\n        aucs.append(auc)\n        print(f\"Class {i} AUC: {auc:.4f}\")\n    except:\n        print(f\"Class {i} AUC: Not computable\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-16T12:08:50.29349Z","iopub.execute_input":"2026-02-16T12:08:50.294132Z","iopub.status.idle":"2026-02-16T12:08:50.336489Z","shell.execute_reply.started":"2026-02-16T12:08:50.294103Z","shell.execute_reply":"2026-02-16T12:08:50.335846Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"torch.save(backbone.state_dict(), \"/kaggle/working/backbone_weights.pth\")\ntorch.save(patient_model.state_dict(), \"/kaggle/working/patient_model_weights.pth\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-16T12:18:59.871681Z","iopub.execute_input":"2026-02-16T12:18:59.87246Z","iopub.status.idle":"2026-02-16T12:18:59.940094Z","shell.execute_reply.started":"2026-02-16T12:18:59.872428Z","shell.execute_reply":"2026-02-16T12:18:59.939333Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nos.listdir(\"/kaggle/working\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-16T13:43:59.373505Z","iopub.execute_input":"2026-02-16T13:43:59.373915Z","iopub.status.idle":"2026-02-16T13:43:59.38057Z","shell.execute_reply.started":"2026-02-16T13:43:59.373876Z","shell.execute_reply":"2026-02-16T13:43:59.379895Z"}},"outputs":[],"execution_count":null}]}