RemiAJR/mva_medicalImages

1

stars

2

commits

Python

primary language

Apr 10, 2026

updated

README

MVA DLMI 2026 — Histopathology OOD Classification

Team BreakingGood — Rémi Al Ajroudi & Bruny Soutarson
Master MVA, ENS Paris-Saclay

Challenge

Binary classification of H&E histopathology patches with strong inter-center distribution shift (train: centers 0,3,4 — val: center 1 — test: unknown center).

Best public LB score: 0.98705 (UNI2-h + LoRA + EMA + HED augmentation + pseudo-labeling + 8× TTA)

Method summary

  1. Foundation model: UNI2-h (Mahmood Lab, ViT-G/14, ~684M params) — pretrained on 350K+ WSIs
  2. LoRA adaptation: rank 16, alpha 32, on Q/V attention projections (0.46% trainable params)
  3. Stain-aware augmentation: HED color jitter (σ=0.10) + ColorJitter + flips + 90° rotations + GaussianBlur
  4. EMA: Exponential Moving Average (decay 0.999) of LoRA + head weights
  5. Pseudo-labeling: FixMatch-style self-training on test set (confidence threshold 0.97/0.03)
  6. TTA: 8× test-time augmentation (flips + rotations), logit averaging

Repository structure

src/
├── data.py                    # H5Dataset (multi-worker safe), HED jitter, transforms
├── models.py                  # Foundation model loaders (Phikon-v2, UNI2-h, DINOv2-L, etc.), LoRA wrap, ClassifHead
├── train_lora.py              # DDP LoRA training (torchrun), EMA, pseudo-labels, train+val combined
├── infer_tta.py               # DDP TTA inference, generates float + int CSVs
├── ensemble.py                # Weighted average of multiple submission CSVs
├── generate_pseudo_labels.py  # Generate pseudo-label JSON from float predictions
└── to_int_submit.py           # Convert float CSV → int 0/1 for Kaggle
overnight_pipeline.sh          # Automated overnight pipeline (pseudo-labeling rounds)
overnight_extended.sh          # Extended pipeline (multi-seed, ensembles)

Reproduce our best submission

Requirements

pip install torch torchvision h5py timm transformers huggingface_hub peft scikit-learn accelerate einops

1. Download data

kaggle competitions download -c mva-dlmi-2026-histopathology-ood-classification
unzip mva-dlmi-2026-histopathology-ood-classification.zip -d data/

2. Train UNI2-h with LoRA

torchrun --nproc_per_node=4 src/train_lora.py \
    --model uni2h --train-h5 data/train.h5 --val-h5 data/val.h5 \
    --epochs 5 --batch-size 32 --lr 1e-4 --head-lr 1e-3 \
    --lora-r 16 --lora-alpha 32 --hed-sigma 0.10 --use-ema \
    --output-dir runs/uni2h_v1

3. Generate pseudo-labels + retrain

# Inference with current best
torchrun --nproc_per_node=4 src/infer_tta.py \
    --model uni2h --checkpoint runs/uni2h_v1/best \
    --test-h5 data/test.h5 --tta 8 --output submissions/uni2h_base.csv

# Generate pseudo-labels
python src/generate_pseudo_labels.py \
    --csv submissions/uni2h_base.csv --high 0.97 --low 0.03 \
    --output cache/pseudo_labels.json

# Retrain with pseudo-labels
torchrun --nproc_per_node=4 src/train_lora.py \
    --model uni2h --train-h5 data/train.h5 --val-h5 data/val.h5 --test-h5 data/test.h5 \
    --pseudo-labels cache/pseudo_labels.json \
    --epochs 4 --batch-size 32 --lr 1e-4 --hed-sigma 0.10 --use-ema \
    --output-dir runs/uni2h_pseudo

# Final inference
torchrun --nproc_per_node=4 src/infer_tta.py \
    --model uni2h --checkpoint runs/uni2h_pseudo/best \
    --test-h5 data/test.h5 --tta 8 --output submissions/final.csv

4. Submit

kaggle competitions submit -c mva-dlmi-2026-histopathology-ood-classification \
    -f submissions/final_INT.csv -m "UNI2-h LoRA + pseudo-labeling + TTA"

Hardware

  • 4× NVIDIA Tesla V100S-PCIE-32GB
  • Training time: ~20 min per model (5 epochs)
  • Inference time: ~10 min (85K test images, 8× TTA, 4 GPUs)

Contributors

RemiAJR

2 commits

RemiAJR/mva_medicalImages

1

stars

2

commits

Python

primary language

Apr 10, 2026

updated

README

MVA DLMI 2026 — Histopathology OOD Classification

Team BreakingGood — Rémi Al Ajroudi & Bruny Soutarson
Master MVA, ENS Paris-Saclay

Challenge

Binary classification of H&E histopathology patches with strong inter-center distribution shift (train: centers 0,3,4 — val: center 1 — test: unknown center).

Best public LB score: 0.98705 (UNI2-h + LoRA + EMA + HED augmentation + pseudo-labeling + 8× TTA)

Method summary

  1. Foundation model: UNI2-h (Mahmood Lab, ViT-G/14, ~684M params) — pretrained on 350K+ WSIs
  2. LoRA adaptation: rank 16, alpha 32, on Q/V attention projections (0.46% trainable params)
  3. Stain-aware augmentation: HED color jitter (σ=0.10) + ColorJitter + flips + 90° rotations + GaussianBlur
  4. EMA: Exponential Moving Average (decay 0.999) of LoRA + head weights
  5. Pseudo-labeling: FixMatch-style self-training on test set (confidence threshold 0.97/0.03)
  6. TTA: 8× test-time augmentation (flips + rotations), logit averaging

Repository structure

src/
├── data.py                    # H5Dataset (multi-worker safe), HED jitter, transforms
├── models.py                  # Foundation model loaders (Phikon-v2, UNI2-h, DINOv2-L, etc.), LoRA wrap, ClassifHead
├── train_lora.py              # DDP LoRA training (torchrun), EMA, pseudo-labels, train+val combined
├── infer_tta.py               # DDP TTA inference, generates float + int CSVs
├── ensemble.py                # Weighted average of multiple submission CSVs
├── generate_pseudo_labels.py  # Generate pseudo-label JSON from float predictions
└── to_int_submit.py           # Convert float CSV → int 0/1 for Kaggle
overnight_pipeline.sh          # Automated overnight pipeline (pseudo-labeling rounds)
overnight_extended.sh          # Extended pipeline (multi-seed, ensembles)

Reproduce our best submission

Requirements

pip install torch torchvision h5py timm transformers huggingface_hub peft scikit-learn accelerate einops

1. Download data

kaggle competitions download -c mva-dlmi-2026-histopathology-ood-classification
unzip mva-dlmi-2026-histopathology-ood-classification.zip -d data/

2. Train UNI2-h with LoRA

torchrun --nproc_per_node=4 src/train_lora.py \
    --model uni2h --train-h5 data/train.h5 --val-h5 data/val.h5 \
    --epochs 5 --batch-size 32 --lr 1e-4 --head-lr 1e-3 \
    --lora-r 16 --lora-alpha 32 --hed-sigma 0.10 --use-ema \
    --output-dir runs/uni2h_v1

3. Generate pseudo-labels + retrain

# Inference with current best
torchrun --nproc_per_node=4 src/infer_tta.py \
    --model uni2h --checkpoint runs/uni2h_v1/best \
    --test-h5 data/test.h5 --tta 8 --output submissions/uni2h_base.csv

# Generate pseudo-labels
python src/generate_pseudo_labels.py \
    --csv submissions/uni2h_base.csv --high 0.97 --low 0.03 \
    --output cache/pseudo_labels.json

# Retrain with pseudo-labels
torchrun --nproc_per_node=4 src/train_lora.py \
    --model uni2h --train-h5 data/train.h5 --val-h5 data/val.h5 --test-h5 data/test.h5 \
    --pseudo-labels cache/pseudo_labels.json \
    --epochs 4 --batch-size 32 --lr 1e-4 --hed-sigma 0.10 --use-ema \
    --output-dir runs/uni2h_pseudo

# Final inference
torchrun --nproc_per_node=4 src/infer_tta.py \
    --model uni2h --checkpoint runs/uni2h_pseudo/best \
    --test-h5 data/test.h5 --tta 8 --output submissions/final.csv

4. Submit

kaggle competitions submit -c mva-dlmi-2026-histopathology-ood-classification \
    -f submissions/final_INT.csv -m "UNI2-h LoRA + pseudo-labeling + TTA"

Hardware

  • 4× NVIDIA Tesla V100S-PCIE-32GB
  • Training time: ~20 min per model (5 epochs)
  • Inference time: ~10 min (85K test images, 8× TTA, 4 GPUs)

Contributors

RemiAJR

2 commits

Languages

Python

79.7%

Shell

20.3%