CompDiff: Hierarchical Compositional Diffusion for Fair and Zero-Shot Intersectional Medical Image Generation
CompDiff conditions Stable Diffusion on demographic attributes (age, sex, race/ethnicity) to generate fair, demographically-controllable synthetic medical images. Demographic attributes are removed from the text prompt and routed through a dedicated Hierarchical Conditioner Network (HCN): a typed compositional conditioner that encodes each attribute in its native type (sex and race as embeddings, age as a continuous value), composes them hierarchically, and presents them to the diffusion UNet as separate, separately supervised cross-attention tokens. The repository ships the CompDiff training code alongside the baselines used in the paper (standard fine-tuning, FairDiffusion, and an unstructured demographic encoder) for comparison.
Project site: mahmoudibrahim98.github.io/compdiff-site · Paper: arXiv:2603.16551 · Pretrained models: compdiff-chest-xray · compdiff-fundus
- Installation
- Quick start — generate images with our pretrained models
- Released models
- How it works
- Train your own models
- Results
- Citation
- Acknowledgments
git clone https://github.com/mahmoudibrahim98/CompDiff.git
cd CompDiff
conda create -n compdiff python=3.10
conda activate compdiff
pip install -r requirements.txtInstall PyTorch with CUDA from pytorch.org for GPU support, matching
your driver (a bare pip install torch may pull a CUDA build newer than your driver supports).
Image generation requires diffusers>=0.35 (already in requirements.txt).
The CompDiff checkpoints are released on the Hugging Face Hub. You do not need the
training code or any dataset to generate images — each model ships a turnkey
CompDiffPipeline that is downloaded automatically.
- Chest X-ray: mahmoudibra98/compdiff-chest-xray
- Fundus: mahmoudibra98/compdiff-fundus
generate.py (repo root) downloads the chosen model and generates images:
# Chest X-ray
python generate.py --modality chest \
--prompt "Cardiomegaly with small bilateral pleural effusions" \
--sex female --race White --age 67 \
--num_images 4 --output_dir generated
# Fundus
python generate.py --modality fundus \
--prompt "glaucoma, severe vision loss, abnormal cup-disc ratio, myopia" \
--sex male --race Asian --age 55 --seed 0Run python generate.py --help for all options (--num_inference_steps,
--guidance_scale, --negative_prompt, --dtype, --revision, etc.).
import torch
from huggingface_hub import snapshot_download
path = snapshot_download("mahmoudibra98/compdiff-chest-xray")
import sys; sys.path.insert(0, path)
from compdiff_pipeline import CompDiffPipeline
pipe = CompDiffPipeline.from_pretrained(path, device="cuda", dtype=torch.float16)
img = pipe.generate("Cardiomegaly with small bilateral pleural effusions",
sex="female", race="White", age=67)[0]
img.save("out.png")- Prompt: clinical findings only — do not write age, sex or race into the text. All three are passed as arguments and conditioned by the model.
- Sex:
0=male,1=female(strings likemale/femalealso accepted). - Race: chest —
0=White, 1=Black/African American, 2=Asian, 3=Hispanic/Latino; fundus —0=White, 1=Black/African American, 2=Asian(no Hispanic/Latino). - Age: a number of years. The chest model takes it as a continuous conditioner input (no binning); the current fundus release prepends it to the prompt (see Released models).
- Fundus findings vocabulary (comma-joined, in order): glaucoma status
(
glaucoma/non-glaucoma), vision loss (normal vision/mild/moderate/severe vision loss), optional cup-to-disc ratio, optional refraction (hyperopia/emmetropia/myopia). Free-form prompts are out-of-distribution.
Research use only — not a medical device. Do not use for diagnosis, screening, or clinical decision-making.
| Model | Hub revision | Conditioner | Age |
|---|---|---|---|
| Chest X-ray (current) | main |
typed 3-attribute HCN, 4 tokens (configs/compdiff/train_compdiff_chest.yaml) |
continuous, through the conditioner |
| Chest X-ray (first release, July 2026) | v1 |
sex × race HCN, 1 token (configs/hcn/train_hcn_age_from_promt.yaml) |
prepended to the prompt |
| Fundus (current) | main |
sex × race HCN, 1 token | prepended to the prompt |
The chest model on main is the checkpoint behind the current version of the paper. The
fundus Hub model will be updated to the typed 3-attribute conditioner in the same way; until
then it is the first release. Every release keeps the same generate(prompt, sex, race, age, ...)
interface, and generate.py --revision v1 selects the first chest release.
CompDiff fine-tunes Stable Diffusion 2.1-base (UNet and CLIP text encoder) and injects demographic attributes through a dedicated conditioner instead of the prompt. The methods below are all trained with the same pipeline and selected via config.
| Method | Description | Config |
|---|---|---|
| CompDiff | Typed compositional HCN: sex/race embeddings + continuous age → pairwise composition → four supervised tokens | configs/compdiff/train_compdiff_chest.yaml, configs/compdiff/train_compdiff_fundus.yaml |
| HCN, sex × race (first release) | Same hierarchy over sex and race only; age stays in the prompt; one fused token | configs/hcn/train_hcn_age_from_promt.yaml |
| Demographic Encoder | Flat MLP over the attributes, no hierarchy (ablation) | configs/FLAT/train_demographic_encoder.yaml |
| Baseline | Standard SD fine-tuning, demographics in the text prompt only | configs/baseline_SD/train_baseline.yaml |
| FairDiffusion | Adaptive per-sample loss re-weighting (Fair Bayesian Perturbation) | configs/fairdiffusion/train_baseline_fairdiffusion.yaml |
CompDiff conditioner (gen_source/compdiff2.py):
- Typed encoders. Sex and race are nominal embeddings; age enters as a continuous value mapped through sinusoidal features and an MLP, so nearby ages get nearby representations.
- Hierarchical composition. Pairwise MLPs compose age×sex, age×race and sex×race, a further MLP fuses the three, and each attribute is re-contextualised against the composed state. Every pairwise component of a never-observed intersection is supported by training data from other cells, which is what makes zero-shot intersections reachable.
- Per-attribute tokens. Four tokens (
t_age,t_sex,t_race,t_cls) are projected into the UNet cross-attention space and concatenated to the 77 CLIP text tokens ([B, 81, 1024]). Each token is supervised by its own auxiliary head during training (age regression, sex and race classification, joint-cell classification), so the tokens the UNet reads carry the attribute information. - Text pathway. Demographics are stripped from every prompt; the text encoder only ever sees clinical findings.
The steps below reproduce the paper from scratch on your own data. Skip this section if you only want to generate images from the released checkpoints (Quick start).
Obtain the source datasets and build WebDataset tar files.
Chest X-ray (MIMIC-CXR)
- Source: MIMIC-CXR Database v2.1.0 (PhysioNet).
- Access: Credentialed — PhysioNet account, required training (e.g. CITI), and data use agreement.
- Prepare with a split CSV (columns:
split,image,final_sentence, disease labels, demographics):
python prepare_datasets/prepare_chest_dataset.py \
--source_dir /path/to/your/mimic-cxr \
--output_dir /path/to/your/chest_webdataset \
--split_csv /path/to/your/split_data.csvFundus (FairGenMed)
- Source: FairGenMed dataset (Google Drive), see FairDiffusion.
- Access: Download from Drive; non-commercial research only (CC BY-NC-ND 4.0).
- Prepare (base dir has
Training/,Validation/,Test/anddata_summary.csv):
python prepare_datasets/prepare_fundus_dataset.py \
--fundus_base_dir /path/to/your/fairgenmed \
--output_dir /path/to/your/fundus_webdatasetUse --help on each script for options (--max_samples_per_tar, --splits, etc.).
Training prompts follow "<AGE> year old <RACE> <SEX>. <IMPRESSION>" for chest and
"SLO fundus image of a <RACE>, <SEX>, <AGE> years old patient with the following conditions: <CONDITIONS>"
for fundus; the data loader parses the demographics from the prompt and strips them from the
text when a conditioner is enabled.
Demo data — run the pipeline without the full datasets:
- Pre-built WebDatasets in
demo_chest/anddemo_fundus/. - Chest demo CSV
real_chest/split_data_demo.csv(10 rows) with placeholder images underchest_images_skeleton/(mirrors the MIMIC-CXR layout). Use--split_csv real_chest/split_data_demo.csv --source_dir chest_images_skeletonfor a minimal run.
Validation/metrics weights — the pretrained_models/ directory holds weights used
during training-time validation (not the released generators):
- Sex classifier:
pretrained_models/sex/resnet-all/epoch=13-step=7125.ckpt— demographic (sex) prediction on generated images. Configs point here by default. - FID / RadImageNet:
pretrained_models/fid_radnet/— used whencompute_fid_radimagenet: true(falls back totorch.hubif absent;RADIMAGENET_LOCAL_DIRoverrides the location). Seepretrained_models/README.md.
cd gen_source
# CompDiff (chest)
python train.py --config_file ../configs/compdiff/train_compdiff_chest.yaml
# CompDiff (fundus)
python train.py --config_file ../configs/compdiff/train_compdiff_fundus.yaml
# Baseline
python train.py --config_file ../configs/baseline_SD/train_baseline.yaml
# FairDiffusion
python train.py --config_file ../configs/fairdiffusion/train_baseline_fairdiffusion.yamlMulti-GPU with Accelerate (the chest model was trained on 6 GPUs at per-device batch 8, the fundus runs on 4 GPUs at per-device batch 24):
accelerate launch --num_processes=6 --multi_gpu --mixed_precision bf16 \
gen_source/train.py --config_file configs/compdiff/train_compdiff_chest.yamlSet dataloader_num_workers to the number of training shards to keep the GPUs fed; the
default of 0 decodes every sample on rank 0. Note that the WebDataset shuffle is not seeded
by seed, so runs are independent replicates rather than exact repeats.
accelerate launch --num_processes=8 --multi_gpu --mixed_precision bf16 \
gen_source/run_validation_monitor_debug.py \
--config_file configs/compdiff/train_compdiff_chest.yaml \
--check_interval 300Checkpoint selection in the paper is validation-based (chest: step 10,000; fundus: step 17,500).
accelerate launch --num_processes=6 --multi_gpu --mixed_precision bf16 \
gen_source/generate_synthetic_dataset.py \
--config_file configs/compdiff/train_compdiff_chest.yaml \
--checkpoint_path outputs/compdiff/chest/checkpoint-10000 \
--output_dir synthetic_datasets/output \
--merge_csvTrain and evaluate downstream classifiers (e.g. pathology) on real vs. synthetic chest data. Run from the repo root:
python downstream_eval_chest/train_downstream_classifier.py \
--strategy 1a \
--real_train_path demo_chest/training_data \
--real_val_path demo_chest/val_data \
--real_test_path demo_chest/test_data \
--output_dir outputs/downstream_evalSee downstream_eval_chest/README.md for strategies (1a/1b), CheXpert evaluation, and analysis scripts.
Main YAML options (see configs/ for full examples):
- CompDiff:
use_hcn: true,use_compdiff2: true,cd2_composer: hierarchical,cd2_multi_token: true,max_age: 100,hcn_num_sex: 2,hcn_num_race: 4(chest) or3(fundus),hcn_aux_weight: 1,strip_demographics_in_validation: true - HCN, sex × race (first release):
use_hcn: true,hcn_encode_age: false,keep_age_in_prompt: true - Demographic Encoder:
use_demographic_encoder: true,demo_mode: 'single',demo_aux_weight: 1.0 - FairDiffusion:
use_fairdiffusion: true,fairdiffusion_time_window: 30,fairdiffusion_exploitation_rate: 0.95
Mean ± SD across three independently trained runs per method (validation split), from the paper. Sampling: DDPM, 75 steps, classifier-free guidance 7.5, 512×512.
| Chest FID ↓ | Chest FID-RadImageNet ↓ | Fundus FID ↓ | Fundus glaucoma AUROC ↑ | Fundus cup-disc AUROC ↑ | |
|---|---|---|---|---|---|
| Baseline (SD fine-tune) | 88.4 ± 4.7 | 8.62 ± 0.51 | 72.7 ± 3.7 | 0.916 | 0.957 |
| FairDiffusion | 85.6 ± 1.7 | 8.85 ± 1.05 | 64.2 ± 1.8 | 0.930 | 0.904 |
| CompDiff | 74.7 ± 5.7 | 6.64 ± 0.77 | 60.1 ± 4.3 | 0.957 | 0.994 |
On two disjoint held-out splits of MIMIC-CXR, CompDiff synthesises demographic intersections absent from training and attains the lowest per-cell RadImageNet FID on 15 of 16 held-out cells against both baselines.
@article{ibrahim2026compdiff,
title = {CompDiff: Hierarchical Compositional Diffusion for Fair and Zero-Shot Intersectional Medical Image Generation},
author = {Ibrahim, Mahmoud and Elen, Bart and Sun, Chang and Ertaylan, Gokhan and Dumontier, Michel},
journal = {arXiv preprint arXiv:2603.16551},
year = {2026},
url = {https://arxiv.org/abs/2603.16551}
}This codebase builds on the Hugging Face Diffusers library.
- RoentGen-v2 (Stanford MIMI): Improving Performance, Robustness, and Fairness of Radiographic AI Models with Finely-Controllable Synthetic Data — chest X-ray generation and baseline.
- FairDiffusion (Harvard Ophthalmology AI Lab): FairDiffusion: Enhancing Equity in Latent Diffusion Models via Fair Bayesian Perturbation (Science Advances) — fairness-aware training and FairGenMed dataset.


