{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.12.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[],"dockerImageVersionId":28755,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import os\nimport numpy as np\nimport pandas as pd\nimport torch\nimport torch.nn as nn\nfrom torch.utils.data import Dataset, DataLoader\nimport torchvision.models as models\nimport torchvision.transforms as T\nimport pydicom\nfrom PIL import Image\nfrom sklearn.model_selection import train_test_split\n\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\nprint(\"Using device:\", device)","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-09-05T09:54:32.969451Z","iopub.execute_input":"2026-09-05T09:54:32.969985Z","iopub.status.idle":"2026-09-05T09:54:32.975265Z","shell.execute_reply.started":"2026-09-05T09:54:32.969955Z","shell.execute_reply":"2026-09-05T09:54:32.974449Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"DATA_DIR = \"/kaggle/input/competitions/rsna-knee-abnormality-detection\"\n\nTRAIN_CSV = os.path.join(DATA_DIR, \"train.csv\")\nTRAIN_SERIES_CSV = os.path.join(DATA_DIR, \"train_series.csv\")\nTEST_CSV = os.path.join(DATA_DIR, \"test.csv\")\nTEST_SERIES_CSV = os.path.join(DATA_DIR, \"test_series.csv\")\nSAMPLE_SUB_CSV = os.path.join(DATA_DIR, \"sample_submission.csv\")\n\nTRAIN_SERIES_DIR = os.path.join(DATA_DIR, \"train_series\")\nTEST_SERIES_DIR = os.path.join(DATA_DIR, \"test_series\")\n\nLABEL_COLS = [\n    \"ACL\", \"MCL\", \"Medial Meniscus\", \"Lateral Meniscus\",\n    \"Medial OA\", \"Lateral OA\", \"PF OA\", \"Effusion\",\n    \"Synovitis\", \"Baker's\", \"Contusion\", \"Fracture\"\n]\n\ntrain_df = pd.read_csv(TRAIN_CSV)\ntrain_series_df = pd.read_csv(TRAIN_SERIES_CSV)\ntest_df = pd.read_csv(TEST_CSV)\ntest_series_df = pd.read_csv(TEST_SERIES_CSV)\nsample_sub = pd.read_csv(SAMPLE_SUB_CSV)\n\nprint(\"train:\", train_df.shape)\nprint(\"train_series:\", train_series_df.shape)\nprint(\"test:\", test_df.shape)\ntrain_df.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-05T09:54:32.982587Z","iopub.execute_input":"2026-09-05T09:54:32.982918Z","iopub.status.idle":"2026-09-05T09:54:33.140808Z","shell.execute_reply.started":"2026-09-05T09:54:32.982894Z","shell.execute_reply":"2026-09-05T09:54:33.140147Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def pick_series_for_study(study_id, series_df):\n    subset = series_df[series_df[\"StudyInstanceUID\"] == study_id]\n    if len(subset) == 0:\n        return None\n    sagittal = subset[subset[\"Anatomical_Plane\"] == \"Sagittal\"]\n    if len(sagittal) > 0:\n        return sagittal.iloc[0][\"SeriesInstanceUID\"]\n    return subset.iloc[0][\"SeriesInstanceUID\"]\n\n\ndef get_middle_slice_path(series_dir, study_id, series_id):\n    folder = os.path.join(series_dir, study_id, series_id)\n    if not os.path.isdir(folder):\n        return None\n    files = sorted(os.listdir(folder))\n    files = [f for f in files if f.endswith(\".dcm\")]\n    if len(files) == 0:\n        return None\n    middle_file = files[len(files) // 2]\n    return os.path.join(folder, middle_file)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-05T09:54:33.142457Z","iopub.execute_input":"2026-09-05T09:54:33.143042Z","iopub.status.idle":"2026-09-05T09:54:33.149001Z","shell.execute_reply.started":"2026-09-05T09:54:33.143017Z","shell.execute_reply":"2026-09-05T09:54:33.148068Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class KneeDataset(Dataset):\n    def __init__(self, df, series_df, series_dir, label_cols=None, transform=None):\n        self.df = df.reset_index(drop=True)\n        self.series_df = series_df\n        self.series_dir = series_dir\n        self.label_cols = label_cols\n        self.transform = transform\n\n    def __len__(self):\n        return len(self.df)\n\n    def load_dicom_as_image(self, path):\n        dcm = pydicom.dcmread(path)\n        arr = dcm.pixel_array.astype(np.float32)\n        arr = (arr - arr.min()) / (arr.max() - arr.min() + 1e-6)\n        arr = (arr * 255).astype(np.uint8)\n        img = Image.fromarray(arr).convert(\"RGB\")\n        return img\n\n    def __getitem__(self, idx):\n        row = self.df.iloc[idx]\n        study_id = row[\"StudyInstanceUID\"]\n\n        series_id = pick_series_for_study(study_id, self.series_df)\n        img_path = None\n        if series_id is not None:\n            img_path = get_middle_slice_path(self.series_dir, study_id, series_id)\n\n        if img_path is None:\n            img = Image.new(\"RGB\", (224, 224), color=(128, 128, 128))\n        else:\n            img = self.load_dicom_as_image(img_path)\n\n        if self.transform:\n            img = self.transform(img)\n\n        if self.label_cols is not None:\n            labels = row[self.label_cols].values.astype(np.float32)\n            return img, torch.tensor(labels)\n        else:\n            return img, study_id\n\n\ntransform = T.Compose([\n    T.Resize((224, 224)),\n    T.ToTensor(),\n    T.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),\n])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-05T09:54:45.964899Z","iopub.execute_input":"2026-09-05T09:54:45.965295Z","iopub.status.idle":"2026-09-05T09:54:45.973681Z","shell.execute_reply.started":"2026-09-05T09:54:45.965267Z","shell.execute_reply":"2026-09-05T09:54:45.973034Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class KneeModel(nn.Module):\n    def __init__(self, n_outputs=12):\n        super().__init__()\n        self.backbone = models.resnet18(weights=models.ResNet18_Weights.DEFAULT)\n        in_features = self.backbone.fc.in_features\n        self.backbone.fc = nn.Linear(in_features, n_outputs)\n\n    def forward(self, x):\n        return self.backbone(x)\n\n\nmodel = KneeModel(n_outputs=len(LABEL_COLS)).to(device)\ncriterion = nn.BCEWithLogitsLoss()\noptimizer = torch.optim.Adam(model.parameters(), lr=1e-4)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-05T09:55:53.993718Z","iopub.execute_input":"2026-09-05T09:55:53.994544Z","iopub.status.idle":"2026-09-05T09:55:55.221041Z","shell.execute_reply.started":"2026-09-05T09:55:53.994512Z","shell.execute_reply":"2026-09-05T09:55:55.220451Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Sirf wahi rows rakho jinke saare 12 labels present hain (baseline ke liye)\nlabeled_df = train_df.dropna(subset=LABEL_COLS).reset_index(drop=True)\nprint(\"Labeled studies available:\", len(labeled_df), \"out of\", len(train_df))\n\ntrain_part, val_part = train_test_split(labeled_df, test_size=0.15, random_state=42)\n\ntrain_dataset = KneeDataset(train_part, train_series_df, TRAIN_SERIES_DIR, LABEL_COLS, transform)\nval_dataset = KneeDataset(val_part, train_series_df, TRAIN_SERIES_DIR, LABEL_COLS, transform)\n\ntrain_loader = DataLoader(train_dataset, batch_size=16, shuffle=True, num_workers=2)\nval_loader = DataLoader(val_dataset, batch_size=16, shuffle=False, num_workers=2)\n\nEPOCHS = 3\n\nfor epoch in range(EPOCHS):\n    model.train()\n    train_loss = 0.0\n    for imgs, labels in train_loader:\n        imgs, labels = imgs.to(device), labels.to(device)\n        optimizer.zero_grad()\n        outputs = model(imgs)\n        loss = criterion(outputs, labels)\n        loss.backward()\n        optimizer.step()\n        train_loss += loss.item() * imgs.size(0)\n    train_loss /= len(train_dataset)\n\n    model.eval()\n    val_loss = 0.0\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    val_loss /= len(val_dataset)\n\n    print(f\"Epoch {epoch+1}/{EPOCHS} | Train Loss: {train_loss:.4f} | Val Loss: {val_loss:.4f}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-05T09:56:42.039037Z","iopub.execute_input":"2026-09-05T09:56:42.039893Z","iopub.status.idle":"2026-09-05T09:56:47.619627Z","shell.execute_reply.started":"2026-09-05T09:56:42.03986Z","shell.execute_reply":"2026-09-05T09:56:47.618871Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"test_dataset = KneeDataset(test_df, test_series_df, TEST_SERIES_DIR, label_cols=None, transform=transform)\ntest_loader = DataLoader(test_dataset, batch_size=16, shuffle=False, num_workers=2)\n\nmodel.eval()\nall_preds = []\nall_ids = []\n\nwith torch.no_grad():\n    for imgs, study_ids in test_loader:\n        imgs = imgs.to(device)\n        outputs = model(imgs)\n        probs = torch.sigmoid(outputs).cpu().numpy()\n        all_preds.append(probs)\n        all_ids.extend(study_ids)\n\nall_preds = np.concatenate(all_preds, axis=0)\n\nsubmission = pd.DataFrame(all_preds, columns=LABEL_COLS)\nsubmission.insert(0, \"StudyInstanceUID\", all_ids)\nsubmission = submission[sample_sub.columns]\n\nsubmission.to_csv(\"submission.csv\", index=False)\nprint(\"Saved submission.csv\")\nsubmission.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-05T09:57:36.790042Z","iopub.execute_input":"2026-09-05T09:57:36.790522Z","iopub.status.idle":"2026-09-05T09:57:37.026127Z","shell.execute_reply.started":"2026-09-05T09:57:36.790486Z","shell.execute_reply":"2026-09-05T09:57:37.025357Z"}},"outputs":[],"execution_count":null}]}