{"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":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session\n\n# Use the kagglehub client library to attach Kaggle resources like competitions, datasets, and models to your session\n# Learn more about kagglehub: https://github.com/Kaggle/kagglehub/blob/main/README.md\n\nimport kagglehub\n# kagglehub.dataset_download('<owner>/<dataset-slug>')","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport cv2\nimport random\nimport warnings\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\n\nimport pydicom\n\nimport torch\nimport torch.nn as nn\nfrom torch.utils.data import Dataset, DataLoader\n\nfrom torchvision import transforms\nfrom torchvision.models import efficientnet_b3, EfficientNet_B3_Weights\n\nfrom sklearn.model_selection import train_test_split\n\nfrom tqdm.auto import tqdm\n\nwarnings.filterwarnings(\"ignore\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-07T14:38:15.915174Z","iopub.execute_input":"2026-08-07T14:38:15.915575Z","iopub.status.idle":"2026-08-07T14:38:26.164682Z","shell.execute_reply.started":"2026-08-07T14:38:15.915547Z","shell.execute_reply":"2026-08-07T14:38:26.164129Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"DATA_DIR = \"/kaggle/input/competitions/rsna-knee-abnormality-detection\"\n\nTRAIN_SERIES_DIR = os.path.join(DATA_DIR, \"train_series\")\nTEST_SERIES_DIR = os.path.join(DATA_DIR, \"test_series\")\n\nTRAIN_CSV = os.path.join(DATA_DIR, \"train.csv\")\nTEST_CSV = os.path.join(DATA_DIR, \"test.csv\")\n\nTRAIN_SERIES_CSV = os.path.join(DATA_DIR, \"train_series.csv\")\nTEST_SERIES_CSV = os.path.join(DATA_DIR, \"test_series.csv\")\n\nSAMPLE_SUBMISSION_CSV = os.path.join(DATA_DIR, \"sample_submission.csv\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-07T14:38:31.19701Z","iopub.execute_input":"2026-08-07T14:38:31.197969Z","iopub.status.idle":"2026-08-07T14:38:31.2027Z","shell.execute_reply.started":"2026-08-07T14:38:31.197935Z","shell.execute_reply":"2026-08-07T14:38:31.201992Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_df = pd.read_csv(TRAIN_CSV)\ntest_df = pd.read_csv(TEST_CSV)\n\ntrain_series_df = pd.read_csv(TRAIN_SERIES_CSV)\ntest_series_df = pd.read_csv(TEST_SERIES_CSV)\n\nsample_submission = pd.read_csv(SAMPLE_SUBMISSION_CSV)\n\nprint(train_df.shape)\nprint(test_df.shape)\nprint(train_series_df.shape)\nprint(test_series_df.shape)\nprint(sample_submission.shape)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-07T14:38:36.605121Z","iopub.execute_input":"2026-08-07T14:38:36.605727Z","iopub.status.idle":"2026-08-07T14:38:36.876969Z","shell.execute_reply.started":"2026-08-07T14:38:36.605697Z","shell.execute_reply":"2026-08-07T14:38:36.876331Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"label_columns = [\n    \"ACL\",\n    \"MCL\",\n    \"Medial Meniscus\",\n    \"Lateral Meniscus\",\n    \"Medial OA\",\n    \"Lateral OA\",\n    \"PF OA\",\n    \"Effusion\",\n    \"Synovitis\",\n    \"Baker's\",\n    \"Contusion\",\n    \"Fracture\"\n]\n\nprint(label_columns)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-07T14:38:40.405234Z","iopub.execute_input":"2026-08-07T14:38:40.405922Z","iopub.status.idle":"2026-08-07T14:38:40.410253Z","shell.execute_reply.started":"2026-08-07T14:38:40.405896Z","shell.execute_reply":"2026-08-07T14:38:40.40946Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\nprint(device)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-07T14:38:44.230521Z","iopub.execute_input":"2026-08-07T14:38:44.231252Z","iopub.status.idle":"2026-08-07T14:38:44.515845Z","shell.execute_reply.started":"2026-08-07T14:38:44.231223Z","shell.execute_reply":"2026-08-07T14:38:44.51488Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport pydicom\nimport numpy as np\n\ndef load_middle_slice(series_folder):\n    \"\"\"\n    Load only the middle DICOM slice from a series.\n    Much faster than loading the entire volume.\n    \"\"\"\n\n    dicom_files = sorted([\n        os.path.join(series_folder, f)\n        for f in os.listdir(series_folder)\n        if f.endswith(\".dcm\")\n    ])\n\n    if len(dicom_files) == 0:\n        raise RuntimeError(f\"No DICOM files found in {series_folder}\")\n\n    middle_file = dicom_files[len(dicom_files) // 2]\n\n    ds = pydicom.dcmread(middle_file)\n\n    image = ds.pixel_array\n\n    return image","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-07T15:57:07.042403Z","iopub.execute_input":"2026-08-07T15:57:07.043031Z","iopub.status.idle":"2026-08-07T15:57:07.048533Z","shell.execute_reply.started":"2026-08-07T15:57:07.043003Z","shell.execute_reply":"2026-08-07T15:57:07.047592Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from torchvision import transforms\n\ntrain_transform = transforms.Compose([\n    transforms.ToPILImage(),\n    transforms.Resize((384, 384)),\n    transforms.RandomHorizontalFlip(p=0.5),\n    transforms.RandomRotation(10),\n    transforms.ToTensor(),\n])\n\nval_transform = transforms.Compose([\n    transforms.ToPILImage(),\n    transforms.Resize((384, 384)),\n    transforms.ToTensor(),\n])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-07T15:57:31.840758Z","iopub.execute_input":"2026-08-07T15:57:31.841224Z","iopub.status.idle":"2026-08-07T15:57:31.848735Z","shell.execute_reply.started":"2026-08-07T15:57:31.841195Z","shell.execute_reply":"2026-08-07T15:57:31.84789Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"merged_df = train_series_df.merge(\n    train_df,\n    on=\"StudyInstanceUID\",\n    how=\"inner\"\n)\n\nprint(\"Merged shape:\", merged_df.shape)\nprint(merged_df.columns.tolist())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-07T15:57:36.205939Z","iopub.execute_input":"2026-08-07T15:57:36.206283Z","iopub.status.idle":"2026-08-07T15:57:36.232551Z","shell.execute_reply.started":"2026-08-07T15:57:36.206255Z","shell.execute_reply":"2026-08-07T15:57:36.23186Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Remove rows that don't have labels\nmerged_df = merged_df.dropna(subset=label_columns).reset_index(drop=True)\n\nprint(\"Merged shape after removing NaNs:\", merged_df.shape)\n\nprint(\"\\nRemaining NaNs:\")\nprint(merged_df[label_columns].isna().sum())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-07T16:24:09.246187Z","iopub.execute_input":"2026-08-07T16:24:09.246899Z","iopub.status.idle":"2026-08-07T16:24:09.275631Z","shell.execute_reply.started":"2026-08-07T16:24:09.246871Z","shell.execute_reply":"2026-08-07T16:24:09.27482Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_df_split, val_df_split = train_test_split(\n    merged_df,\n    test_size=0.2,\n    random_state=42,\n    shuffle=True\n)\n\nprint(\"Train:\", train_df_split.shape)\nprint(\"Validation:\", val_df_split.shape)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-07T16:24:22.545762Z","iopub.execute_input":"2026-08-07T16:24:22.546525Z","iopub.status.idle":"2026-08-07T16:24:22.555617Z","shell.execute_reply.started":"2026-08-07T16:24:22.546494Z","shell.execute_reply":"2026-08-07T16:24:22.554769Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class KneeDataset(Dataset):\n\n    def __init__(self, dataframe, transform=None):\n        self.df = dataframe.reset_index(drop=True)\n        self.transform = transform\n\n    def __len__(self):\n        return len(self.df)\n\n    def __getitem__(self, idx):\n\n        row = self.df.iloc[idx]\n\n        study_id = row[\"StudyInstanceUID\"]\n        series_id = row[\"SeriesInstanceUID\"]\n\n        series_folder = os.path.join(\n            TRAIN_SERIES_DIR,\n            study_id,\n            series_id\n        )\n\n        # Load only the middle DICOM slice\n        try:\n            image = load_middle_slice(series_folder)\n        except Exception:\n            # Skip unreadable series\n            return self.__getitem__((idx + 1) % len(self.df))\n\n        # Normalize image\n        image = image.astype(np.float32)\n        image = (image - image.min()) / (image.max() - image.min() + 1e-8)\n        image = (image * 255).astype(np.uint8)\n\n        # Convert grayscale to RGB\n        image = cv2.cvtColor(image, cv2.COLOR_GRAY2RGB)\n\n        # Apply transforms\n        if self.transform:\n            image = self.transform(image)\n\n        # Labels\n        labels = torch.tensor(\n            row[label_columns].values.astype(np.float32),\n            dtype=torch.float32\n        )\n\n        return image, labels","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-07T16:24:25.905721Z","iopub.execute_input":"2026-08-07T16:24:25.906222Z","iopub.status.idle":"2026-08-07T16:24:25.913866Z","shell.execute_reply.started":"2026-08-07T16:24:25.906184Z","shell.execute_reply":"2026-08-07T16:24:25.912966Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_dataset = KneeDataset(\n    train_df_split,\n    transform=train_transform\n)\n\nval_dataset = KneeDataset(\n    val_df_split,\n    transform=val_transform\n)\n\nprint(len(train_dataset))\nprint(len(val_dataset))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-07T16:24:30.315344Z","iopub.execute_input":"2026-08-07T16:24:30.316068Z","iopub.status.idle":"2026-08-07T16:24:30.320817Z","shell.execute_reply.started":"2026-08-07T16:24:30.316024Z","shell.execute_reply":"2026-08-07T16:24:30.320131Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"image, labels = train_dataset[0]\n\nprint(image.shape)\nprint(labels.shape)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-07T16:24:36.825527Z","iopub.execute_input":"2026-08-07T16:24:36.826069Z","iopub.status.idle":"2026-08-07T16:24:36.847394Z","shell.execute_reply.started":"2026-08-07T16:24:36.82604Z","shell.execute_reply":"2026-08-07T16:24:36.846525Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_loader = DataLoader(\n    train_dataset,\n    batch_size=8,\n    shuffle=True,\n    num_workers=2,\n    pin_memory=True\n)\n\nval_loader = DataLoader(\n    val_dataset,\n    batch_size=8,\n    shuffle=False,\n    num_workers=2,\n    pin_memory=True\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-07T16:24:39.906455Z","iopub.execute_input":"2026-08-07T16:24:39.907052Z","iopub.status.idle":"2026-08-07T16:24:39.913192Z","shell.execute_reply.started":"2026-08-07T16:24:39.907023Z","shell.execute_reply":"2026-08-07T16:24:39.912226Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"images, labels = next(iter(train_loader))\n\nprint(images.shape)\nprint(labels.shape)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-07T15:18:56.809953Z","iopub.execute_input":"2026-08-07T15:18:56.810802Z","iopub.status.idle":"2026-08-07T15:19:02.739963Z","shell.execute_reply.started":"2026-08-07T15:18:56.810771Z","shell.execute_reply":"2026-08-07T15:19:02.739167Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model = efficientnet_b3(\n    weights=EfficientNet_B3_Weights.IMAGENET1K_V1\n)\n\nin_features = model.classifier[1].in_features\n\nmodel.classifier = nn.Sequential(\n    nn.Dropout(0.3),\n    nn.Linear(in_features, 12)\n)\n\nmodel = model.to(device)\n\nprint(\"Model loaded successfully!\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-07T16:24:49.706027Z","iopub.execute_input":"2026-08-07T16:24:49.706399Z","iopub.status.idle":"2026-08-07T16:24:50.224627Z","shell.execute_reply.started":"2026-08-07T16:24:49.706375Z","shell.execute_reply":"2026-08-07T16:24:50.223734Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"criterion = nn.BCEWithLogitsLoss()\n\noptimizer = torch.optim.AdamW(\n    model.parameters(),\n    lr=1e-4,\n    weight_decay=1e-4\n)\n\nprint(\"Optimizer ready!\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-07T15:19:09.429807Z","iopub.execute_input":"2026-08-07T15:19:09.430729Z","iopub.status.idle":"2026-08-07T15:19:09.436409Z","shell.execute_reply.started":"2026-08-07T15:19:09.430698Z","shell.execute_reply":"2026-08-07T15:19:09.435675Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from tqdm.auto import tqdm\n\ndef train_one_epoch(model, loader, optimizer, criterion, device):\n\n    model.train()\n\n    running_loss = 0.0\n\n    for images, labels in tqdm(loader):\n\n        images = images.to(device)\n        labels = labels.to(device)\n\n        optimizer.zero_grad()\n\n        outputs = model(images)\n\n        loss = criterion(outputs, labels)\n\n        loss.backward()\n\n        optimizer.step()\n\n        running_loss += loss.item()\n\n    return running_loss / len(loader)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-07T15:19:12.825199Z","iopub.execute_input":"2026-08-07T15:19:12.82579Z","iopub.status.idle":"2026-08-07T15:19:12.830927Z","shell.execute_reply.started":"2026-08-07T15:19:12.825761Z","shell.execute_reply":"2026-08-07T15:19:12.830045Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def validate(model, loader, criterion, device):\n\n    model.eval()\n\n    running_loss = 0.0\n\n    with torch.no_grad():\n\n        for images, labels in loader:\n\n            images = images.to(device)\n            labels = labels.to(device)\n\n            outputs = model(images)\n\n            loss = criterion(outputs, labels)\n\n            running_loss += loss.item()\n\n    return running_loss / len(loader)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-07T16:25:14.378372Z","iopub.execute_input":"2026-08-07T16:25:14.37884Z","iopub.status.idle":"2026-08-07T16:25:14.384667Z","shell.execute_reply.started":"2026-08-07T16:25:14.378809Z","shell.execute_reply":"2026-08-07T16:25:14.38373Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"EPOCHS = 10\n\nbest_loss = float(\"inf\")\n\nfor epoch in range(EPOCHS):\n\n    train_loss = train_one_epoch(\n        model,\n        train_loader,\n        optimizer,\n        criterion,\n        device\n    )\n\n    val_loss = validate(\n        model,\n        val_loader,\n        criterion,\n        device\n    )\n\n    print(\n        f\"Epoch {epoch+1}/{EPOCHS} | \"\n        f\"Train Loss: {train_loss:.4f} | \"\n        f\"Val Loss: {val_loss:.4f}\"\n    )\n\n    if val_loss < best_loss:\n        best_loss = val_loss\n        torch.save(model.state_dict(), \"best_model.pth\")\n\nprint(\"Training Complete!\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-07T15:21:54.6445Z","iopub.execute_input":"2026-08-07T15:21:54.645407Z","iopub.status.idle":"2026-08-07T15:48:34.637301Z","shell.execute_reply.started":"2026-08-07T15:21:54.645367Z","shell.execute_reply":"2026-08-07T15:48:34.635624Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_series_df.groupby(\"StudyInstanceUID\").size().describe()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-07T15:50:02.640784Z","iopub.execute_input":"2026-08-07T15:50:02.641146Z","iopub.status.idle":"2026-08-07T15:50:02.654651Z","shell.execute_reply.started":"2026-08-07T15:50:02.641064Z","shell.execute_reply":"2026-08-07T15:50:02.653746Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(train_df.shape)\nprint(train_series_df.shape)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-07T15:51:27.480976Z","iopub.execute_input":"2026-08-07T15:51:27.481816Z","iopub.status.idle":"2026-08-07T15:51:27.486271Z","shell.execute_reply.started":"2026-08-07T15:51:27.481782Z","shell.execute_reply":"2026-08-07T15:51:27.485444Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_loader = DataLoader(\n    train_dataset,\n    batch_size=8,\n    shuffle=True,\n    num_workers=2,\n    pin_memory=True\n)\n\nval_loader = DataLoader(\n    val_dataset,\n    batch_size=8,\n    shuffle=False,\n    num_workers=2,\n    pin_memory=True\n)\n\nprint(\"DataLoaders created successfully!\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-07T16:25:34.217499Z","iopub.execute_input":"2026-08-07T16:25:34.217945Z","iopub.status.idle":"2026-08-07T16:25:34.223536Z","shell.execute_reply.started":"2026-08-07T16:25:34.217914Z","shell.execute_reply":"2026-08-07T16:25:34.222818Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"images, labels = next(iter(train_loader))\n\nprint(images.shape)\nprint(labels.shape)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-07T16:02:12.005273Z","iopub.execute_input":"2026-08-07T16:02:12.005938Z","iopub.status.idle":"2026-08-07T16:02:12.748764Z","shell.execute_reply.started":"2026-08-07T16:02:12.005905Z","shell.execute_reply":"2026-08-07T16:02:12.747764Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"EPOCHS = 1\n\nbest_loss = float(\"inf\")\n\nfor epoch in range(EPOCHS):\n\n    train_loss = train_one_epoch(\n        model,\n        train_loader,\n        optimizer,\n        criterion,\n        device\n    )\n\n    val_loss = validate(\n        model,\n        val_loader,\n        criterion,\n        device\n    )\n\n    print(\n        f\"Epoch {epoch+1}/{EPOCHS} | \"\n        f\"Train Loss: {train_loss:.4f} | \"\n        f\"Val Loss: {val_loss:.4f}\"\n    )\n\n    if val_loss < best_loss:\n        best_loss = val_loss\n        torch.save(model.state_dict(), \"best_model.pth\")\n\nprint(\"Training Complete!\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-07T16:02:24.731354Z","iopub.execute_input":"2026-08-07T16:02:24.732067Z","iopub.status.idle":"2026-08-07T16:12:45.02104Z","shell.execute_reply.started":"2026-08-07T16:02:24.732029Z","shell.execute_reply":"2026-08-07T16:12:45.020068Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\"Any NaN labels:\", train_df_split[label_columns].isna().sum())\n\nprint(\"\\nUnique values in each label:\\n\")\n\nfor col in label_columns:\n    print(col, sorted(train_df_split[col].dropna().unique()))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-07T16:21:52.542456Z","iopub.execute_input":"2026-08-07T16:21:52.543706Z","iopub.status.idle":"2026-08-07T16:21:52.561677Z","shell.execute_reply.started":"2026-08-07T16:21:52.543657Z","shell.execute_reply":"2026-08-07T16:21:52.561006Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"image, labels = train_dataset[0]\n\nprint(\"Image min:\", image.min())\nprint(\"Image max:\", image.max())\n\nprint(\"Any NaN in image:\", torch.isnan(image).any())\nprint(\"Any NaN in labels:\", torch.isnan(labels).any())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-07T16:22:11.900821Z","iopub.execute_input":"2026-08-07T16:22:11.90169Z","iopub.status.idle":"2026-08-07T16:22:12.011325Z","shell.execute_reply.started":"2026-08-07T16:22:11.901658Z","shell.execute_reply":"2026-08-07T16:22:12.010622Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"image, labels = train_dataset[0]\n\nprint(torch.isnan(labels).any())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-07T16:25:53.98707Z","iopub.execute_input":"2026-08-07T16:25:53.987893Z","iopub.status.idle":"2026-08-07T16:25:54.003688Z","shell.execute_reply.started":"2026-08-07T16:25:53.987863Z","shell.execute_reply":"2026-08-07T16:25:54.002753Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\"Merged shape after removing NaNs:\", merged_df.shape)\nprint(merged_df[label_columns].isna().sum())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-07T16:26:12.493955Z","iopub.execute_input":"2026-08-07T16:26:12.494883Z","iopub.status.idle":"2026-08-07T16:26:12.501353Z","shell.execute_reply.started":"2026-08-07T16:26:12.494848Z","shell.execute_reply":"2026-08-07T16:26:12.500503Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sklearn.model_selection import train_test_split\n\ntrain_df_split, val_df_split = train_test_split(\n    merged_df,\n    test_size=0.2,\n    random_state=42,\n    shuffle=True\n)\n\nprint(train_df_split.shape)\nprint(val_df_split.shape)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-07T16:26:46.833488Z","iopub.execute_input":"2026-08-07T16:26:46.833933Z","iopub.status.idle":"2026-08-07T16:26:46.841328Z","shell.execute_reply.started":"2026-08-07T16:26:46.8339Z","shell.execute_reply":"2026-08-07T16:26:46.840416Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_dataset = KneeDataset(\n    train_df_split,\n    transform=train_transform\n)\n\nval_dataset = KneeDataset(\n    val_df_split,\n    transform=val_transform\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-07T16:26:58.253196Z","iopub.execute_input":"2026-08-07T16:26:58.253618Z","iopub.status.idle":"2026-08-07T16:26:58.258497Z","shell.execute_reply.started":"2026-08-07T16:26:58.253591Z","shell.execute_reply":"2026-08-07T16:26:58.25777Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_loader = DataLoader(\n    train_dataset,\n    batch_size=8,\n    shuffle=True,\n    num_workers=2,\n    pin_memory=True\n)\n\nval_loader = DataLoader(\n    val_dataset,\n    batch_size=8,\n    shuffle=False,\n    num_workers=2,\n    pin_memory=True\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-07T16:27:05.77441Z","iopub.execute_input":"2026-08-07T16:27:05.77484Z","iopub.status.idle":"2026-08-07T16:27:05.779532Z","shell.execute_reply.started":"2026-08-07T16:27:05.774812Z","shell.execute_reply":"2026-08-07T16:27:05.778664Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"image, labels = train_dataset[0]\n\nprint(torch.isnan(labels).any())\nprint(labels)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-07T16:27:15.172844Z","iopub.execute_input":"2026-08-07T16:27:15.17355Z","iopub.status.idle":"2026-08-07T16:27:15.188479Z","shell.execute_reply.started":"2026-08-07T16:27:15.173518Z","shell.execute_reply":"2026-08-07T16:27:15.187592Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"images, labels = next(iter(train_loader))\n\nprint(\"Images:\", images.shape)\nprint(\"Labels:\", labels.shape)\n\noutputs = model(images.to(device))\n\nprint(\"Output shape:\", outputs.shape)\nprint(\"Any NaN in outputs:\", torch.isnan(outputs).any())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-07T16:28:38.932355Z","iopub.execute_input":"2026-08-07T16:28:38.932785Z","iopub.status.idle":"2026-08-07T16:28:39.453684Z","shell.execute_reply.started":"2026-08-07T16:28:38.932759Z","shell.execute_reply":"2026-08-07T16:28:39.452824Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"EPOCHS = 1\n\nbest_loss = float(\"inf\")\n\nfor epoch in range(EPOCHS):\n\n    train_loss = train_one_epoch(\n        model,\n        train_loader,\n        optimizer,\n        criterion,\n        device\n    )\n\n    val_loss = validate(\n        model,\n        val_loader,\n        criterion,\n        device\n    )\n\n    print(\n        f\"Epoch {epoch+1}/{EPOCHS} | \"\n        f\"Train Loss: {train_loss:.4f} | \"\n        f\"Val Loss: {val_loss:.4f}\"\n    )\n\n    if val_loss < best_loss:\n        best_loss = val_loss\n        torch.save(model.state_dict(), \"best_model.pth\")\n\nprint(\"Training Complete!\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-07T16:29:04.072882Z","iopub.execute_input":"2026-08-07T16:29:04.073894Z","iopub.status.idle":"2026-08-07T16:29:14.090001Z","shell.execute_reply.started":"2026-08-07T16:29:04.073855Z","shell.execute_reply":"2026-08-07T16:29:14.089076Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class TestKneeDataset(Dataset):\n\n    def __init__(self, test_df, test_series_df, transform=None):\n        self.df = test_df\n        self.series_df = test_series_df\n        self.transform = transform\n\n    def __len__(self):\n        return len(self.df)\n\n    def __getitem__(self, idx):\n\n        study_id = self.df.iloc[idx][\"StudyInstanceUID\"]\n\n        series = self.series_df[\n            self.series_df[\"StudyInstanceUID\"] == study_id\n        ].iloc[0]\n\n        series_folder = os.path.join(\n            TEST_SERIES_DIR,\n            study_id,\n            series[\"SeriesInstanceUID\"]\n        )\n\n        image = load_middle_slice(series_folder)\n\n        image = image.astype(np.float32)\n        image = (image - image.min()) / (image.max() - image.min() + 1e-8)\n        image = (image * 255).astype(np.uint8)\n\n        image = cv2.cvtColor(image, cv2.COLOR_GRAY2RGB)\n\n        if self.transform:\n            image = self.transform(image)\n\n        return image","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-07T16:30:20.131222Z","iopub.execute_input":"2026-08-07T16:30:20.131647Z","iopub.status.idle":"2026-08-07T16:30:20.138025Z","shell.execute_reply.started":"2026-08-07T16:30:20.13162Z","shell.execute_reply":"2026-08-07T16:30:20.137177Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"test_dataset = TestKneeDataset(\n    test_df,\n    test_series_df,\n    transform=val_transform\n)\n\ntest_loader = DataLoader(\n    test_dataset,\n    batch_size=1,\n    shuffle=False,\n    num_workers=2,\n    pin_memory=True\n)\n\nprint(\"Test samples:\", len(test_dataset))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-07T16:30:45.665938Z","iopub.execute_input":"2026-08-07T16:30:45.666406Z","iopub.status.idle":"2026-08-07T16:30:45.67211Z","shell.execute_reply.started":"2026-08-07T16:30:45.666375Z","shell.execute_reply":"2026-08-07T16:30:45.671236Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model.load_state_dict(torch.load(\"best_model.pth\"))\nmodel.eval()\n\nprint(\"Model Loaded Successfully!\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-07T16:30:57.630631Z","iopub.execute_input":"2026-08-07T16:30:57.631049Z","iopub.status.idle":"2026-08-07T16:31:07.823294Z","shell.execute_reply.started":"2026-08-07T16:30:57.631019Z","shell.execute_reply":"2026-08-07T16:31:07.822582Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"predictions = []\n\nwith torch.no_grad():\n\n    for images in test_loader:\n\n        images = images.to(device)\n\n        outputs = model(images)\n\n        probs = torch.sigmoid(outputs)\n\n        predictions.append(\n            probs.cpu().numpy()[0]\n        )\n\npredictions = np.array(predictions)\n\nprint(predictions.shape)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-07T16:31:17.651377Z","iopub.execute_input":"2026-08-07T16:31:17.651788Z","iopub.status.idle":"2026-08-07T16:31:18.085925Z","shell.execute_reply.started":"2026-08-07T16:31:17.651758Z","shell.execute_reply":"2026-08-07T16:31:18.085165Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"submission = sample_submission.copy()\n\nsubmission[label_columns] = predictions\n\nsubmission.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-07T16:31:29.136046Z","iopub.execute_input":"2026-08-07T16:31:29.136569Z","iopub.status.idle":"2026-08-07T16:31:29.170697Z","shell.execute_reply.started":"2026-08-07T16:31:29.136536Z","shell.execute_reply":"2026-08-07T16:31:29.170016Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"submission.to_csv(\"submission.csv\", index=False)\n\nprint(\"submission.csv created successfully!\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-07T16:31:41.450361Z","iopub.execute_input":"2026-08-07T16:31:41.450785Z","iopub.status.idle":"2026-08-07T16:31:41.465538Z","shell.execute_reply.started":"2026-08-07T16:31:41.450756Z","shell.execute_reply":"2026-08-07T16:31:41.464771Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"submission","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-07T16:31:52.572905Z","iopub.execute_input":"2026-08-07T16:31:52.573537Z","iopub.status.idle":"2026-08-07T16:31:52.586184Z","shell.execute_reply.started":"2026-08-07T16:31:52.573506Z","shell.execute_reply":"2026-08-07T16:31:52.585333Z"}},"outputs":[],"execution_count":null}]}