claimflow-api / ml_training /cards /modality-classifier.md
Minifigures's picture
feat: ClaimFlow API demo backend
ceea9e1 verified
|
Raw
History Blame Contribute Delete
2.87 kB
# Model card — modality classifier
**Model.** EfficientNet-B0 (timm, ImageNet init, standard 3-channel stem; grayscale
inputs replicated 1→3ch in the transform), 3-class head: `ct` / `mri` / `xray`.
Weights: `backend/weights/modality_efficientnet_b0.pt` + `modality_config.json`
(classes, input size, normalization, fitted temperature). Served by
`backend/app/ml/imaging/real.py` under `MODEL_BACKEND=real`.
## Intended use
Routing and cross-checking only: classify the modality of a claimant-uploaded
medical image so the diagnostic-report draft addresses the right study type, and
flag disagreement with the DICOM Modality tag as a fraud signal. The confidence
that gates mandatory human review is the temperature-scaled probability.
**Not** a diagnostic device. No clinical claim is made or implied; every output
is reviewed by an imaging specialist before anything downstream happens.
## Training data
15,000 images from ROCOv2 (`eltorio/ROCOv2-radiology`), 5,000 per class, labels
derived from UMLS CUI tags; only rows matching exactly one modality CUI are kept
(`ml_training/datasets/build_datasets.py`). Single-source by design: drawing all
three classes from one corpus avoids the per-class source confound (resolution,
scanner, compression signatures) that turns stitched-together datasets into
accidental source detectors. Train recipe adds random JPEG-quality re-encoding
(q 60–95) and occasional Gaussian blur on top of geometric augs to kill the
remaining compression/sharpness shortcuts. Split 80/10/10, seed 42.
## Metrics (held-out test split)
| Metric | Value |
|---|---|
| Accuracy | 0.942 (n=1,543) |
| Macro-F1 | 0.943 |
| Per-class precision/recall | ct 0.92/0.93 · mri 0.91/0.92 · xray 1.00/0.98 |
| ECE before → after temperature scaling | 0.030 → 0.016 |
| Fitted temperature | 1.90 |
Most confusion is ct↔mri (76 of the 90 errors); xray is near-perfect. Trained
2026-06-10, 12 epochs, seed 42, best epoch selected by val macro-F1 (0.938 at
epoch 8).
Reproduce: `uv run python -m ml_training.evaluate --weights-dir weights/
--data-dir ml_training/data --report` (full report at
`ml_training/data/eval_report.json`, not committed).
## Caveats and limitations
- **Domain shift is unmeasured.** ROCOv2 is publication-figure radiology; real
claimant uploads (phone photos of films, portal exports, cropped screenshots)
look different. The confound-killer augs narrow this gap but do not close it.
- Three classes only; ultrasound, PET, mammography and anything else will be
forced into the nearest of ct/mri/xray — the calibrated confidence (and the
mandatory-review gate it feeds) is the only guardrail.
- Calibration was fitted on the validation split of the same corpus; the
temperature is not guaranteed to transfer under domain shift.
- CUI-derived labels are weak labels; ROCOv2 label noise propagates.