{"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 pandas as pd\nimport numpy as np\nimport pydicom\nfrom tqdm.auto import tqdm\nfrom PIL import Image\nfrom pathlib import Path\nfrom fastai.vision.all import *\nimport warnings\nwarnings.filterwarnings('ignore')","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-08-15T13:15:24.241828Z","iopub.execute_input":"2026-08-15T13:15:24.242051Z","iopub.status.idle":"2026-08-15T13:15:39.525621Z","shell.execute_reply.started":"2026-08-15T13:15:24.242029Z","shell.execute_reply":"2026-08-15T13:15:39.524769Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"DATA = Path('/kaggle/input/competitions/rsna-knee-abnormality-detection')\nTRAIN_SERIES_DIR = DATA / 'train_series'\nTEST_SERIES_DIR  = DATA / 'test_series'\nWORKING = Path('/kaggle/working')\nSLICE_DIR = WORKING / 'middle_slices'\nSLICE_DIR.mkdir(exist_ok=True)","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-08-15T13:15:39.528075Z","iopub.execute_input":"2026-08-15T13:15:39.528606Z","iopub.status.idle":"2026-08-15T13:15:39.533479Z","shell.execute_reply.started":"2026-08-15T13:15:39.528583Z","shell.execute_reply":"2026-08-15T13:15:39.532773Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"LABELS = ['ACL', 'MCL', 'Medial Meniscus', 'Lateral Meniscus',\n          'Medial OA', 'Lateral OA', 'PF OA', 'Effusion', 'Synovitis',\n          \"Baker's\", 'Contusion', 'Fracture']","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-08-15T13:15:39.534443Z","iopub.execute_input":"2026-08-15T13:15:39.53471Z","iopub.status.idle":"2026-08-15T13:15:39.673963Z","shell.execute_reply.started":"2026-08-15T13:15:39.534684Z","shell.execute_reply":"2026-08-15T13:15:39.673152Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def read_dicom(path):\n    ds = pydicom.dcmread(path, force=True)\n    img = ds.pixel_array.astype(np.float32)\n    if hasattr(ds, 'RescaleSlope') and hasattr(ds, 'RescaleIntercept'):\n        img = img * float(ds.RescaleSlope) + float(ds.RescaleIntercept)\n    img = (img - img.min()) / (img.max() - img.min() + 1e-8)\n    return img\n\ndef get_largest_series(study_dir: Path):\n    series_dirs = [p for p in study_dir.iterdir() if p.is_dir()]\n    if not series_dirs:\n        return None\n    return max(series_dirs, key=lambda p: len(list(p.glob('*.dcm'))))\n\ndef extract_middle_slice(study_uid: str, series_root: Path, out_dir: Path = None, save: bool = True):\n    study_dir = series_root / study_uid\n    if not study_dir.exists():\n        return None\n    sdir = get_largest_series(study_dir)\n    if sdir is None:\n        return None\n    dcms = sorted(list(sdir.glob('*.dcm')), key=lambda x: int(x.stem) if x.stem.isdigit() else 0)\n    if not dcms:\n        return None\n    mid = len(dcms) // 2\n    img = read_dicom(dcms[mid])\n    if save and out_dir is not None:\n        out_path = out_dir / f\"{study_uid}.png\"\n        Image.fromarray((img * 255).astype(np.uint8)).save(out_path)\n        return out_path\n    return img","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-08-15T13:15:39.674934Z","iopub.execute_input":"2026-08-15T13:15:39.675516Z","iopub.status.idle":"2026-08-15T13:15:39.686775Z","shell.execute_reply.started":"2026-08-15T13:15:39.675494Z","shell.execute_reply":"2026-08-15T13:15:39.685925Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train = pd.read_csv(DATA / 'train.csv')\nlabeled = train.dropna(subset=LABELS).copy()\nlabeled[LABELS] = labeled[LABELS].fillna(0).astype(int)","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-08-15T13:15:39.687834Z","iopub.execute_input":"2026-08-15T13:15:39.688218Z","iopub.status.idle":"2026-08-15T13:15:39.845004Z","shell.execute_reply.started":"2026-08-15T13:15:39.688172Z","shell.execute_reply":"2026-08-15T13:15:39.844119Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"rows = []\nfor uid in tqdm(labeled['StudyInstanceUID'], desc='Extracting train slices'):\n    img_path = extract_middle_slice(uid, TRAIN_SERIES_DIR, SLICE_DIR, save=True)\n    if img_path is not None:\n        row = {'StudyInstanceUID': uid, 'image_path': str(img_path)}\n        for lab in LABELS:\n            row[lab] = labeled.loc[labeled['StudyInstanceUID'] == uid, lab].values[0]\n        rows.append(row)","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-08-15T13:15:39.846004Z","iopub.execute_input":"2026-08-15T13:15:39.846299Z","iopub.status.idle":"2026-08-15T13:15:46.117945Z","shell.execute_reply.started":"2026-08-15T13:15:39.846273Z","shell.execute_reply":"2026-08-15T13:15:46.116985Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df_slices = pd.DataFrame(rows)\nprint(f\"Train slices ready: {len(df_slices)}\")","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-08-15T13:15:46.120272Z","iopub.execute_input":"2026-08-15T13:15:46.120592Z","iopub.status.idle":"2026-08-15T13:15:46.127433Z","shell.execute_reply.started":"2026-08-15T13:15:46.120569Z","shell.execute_reply":"2026-08-15T13:15:46.126545Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def get_y(row):\n    return [lab for lab in LABELS if row[lab] == 1]\n\ndblock = DataBlock(\n    blocks=(ImageBlock, MultiCategoryBlock(vocab=LABELS)),\n    get_x=ColReader('image_path'),\n    get_y=get_y,\n    splitter=RandomSplitter(valid_pct=0.2, seed=42),\n    item_tfms=Resize(384, method=ResizeMethod.Pad, pad_mode='zeros'),\n    batch_tfms=[\n        *aug_transforms(size=224, max_rotate=10, max_zoom=1.1,\n                        max_lighting=0.2, p_affine=0.75, p_lighting=0.75),\n        Normalize.from_stats(*imagenet_stats)\n    ]\n)","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-08-15T13:15:46.128607Z","iopub.execute_input":"2026-08-15T13:15:46.128924Z","iopub.status.idle":"2026-08-15T13:15:46.718245Z","shell.execute_reply.started":"2026-08-15T13:15:46.128893Z","shell.execute_reply":"2026-08-15T13:15:46.717367Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"dls = dblock.dataloaders(df_slices, bs=32, num_workers=2)","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-08-15T13:15:46.719263Z","iopub.execute_input":"2026-08-15T13:15:46.719543Z","iopub.status.idle":"2026-08-15T13:15:47.434532Z","shell.execute_reply.started":"2026-08-15T13:15:46.719513Z","shell.execute_reply":"2026-08-15T13:15:47.433748Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"dls.show_batch(max_n=9)","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-08-15T13:15:47.435421Z","iopub.execute_input":"2026-08-15T13:15:47.435703Z","iopub.status.idle":"2026-08-15T13:15:48.425548Z","shell.execute_reply.started":"2026-08-15T13:15:47.435673Z","shell.execute_reply":"2026-08-15T13:15:48.423797Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"learn = vision_learner(\n    dls,\n    resnet34,\n    metrics=[accuracy_multi, RocAucMulti(average='macro')],\n    loss_func=BCEWithLogitsLossFlat()\n)","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-08-15T13:15:48.426751Z","iopub.execute_input":"2026-08-15T13:15:48.427204Z","iopub.status.idle":"2026-08-15T13:15:49.571733Z","shell.execute_reply.started":"2026-08-15T13:15:48.427159Z","shell.execute_reply":"2026-08-15T13:15:49.570998Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"learn.lr_find()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-15T13:15:49.572904Z","iopub.execute_input":"2026-08-15T13:15:49.573324Z","iopub.status.idle":"2026-08-15T13:16:48.131341Z","shell.execute_reply.started":"2026-08-15T13:15:49.573294Z","shell.execute_reply":"2026-08-15T13:16:48.130499Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"learn.fine_tune(5, base_lr=1e-3)","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-08-15T13:16:48.132626Z","iopub.execute_input":"2026-08-15T13:16:48.132976Z","iopub.status.idle":"2026-08-15T13:16:54.261497Z","shell.execute_reply.started":"2026-08-15T13:16:48.132922Z","shell.execute_reply":"2026-08-15T13:16:54.260569Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#save model\nlearn.export(WORKING / 'baseline_export.pkl')","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-08-15T13:16:54.262988Z","iopub.execute_input":"2026-08-15T13:16:54.263327Z","iopub.status.idle":"2026-08-15T13:16:54.424137Z","shell.execute_reply.started":"2026-08-15T13:16:54.263298Z","shell.execute_reply":"2026-08-15T13:16:54.423425Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Submission","metadata":{}},{"cell_type":"code","source":"test = pd.read_csv(DATA / 'test.csv') \nsample_sub = pd.read_csv(DATA / 'sample_submission.csv')\ntest_uids = sample_sub['StudyInstanceUID'].unique().tolist()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-15T13:16:54.425289Z","iopub.execute_input":"2026-08-15T13:16:54.425698Z","iopub.status.idle":"2026-08-15T13:16:54.440576Z","shell.execute_reply.started":"2026-08-15T13:16:54.425666Z","shell.execute_reply":"2026-08-15T13:16:54.439861Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"TEST_SLICE_DIR = WORKING / 'test_middle_slices'\nTEST_SLICE_DIR.mkdir(exist_ok=True)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-15T13:16:54.441554Z","iopub.execute_input":"2026-08-15T13:16:54.441871Z","iopub.status.idle":"2026-08-15T13:16:54.445871Z","shell.execute_reply.started":"2026-08-15T13:16:54.441851Z","shell.execute_reply":"2026-08-15T13:16:54.445047Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"test_rows = []\nfor uid in tqdm(test_uids, desc='Extracting test slices'):\n    img_path = extract_middle_slice(uid, TEST_SERIES_DIR, TEST_SLICE_DIR, save=True)\n    if img_path is not None:\n        test_rows.append({'StudyInstanceUID': uid, 'image_path': str(img_path)})","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-15T13:16:54.446772Z","iopub.execute_input":"2026-08-15T13:16:54.447065Z","iopub.status.idle":"2026-08-15T13:16:54.808815Z","shell.execute_reply.started":"2026-08-15T13:16:54.447019Z","shell.execute_reply":"2026-08-15T13:16:54.807919Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df_test = pd.DataFrame(test_rows)\nprint(f\"Test slices ready: {len(df_test)}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-15T13:16:54.809924Z","iopub.execute_input":"2026-08-15T13:16:54.810243Z","iopub.status.idle":"2026-08-15T13:16:54.815594Z","shell.execute_reply.started":"2026-08-15T13:16:54.810186Z","shell.execute_reply":"2026-08-15T13:16:54.81476Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"test_dl = learn.dls.test_dl(df_test)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-15T13:16:54.816508Z","iopub.execute_input":"2026-08-15T13:16:54.816799Z","iopub.status.idle":"2026-08-15T13:16:54.829096Z","shell.execute_reply.started":"2026-08-15T13:16:54.816774Z","shell.execute_reply":"2026-08-15T13:16:54.828332Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"preds, _ = learn.get_preds(dl=test_dl)\npreds = preds.numpy()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-15T13:16:54.830135Z","iopub.execute_input":"2026-08-15T13:16:54.830441Z","iopub.status.idle":"2026-08-15T13:16:55.094773Z","shell.execute_reply.started":"2026-08-15T13:16:54.830411Z","shell.execute_reply":"2026-08-15T13:16:55.093707Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"sub = pd.DataFrame(preds, columns=LABELS)\nsub.insert(0, 'StudyInstanceUID', df_test['StudyInstanceUID'].values)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-15T13:16:55.096128Z","iopub.execute_input":"2026-08-15T13:16:55.097506Z","iopub.status.idle":"2026-08-15T13:16:55.104313Z","shell.execute_reply.started":"2026-08-15T13:16:55.097466Z","shell.execute_reply":"2026-08-15T13:16:55.103256Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"sub = sub[sample_sub.columns]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-15T13:16:55.1089Z","iopub.execute_input":"2026-08-15T13:16:55.109627Z","iopub.status.idle":"2026-08-15T13:16:55.118869Z","shell.execute_reply.started":"2026-08-15T13:16:55.109594Z","shell.execute_reply":"2026-08-15T13:16:55.118112Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"missing = set(sample_sub['StudyInstanceUID']) - set(sub['StudyInstanceUID'])\nif missing:\n    print(f\"Missing {len(missing)} studies – filling with 0.5\")\n    fill = pd.DataFrame({'StudyInstanceUID': list(missing)})\n    for c in LABELS:\n        fill[c] = 0.5\n    sub = pd.concat([sub, fill], ignore_index=True)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-15T13:16:55.119811Z","iopub.execute_input":"2026-08-15T13:16:55.120093Z","iopub.status.idle":"2026-08-15T13:16:55.128389Z","shell.execute_reply.started":"2026-08-15T13:16:55.120066Z","shell.execute_reply":"2026-08-15T13:16:55.127435Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"sub = sub.set_index('StudyInstanceUID').loc[sample_sub['StudyInstanceUID']].reset_index()\nsub.to_csv(WORKING / 'submission.csv', index=False)\nprint(\"Saved submission.csv\")\nprint(sub.head())\nprint(sub.shape)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-15T13:16:55.129671Z","iopub.execute_input":"2026-08-15T13:16:55.129951Z","iopub.status.idle":"2026-08-15T13:16:55.156355Z","shell.execute_reply.started":"2026-08-15T13:16:55.129922Z","shell.execute_reply":"2026-08-15T13:16:55.155593Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# !kaggle competitions submit -c rsna-knee-abnormality-detection -f /kaggle/working/submission.csv -m \"Baseline: Resnet34 https://www.kaggle.com/code/utkarshtomar736/rsna-baseline-fastai\"","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-15T13:27:46.164759Z","iopub.execute_input":"2026-08-15T13:27:46.16545Z","iopub.status.idle":"2026-08-15T13:27:46.17008Z","shell.execute_reply.started":"2026-08-15T13:27:46.165416Z","shell.execute_reply":"2026-08-15T13:27:46.169291Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}