{"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 shutil, sys, os\nLIB = '/kaggle/input/datasets/jiachuanbai/rsna-knee/rsna_knee_lib/rsna_knee'\nLABELS_DIR = '/kaggle/input/datasets/jiachuanbai/rsna-knee/rsna_knee_lib/labels'\nPREV = '/kaggle/input/notebooks/jiachuanbai/explo-attention-is-what-you-kneed'\nshutil.copytree(LIB, '/kaggle/working/rsna_knee', dirs_exist_ok=True)\nshutil.copytree(LABELS_DIR, '/kaggle/working/', dirs_exist_ok=True)\nsys.path.insert(0, '/kaggle/working')\n!pip install -q pylibjpeg pylibjpeg-libjpeg pylibjpeg-openjpeg python-gdcm\nfrom rsna_knee.kaggle_setup import setup\nP = setup(competition='/kaggle/input/competitions/rsna-knee-abnormality-detection')","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-09-05T21:01:51.457537Z","iopub.execute_input":"2026-09-05T21:01:51.457968Z","iopub.status.idle":"2026-09-05T21:02:05.321936Z","shell.execute_reply.started":"2026-09-05T21:01:51.457922Z","shell.execute_reply":"2026-09-05T21:02:05.319651Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"for name in os.listdir(PREV):\n    if 'cache' in name:\n        print(name)\n        shutil.copytree(f'{PREV}/{name}', f'/kaggle/working/{name}', dirs_exist_ok=True)\n        ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-05T21:00:34.665788Z","iopub.execute_input":"2026-09-05T21:00:34.667346Z","iopub.status.idle":"2026-09-05T21:01:51.452629Z","shell.execute_reply.started":"2026-09-05T21:00:34.667302Z","shell.execute_reply":"2026-09-05T21:01:51.45086Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from IPython.display import Image, display\n\ndef show(name):\n    \"\"\"The modules save figures to disk (matplotlib Agg backend),\n    so display them explicitly.\"\"\"\n    display(Image(filename=str(P.figures / name)))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-05T21:04:59.356375Z","iopub.execute_input":"2026-09-05T21:04:59.358363Z","iopub.status.idle":"2026-09-05T21:04:59.367267Z","shell.execute_reply.started":"2026-09-05T21:04:59.358273Z","shell.execute_reply":"2026-09-05T21:04:59.366124Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Explo data and labels ","metadata":{}},{"cell_type":"markdown","source":"## Directory layout and size","metadata":{}},{"cell_type":"code","source":"from rsna_knee.explore import show_tree, count_structure\nshow_tree(n_studies=2, n_series=3, n_slices=3)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-03T23:21:13.397417Z","iopub.execute_input":"2026-09-03T23:21:13.398062Z","iopub.status.idle":"2026-09-03T23:21:16.150965Z","shell.execute_reply.started":"2026-09-03T23:21:13.398032Z","shell.execute_reply":"2026-09-03T23:21:16.150218Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"series_df, train_df = count_structure()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-03T23:21:16.152699Z","iopub.execute_input":"2026-09-03T23:21:16.153015Z","iopub.status.idle":"2026-09-03T23:21:16.329053Z","shell.execute_reply.started":"2026-09-03T23:21:16.152977Z","shell.execute_reply":"2026-09-03T23:21:16.328392Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":" ## Image geometry and acquisition heterogeneity","metadata":{}},{"cell_type":"code","source":"from rsna_knee.explore import scan_headers, plot_header_stats\nhdr = scan_headers(n_studies=60)\nplot_header_stats(hdr)\nshow('e1_headers.png')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-03T23:21:16.802674Z","iopub.execute_input":"2026-09-03T23:21:16.803012Z","iopub.status.idle":"2026-09-03T23:21:18.539876Z","shell.execute_reply.started":"2026-09-03T23:21:16.802986Z","shell.execute_reply":"2026-09-03T23:21:18.539167Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(hdr.left.value_counts(dropna=False))\nprint(f\"\\nunknown laterality: {100*hdr.left.isna().mean():.1f}% of series\")\nprint(\"if this is large, drop hflip from augmentation and TTA\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-03T23:21:18.541357Z","iopub.execute_input":"2026-09-03T23:21:18.541737Z","iopub.status.idle":"2026-09-03T23:21:18.54812Z","shell.execute_reply.started":"2026-09-03T23:21:18.541693Z","shell.execute_reply":"2026-09-03T23:21:18.547299Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Labels","metadata":{}},{"cell_type":"code","source":"from rsna_knee.explore import label_overview\ngold = label_overview(train_df)\nshow('e2_labels.png')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-03T23:21:19.783522Z","iopub.execute_input":"2026-09-03T23:21:19.783797Z","iopub.status.idle":"2026-09-03T23:21:20.278751Z","shell.execute_reply.started":"2026-09-03T23:21:19.783775Z","shell.execute_reply":"2026-09-03T23:21:20.278071Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":" ## Data visualization","metadata":{}},{"cell_type":"code","source":"from rsna_knee.explore import show_study\nuid = train_df.StudyInstanceUID.iloc[0]\nshow_study(uid)\nshow(f'e3_study_{uid[:10]}.png')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-03T23:21:20.279996Z","iopub.execute_input":"2026-09-03T23:21:20.280304Z","iopub.status.idle":"2026-09-03T23:21:24.041767Z","shell.execute_reply.started":"2026-09-03T23:21:20.280238Z","shell.execute_reply":"2026-09-03T23:21:24.040637Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from rsna_knee.explore import show_label_examples\nfor lab in ['ACL', 'Medial Meniscus', \"Baker's\"]:\n    show_label_examples(lab, n=3)\n    show(f\"e4_{lab.replace(' ','_').replace(chr(39),'')}_pos.png\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-03T23:21:24.043369Z","iopub.execute_input":"2026-09-03T23:21:24.043707Z","iopub.status.idle":"2026-09-03T23:21:30.529886Z","shell.execute_reply.started":"2026-09-03T23:21:24.043683Z","shell.execute_reply":"2026-09-03T23:21:30.529058Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# positives beside negatives for the same finding\nfrom rsna_knee.explore import compare_pos_neg\ncompare_pos_neg('Effusion', n=2)\nshow('e4_Effusion_pos.png'); show('e4_Effusion_neg.png')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-03T23:21:30.531074Z","iopub.execute_input":"2026-09-03T23:21:30.531452Z","iopub.status.idle":"2026-09-03T23:21:33.492067Z","shell.execute_reply.started":"2026-09-03T23:21:30.531427Z","shell.execute_reply":"2026-09-03T23:21:33.491239Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Preprocessing","metadata":{}},{"cell_type":"markdown","source":"## Compress image ","metadata":{}},{"cell_type":"code","source":"from rsna_knee.kaggle_setup import check_decoders\ncheck_decoders()\nprint('\\nAll-uncompressed dataset -> MISS lines are fine here.')\nprint('Still run nb_wheels before the submission notebook, as insurance.')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-03T23:21:33.494379Z","iopub.execute_input":"2026-09-03T23:21:33.49471Z","iopub.status.idle":"2026-09-03T23:21:33.500877Z","shell.execute_reply.started":"2026-09-03T23:21:33.494686Z","shell.execute_reply":"2026-09-03T23:21:33.500069Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import time, pandas as pd\nn_studies = pd.read_csv(P.train_series_csv).StudyInstanceUID.nunique()\nt0 = time.time()\n!cd /kaggle/working && python -m rsna_knee.preprocess --split train --workers 4 --limit 20\ndt = time.time() - t0\nprint(f\"\\n{dt:.0f}s for 20 -> ~{dt/20*n_studies/3600:.1f}h for all {n_studies}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-03T23:21:33.502163Z","iopub.execute_input":"2026-09-03T23:21:33.503086Z","iopub.status.idle":"2026-09-03T23:21:35.013488Z","shell.execute_reply.started":"2026-09-03T23:21:33.503058Z","shell.execute_reply":"2026-09-03T23:21:35.012352Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import numpy as np, matplotlib.pyplot as plt\nfrom pathlib import Path\nfrom rsna_knee.preprocess import from_sprite\n\nf = sorted(Path('/kaggle/working/cache_train').glob('*.npz'))[0]\nz = np.load(f); S, N, H, W = [int(v) for v in z['shape']]\nvols = from_sprite(z['jpg'].tobytes(), S, N, H)\nmb = f.stat().st_size / 1e6\nprint(f\"{vols.shape}   {mb:.2f} MB/study   -> {mb*n_studies/1000:.1f} GB projected\")\nprint(\"desc [plane, fluid, fatsat]:\\n\", z['desc'])\n\nfig, ax = plt.subplots(S, 6, figsize=(12, 2.1*S))\nfor s in range(S):\n    for c, n in enumerate(np.linspace(0, N-1, 6).astype(int)):\n        ax[s, c].imshow(vols[s, n], cmap='gray'); ax[s, c].axis('off')\nplt.tight_layout(); plt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-03T23:21:35.015123Z","iopub.execute_input":"2026-09-03T23:21:35.015509Z","iopub.status.idle":"2026-09-03T23:21:35.566486Z","shell.execute_reply.started":"2026-09-03T23:21:35.015473Z","shell.execute_reply":"2026-09-03T23:21:35.565505Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!cd /kaggle/working && python -u -m rsna_knee.preprocess \\\n    --split train --workers 4 --size 384 \\\n    --out /kaggle/working/cache_train_384\n!cd /kaggle/working && python -m rsna_knee.preprocess --split train --workers 4\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-05T21:05:41.983299Z","iopub.execute_input":"2026-09-05T21:05:41.983916Z","iopub.status.idle":"2026-09-05T21:05:48.068973Z","shell.execute_reply.started":"2026-09-05T21:05:41.983861Z","shell.execute_reply":"2026-09-05T21:05:48.067546Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pandas as pd\nfrom pathlib import Path\nqc = pd.read_csv('/kaggle/working/qc_train_0.csv')\nprint(f\"series            : {len(qc)}\")\nprint(f\"decode failures   : {(~qc.decoded).sum()}\")\nprint(f\"degenerate        : {qc.degenerate.sum()}\")\nprint(f\"plane mismatch    : {qc.plane_mismatch.sum()}\")\nprint(f\"laterality unknown: {(~qc.laterality_known).sum()} \"\n      f\"({100*(~qc.laterality_known).mean():.1f}%)\")\n\nc = list(Path('/kaggle/working/cache_train').glob('*.npz'))\nprint(f\"\\nstudies cached    : {len(c)}\")\nprint(f\"cache size        : {sum(p.stat().st_size for p in c)/1e9:.2f} GB\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-03T23:21:37.038528Z","iopub.execute_input":"2026-09-03T23:21:37.038866Z","iopub.status.idle":"2026-09-03T23:21:37.108347Z","shell.execute_reply.started":"2026-09-03T23:21:37.038836Z","shell.execute_reply":"2026-09-03T23:21:37.107681Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Clean labels from report to label ","metadata":{}},{"cell_type":"code","source":"# ═══════════════ THE ONLY CELL YOU EDIT ═══════════════\nLABELER = 'llm'          # 'rules' | 'llm' | 'duel'\nMODEL_A = 'Qwen/Qwen2.5-7B-Instruct-AWQ'\nMODEL_B = 'casperhansen/llama-3-8b-instruct-awq'   # duel only, cross-family\nSAMPLES = 3              # >1 = vote for graded probabilities\nSMOKE   = False           # True = 20 reports first. Always do this once.\n# ══════════════════════════════════════════════════════\nprint(f'LABELER={LABELER}  SMOKE={SMOKE}')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-02T21:31:41.845643Z","iopub.execute_input":"2026-09-02T21:31:41.846Z","iopub.status.idle":"2026-09-02T21:31:41.851171Z","shell.execute_reply.started":"2026-09-02T21:31:41.845976Z","shell.execute_reply":"2026-09-02T21:31:41.850399Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!pip install -q transformers sentencepiece umap-learn langdetect iterative-stratification\nif LABELER in ('llm', 'duel') and not Path(\"/kaggle/working/labels_final.csv\").exists():\n    !pip install -q vllm autoawq","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-02T21:31:41.852199Z","iopub.execute_input":"2026-09-02T21:31:41.852499Z","iopub.status.idle":"2026-09-02T21:31:48.330521Z","shell.execute_reply.started":"2026-09-02T21:31:41.852471Z","shell.execute_reply":"2026-09-02T21:31:48.329747Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pandas as pd\nfrom rsna_knee.report_labeler import rules_frame, validate\ntrain = pd.read_csv(P.train_csv)\nrules = rules_frame(train)\nprint(validate(rules, train).to_string(index=False))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-02T21:31:48.331745Z","iopub.execute_input":"2026-09-02T21:31:48.332157Z","iopub.status.idle":"2026-09-02T21:31:53.436981Z","shell.execute_reply.started":"2026-09-02T21:31:48.332123Z","shell.execute_reply":"2026-09-02T21:31:53.436081Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"OUT = {'llm': '/kaggle/working/labels_llm.csv',\n       'duel': '/kaggle/working/labels_duel.csv'}.get(LABELER)\n\nif LABELER == 'rules':\n    print('rules only — nothing to run')\nelif OUT and Path(OUT).exists():\n    import pandas as pd\n    print(f'{OUT} exists — {len(pd.read_csv(OUT))} rows, skipping')\nelif LABELER == 'llm':\n    lim = '--limit 20' if SMOKE else ''\n    !cd /kaggle/working && python -u -m rsna_knee.llm_labeler \\\n        --model {MODEL_A} --samples {SAMPLES} \\\n        --tensor-parallel-size 2 {lim}\nelif LABELER == 'duel':\n    lim = '--limit 20' if SMOKE else ''\n    !cd /kaggle/working && python -u -m rsna_knee.llm_duel \\\n        --model-a {MODEL_A} --model-b {MODEL_B} \\\n        --samples {SAMPLES} --adjudicate --tensor-parallel-size 2 {lim}","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-02T15:50:05.285083Z","iopub.execute_input":"2026-09-02T15:50:05.2859Z","iopub.status.idle":"2026-09-02T16:36:23.920273Z","shell.execute_reply.started":"2026-09-02T15:50:05.285856Z","shell.execute_reply":"2026-09-02T16:36:23.919403Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from pathlib import Path\nRECOMPUTE  = False\nSRC = {'rules': 'rules',\n       'llm':   '/kaggle/working/labels_llm.csv',\n       'duel':  '/kaggle/working/labels_duel.csv'}[LABELER]\nFINAL = Path('/kaggle/working/labels_final.csv')\n\nif FINAL.exists() and not RECOMPUTE:\n    print(f'reusing existing {FINAL}; skipping distillation')\nelse:\n    !cd /kaggle/working && python -m rsna_knee.report_labeler --source {SRC} --gpus 2","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-02T21:31:53.43823Z","iopub.execute_input":"2026-09-02T21:31:53.438694Z","iopub.status.idle":"2026-09-02T21:31:53.445096Z","shell.execute_reply.started":"2026-09-02T21:31:53.438667Z","shell.execute_reply":"2026-09-02T21:31:53.444476Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"final = pd.read_csv('/kaggle/working/labels_final.csv')\nprint(final.shape, '| gold:', int(final.is_gold.sum()))\nfinal.head(3)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-02T21:31:53.446177Z","iopub.execute_input":"2026-09-02T21:31:53.446571Z","iopub.status.idle":"2026-09-02T21:31:53.496131Z","shell.execute_reply.started":"2026-09-02T21:31:53.446547Z","shell.execute_reply":"2026-09-02T21:31:53.495053Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Embedding space projection viz","metadata":{}},{"cell_type":"code","source":"#!cd /kaggle/working && python -u -m rsna_knee.embed_viz --k 10","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-04T08:27:20.333274Z","iopub.execute_input":"2026-09-04T08:27:20.333568Z","iopub.status.idle":"2026-09-04T08:28:58.414364Z","shell.execute_reply.started":"2026-09-04T08:27:20.333532Z","shell.execute_reply":"2026-09-04T08:28:58.413356Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#show('r1_label_panels.png')\n#show('r2_clusters.png')\n#show('r3_confounds.png')\n#show('r4_probe.png')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-03T23:23:04.533791Z","iopub.execute_input":"2026-09-03T23:23:04.53442Z","iopub.status.idle":"2026-09-03T23:23:04.559977Z","shell.execute_reply.started":"2026-09-03T23:23:04.534385Z","shell.execute_reply":"2026-09-03T23:23:04.558968Z"}},"outputs":[],"execution_count":null}]}