Benchmark reproductible de la généralisation, de l'efficacité-label et de l'équité des foundation models en radiologie
Comparer plusieurs encodeurs de vision gelés sur trois axes scientifiques — généralisation cross-dataset, efficacité-label, équité démographique — de façon rigoureuse et reproductible, sur un seul GPU (RTX 4080 Super 16 Go).
Warning
- Projet de recherche en cours : M0 (infra) et M1 (données) terminés ; M3 en cours (extraction d'embeddings + linear probing). Premiers résultats in-distribution imminents.
- Usage recherche uniquement — ce n'est pas un dispositif médical.
- Pourquoi RadGap
- Contributions scientifiques
- Model zoo
- Datasets
- Installation
- Démarrage rapide
- Reproduire un résultat
- Structure du dépôt
- Méthodologie & rigueur
- Résultats
- Éthique, licences & données
- Feuille de route
- Citation
- Remerciements
L'état de l'art 2026 en radiographie thoracique repose sur des Vision Transformers pré-entraînés en self-supervised (foundation models), adaptés en aval par des têtes légères. On n'entraîne plus de réseau from scratch. Les vrais problèmes ouverts ne sont pas la précision brute mais :
- La généralisation — l'effondrement des performances hors de la distribution d'entraînement.
- L'efficacité-label — le coût des annotations radiologiques.
- L'équité — des biais démographiques documentés.
RadGap mesure ces trois axes de façon comparative, statistiquement rigoureuse et reproductible, en gardant chaque backbone sur un pied d'égalité : on gèle l'encodeur, on pré-calcule et cache les embeddings, puis on entraîne des têtes légères en quelques minutes.
Ce que RadGap n'est pas : une course au leaderboard (+0,5 % d'AUROC), un pré-entraînement from scratch, un produit clinique, ou de la génération de compte-rendu. Voir les non-goals dans
CLAUDE.md.
| ID | Contribution | Métrique clé | Jalon |
|---|---|---|---|
| C1 | Generalization gap cross-dataset | Δ AUROC (in-distribution → out-of-distribution) | M4 ★ |
| C2 | Efficacité-label | Courbes AUROC @ {1 %, 10 %, 100 %} labels × backbone | M5 |
| C3 | Audit d'équité | Écarts AUROC / TPR par sexe, âge, race | M6 |
| C4 | (stretch) Transfert thorax → os (MURA) + LoRA | AUROC transfert vs in-domain ; gain LoRA vs coût | M7 |
Tous les backbones sont évalués gelés sous le même protocole.
| Backbone | Type | Source | Dim | Rôle |
|---|---|---|---|---|
| RAD-DINO | ViT-B/14 DINOv2 (CXR) | microsoft/rad-dino |
768 | Foundation model principal |
| RAD-DINO-MAIRA-2 | RAD-DINO + données | microsoft/rad-dino-maira-2 |
768 | Variante (plus de données) |
| BiomedCLIP | CLIP biomédical | microsoft/BiomedCLIP-PubMedBERT_256-vit_base_patch16_224 |
512 | Baseline vision-langage |
| DINOv2-ImageNet | ViT-B/14 naturel | facebook/dinov2-base |
768 | Contrôle « domaine général » |
| DenseNet-121 (CheXpert) | CNN supervisé | torchxrayvision |
— | Baseline supervisée historique |
| Medical MAE | ViT-B/16 MAE médical | lambert-x/medical_mae |
768 | Baseline SSL alternative |
| Dataset | Modalité | Rôle | Accès | Licence |
|---|---|---|---|---|
| CheXpert Plus | Radio thorax + texte + démo. | Train principal (C1) + équité (C3) | Stanford AIMI | Research Use Agreement |
| MURA | Radio os | Transfert (C4) | Stanford AIMI | Research Use Agreement |
| NIH ChestX-ray14 | Radio thorax | Test OOD (C1) | Kaggle | CC0-ish |
| PadChest | Radio thorax | Test OOD (C1) | BIMCV | Research |
| VinDr-CXR | Radio thorax | Test OOD (C1) | PhysioNet | Credentialed |
| MIMIC-CXR | Radio thorax + texte | Test OOD / VLM | PhysioNet | Credentialed |
Aucune donnée n'est versionnée dans ce dépôt (licences recherche). On commit les scripts de téléchargement, jamais les pixels. CheXpert Plus subsume CheXpert (non proposé seul par Stanford AIMI) et fournit la démographie pour C3.
Note
Limite d'egress Redivis (100 Go / 30 j). CheXpert Plus PNG complet (~720 Go en 5 archives) ne peut pas être exporté en une fois. Le développement se fait sur un sous-ensemble disponible (manifest available.parquet, filtré sur les images présentes via scripts/build_available_manifest.py). Le dataset complet sera récupéré par un export redimensionné (resize 518² sur Redivis → ~15-67 Go, sous la limite) — c'est de toute façon l'entrée des foundation models. NIH (Kaggle) n'est pas concerné.
Prérequis : uv, un GPU NVIDIA avec CUDA 12.x (testé sur RTX 4080 Super, driver 591.86, WSL2).
git clone [email protected]:berch-t/radgap.git
cd radgap
uv python install 3.11
uv sync # crée .venv avec torch 2.6 (cu124)
cp env.example .env # renseigner RADGAP_DATA_ROOT + AIMI_API_KEY
uv run python scripts/check_env.py # vérifie GPU + forward AMPcheck_env.py doit afficher le GPU, CUDA dispo : True, et Forward AMP : OK.
uv run pytest -q # suite de tests (33)
uv run ruff check . # lint
# Hydra : tout est paramétré, rien en dur. Exemple (M3, embeddings déjà extraits) :
uv run python scripts/train_probe.py backbone=rad_dino experiment.head=linear
uv run python scripts/train_probe.py -m backbone=rad_dino,dinov2 # sweep multi-backboneLe pipeline complet (disponible au fil des jalons) régénère chaque table et figure depuis zéro :
download → manifest unifié → (manifest disponible) → extract embeddings (cache disque) → train têtes → evaluate → figures
uv run python scripts/download_aimi.py --dataset chexpert_plus --variant png_compressed # M1 (Redivis ; archives auto-extraites)
uv run python scripts/download_aimi.py --dataset mura # M1
uv run python scripts/download_nih.py # M1 (Kaggle, OOD ; cache → data root)
uv run python scripts/build_manifest.py # M1 → manifests/unified.parquet
uv run python scripts/validate_manifests.py # M1 (0 fuite patient, labels valides)
uv run python scripts/build_available_manifest.py # M1 → manifests/available.parquet (images présentes)
uv run python scripts/extract_embeddings.py -m backbone=rad_dino,dinov2 # M3 → embeddings/<backbone>/… (cache disque)
uv run python scripts/train_probe.py -m backbone=rad_dino,dinov2 # M3 → experiments/results/auroc_in_distribution.csvChaque table se régénère à partir d'un CSV versionné dans experiments/results/ — exigence de reproductibilité.
Notebooks académiques commentés au format percent / jupytext : les .py sont la source versionnée, les .ipynb appairés sont fournis (édite l'un ou l'autre, ils restent synchro).
uv run jupytext --to notebook notebooks/*.py # (re)générer les .ipynb si besoin
uv run jupyter lab notebooks/ # via Cursor Remote-WSL : kernel « Python 3 (radgap) »| Notebook | Phase | Jalon |
|---|---|---|
01_eda_and_label_harmonization |
EDA + harmonisation des labels | M1 |
02_preprocessing_and_augmentation |
Préprocessing radiologique CXR-safe | M3 |
03_embeddings_and_linear_probing |
Embeddings gelés + têtes légères | M3 |
04_cross_dataset_generalization_gap |
Generalization gap (C1 ★) | M4 |
05_label_efficiency |
Courbes d'efficacité-label (C2) | M5 |
06_fairness_audit |
Audit d'équité démographique (C3) | M6 |
radgap/
├── CLAUDE.md PLAN.md README.md # contexte IA · avancement · doc publique
├── pyproject.toml .python-version # déps pinnées (uv) · Python 3.11
├── env.example # template d'env (jamais de secret committé)
├── configs/ # Hydra : config.yaml + dataset/ backbone/ experiment/
├── src/radgap/ # package : data · preprocessing · models · eval · utils
├── scripts/ # CLI : download · extract · train · evaluate · figures
├── experiments/ # sorties : embeddings (gitignored), résultats CSV, figures
├── notebooks/ # 6 notebooks académiques (.py percent / jupytext), 1 par phase
├── tests/ # pytest (CI)
└── .claude/skills/ # 5 skills : data · preprocessing · adaptation · eval · repro
Règle : la logique vit dans src/, la configuration dans configs/, les points d'entrée dans scripts/, les sorties dans experiments/.
- Protocole d'éval figé avant de regarder les résultats (pas de p-hacking).
- Chaque AUROC est livré avec un IC 95 % (bootstrap). Pas de point estimate nu.
- Comparaisons de modèles par test de DeLong, pas à l'œil.
- Multi-label = métriques par pathologie d'abord, puis agrégation macro.
- Split par patient (jamais par image), masquage des labels non annotés (NaN ≠ négatif).
- Backbones gelés + embeddings cachés : protocole identique pour tous, itération rapide.
- Runs pilotés par config + seed, hash de commit loggé, multi-seed pour les barres d'erreur.
Détails dans les skills .claude/skills/ (medical-ml-evaluation, reproducible-ml-research, etc.).
Premiers résultats M3 (préliminaires) — linear probe gelé sur un subset de CheXpert Plus (49 091 images, limite d'egress Redivis ; cf. note plus haut). Macro-AUROC sur 9 pathologies évaluables (« No Finding » non évaluable : une seule classe au test CheXpert). IC 95 % bootstrap (1000 rééch.). Générés par
scripts/train_probe.py→experiments/results/auroc_in_distribution.csv.
| Backbone | Macro-AUROC in-dist (CheXpert Plus, subset) | Δ AUROC OOD (NIH / PadChest / VinDr) |
|---|---|---|
| RAD-DINO | 0.819 (0.802–0.833) | M4 |
| DINOv2-ImageNet | 0.792 (0.777–0.806) | M4 |
| BiomedCLIP | à venir | M4 |
| DenseNet-121 (baseline) | M2 | M4 |
Aperçu de la question C1 : à protocole identique (backbones gelés), RAD-DINO devance DINOv2-ImageNet sur les 9 pathologies évaluables (+2,7 pts macro ; +5,8 sur cardiomégalie et pneumothorax) — le pré-entraînement médical aide en in-distribution. Confirmation par test de DeLong à venir. Question centrale (C1, M4) : les foundation models médicaux présentent-ils un generalization gap plus faible que les baselines ImageNet/supervisées ?
Tables M4 (generalization gap, C1), M5 (efficacité-label, C2), M6 (équité, C3) à venir aux jalons correspondants.
- Code : licence MIT (voir
LICENSE). - Données & poids de modèles : conservent leurs propres licences recherche non-commerciales (Stanford Research Use Agreement, PhysioNet credentialing + CITI). Rien n'est redistribué ici.
- Équité : les écarts démographiques sont reportés de façon descriptive, sans claim causal, avec tailles de sous-groupes et limites explicites (intersectionnalité non modélisée).
- Model card + datasheet fournis au jalon M8.
| Jalon | Objectif | Statut |
|---|---|---|
| M0 | Bootstrap & infrastructure | ✅ |
| M1 | Acquisition & harmonisation des données | ✅ |
| M2 | Baseline supervisée (DenseNet-121) | ⬜ |
| M3 | Embeddings FMs + linear probes (in-dist) | 🟡 |
| M4 ★ | Generalization gap cross-dataset (C1) | ⬜ |
| M5 | Courbes d'efficacité-label (C2) | ⬜ |
| M6 | Audit d'équité (C3) | ⬜ |
| M7 | (stretch) Transfert thorax → os + LoRA (C4) | ⬜ |
| M8 | Rédaction, repo public, soutenance | ⬜ |
Détail complet et Definition of Done par jalon : PLAN.md.
@software{berchet_radgap_2026,
author = {Berchet, Thomas},
title = {RadGap: a reproducible benchmark of generalization, label-efficiency
and fairness for radiology foundation models},
year = {2026},
url = {https://github.com/berch-t/radgap}
}Appuyé sur RAD-DINO (Pérez-García et al., 2025), CheXpert / CheXpert Plus (Stanford AIMI, Chambon et al., 2024), DINOv2, BiomedCLIP, et torchxrayvision. Voir les références complètes dans CLAUDE.md §11.