Table of Contents
The RSNA Knee Abnormality Detection competition is a different animal from the tabular problems I usually work. The task sounds simple: predict 12 binary findings per knee MRI study (ACL tear, meniscus, effusion, fracture, and so on), scored by macro-averaged ROC AUC. The reality is harder:
- 4,407 studies, 24,371 series, roughly 820,000 DICOM slices, 100GB+.
- You do not download any of it. The data is mounted inside a Kaggle notebook and you train on Kaggle’s GPU.
- Only 58 of the 4,407 studies are expert-labeled. The rest come with a free-text radiology report, in a dozen languages.
So before writing a model, I wanted to answer one question: can I even reach the data, read it, and get a valid submission accepted? If that plumbing does not work, a fancy model is worthless.
Everything runs on Kaggle, not my machine
This is the part that surprised me the first time I did it. For a “Code Competition” like this one, you do not download the dataset. You write a notebook, push it to Kaggle, and it runs on their servers with the data mounted read-only at /kaggle/input. Your code trains on their GPU and writes submission.csv. Your own machine is just the text editor.
My RTX 3090 sat idle for this one. It could not hold 100GB anyway.
De-risking before building
I pushed two tiny kernels before touching a model.
The first just explored the layout: what files exist, what the 12 finding columns are named, how many studies are labeled, and what a single DICOM looks like. That confirmed the findings list, the 58-of-4,407 label scarcity, and that each slice is a 512x512 16-bit MR image with pixel values well outside the usual 0 to 255 range (so it needs percentile windowing, not a fixed scale).
It also taught me an expensive lesson: recursively listing all 820,000 DICOM files stalls the kernel. You have to index images from the provided CSVs instead of walking the directory tree.
The second kernel was a “smoke test” that ran the full shape of the real job with a deliberately dumb predictor: for every finding, just guess the training-set prevalence. No neural network at all. It read 8 slices per test study (to prove timed DICOM reading works), wrote a properly formatted submission, and I submitted it. It read 24 slices across the visible test studies in about 2 seconds and the leaderboard accepted the file.
That is the whole point. Once the smoke test scores, I know four things work: data access, timed DICOM reads, the exact submission format, and the submit flow. Writing the model is now low-risk instead of a leap of faith.
The conservative baseline
With the path proven, the baseline model is intentionally boring:
- Sample a few evenly-spaced slices per study and mean-pool them.
- Feed them to a pretrained EfficientNet-B0 with 12 sigmoid outputs, trained with binary cross-entropy on the 58 labeled studies.
- If anything fails, or a study can’t be read, fall back to the prevalence guess so the submission is always valid.
No 3D convolutions, no mining the report text, no test-time augmentation, no ensembles. Those are all real levers, and the report-text angle is almost certainly where the winning solutions will find their edge. But none of that matters until the simple version reliably finishes inside Kaggle’s GPU time limit and produces a legal submission.
One gotcha worth the whole post
Kaggle picks your GPU model from a metadata field called machine_shape, not the one you would guess (accelerator). Leave it unset and you silently get an older P100 that modern PyTorch refuses to run on, which produces a dead kernel with a confusing error. Set "machine_shape": "NvidiaTeslaT4" and you get a T4 that just works. I lost real time to this on an earlier competition, so it went straight into my notes.
Why do it this way
The instinct on a big, intimidating competition is to start with the impressive model. But the failure modes here are almost all plumbing: can’t reach the data, reads time out, submission format is wrong, kernel gets the wrong GPU. Proving the boring path first turns a scary project into a series of small, safe steps. The model is the fun part, but it is also the part least likely to be what stops you.
The code, the exploration and smoke kernels, and the unit tests are on GitHub.