This repository provides a PyTorch-based framework for running mammography (and other medical imaging) experiments:
- Training / evaluation loops
- Dataset wrappers and utilities
- Configurable transforms
- Hydra-based config composition
git clone https://github.com/antoncomm/ClinicalTrustLab
cd ClinicalTrustLab
python -m venv .venv
source .venv/bin/activate
pip install -U pip
pip install -e .The repository also includes a Gradio-based interface.
gradio app.pyMore details in Add your dataset.
For a simple classification pipeline we expect a CSV annotation with at least two columns:
image— path to an image relative toimage_folderlabel— class name (after applyingclass_mapit must become a number)
Example:
image,label
patient_001/image1.dicom,BENIGN
patient_001/image2.dicom,MALIGNANTMore details in Configs.
A good starting point is the composed Hydra config:
From the repository root:
python tools/train_torch.py configs/examples/inbreast_efficientnet.yaml \
--engine gpu \
--cuda 0 \
--name test_run \
--workers 8 \
--seed 42Outputs are written to ./experiments/test_run/:
checkpoints/— per-epoch and best model weightslogs/— tracker logs (e.g. TensorBoard)graphs/— metric curves / plots
A “full” experiment config is a regular YAML dictionary. Key sections:
mode:trainortestmodel: what to create and how to load weightsdatasets: one or multiple datasets (combined viaBaseConcatDataset)transforms: Albumentations pipelinerun: batch size, epochs, optimizer, metrics, tracker, k-fold, etc.
The folder configs/ contains Hydra configuration groups that can be composed via defaults::
Hydra entrypoint in this repo:
configs/config.yaml— the base Hydra config
When running Hydra-based scripts, you can override any nested key:
python tools/get_mean_std.py run.engine_name=gpu run.cuda_visible_devices=0 run.batch_size=64To override lists / dicts, follow Hydra/OmegaConf syntax:
python tools/get_mean_std.py transforms.augmentations.Normalize.mean=[0.12,0.12,0.12]- Create a new top-level experiment YAML in
configs/, e.g.configs/my_experiment.yaml:
# configs/my_experiment.yaml
mode: train
defaults:
- model: efficientnet
- datasets:
- inbreast_cls
- transforms: simple_augs
- run: classification
- _self_
# You can override any composed values below:
run:
num_epochs: 10
batch_size: 32- Run a Hydra tool with this experiment config by overriding
config_namein the decorator (or create a dedicated script). In the current repo, tools are hardwired toconfig_name="config"(see@hydra.main(...)), so the simplest pattern is: add your experiment toconfigs/config.yamlas a default.
For example, in configs/config.yaml replace the default experiment:
defaults:
- _self_
- my_experiment
- override hydra/hydra_logging: disabled
- override hydra/job_logging: disabledNow tools will run with your experiment by default:
python tools/get_mean_std.py
python tools/grad_cam.pyBy default, the repository already contains convenient dataset classes that assume the following structure:
your_dataset/
data/
patient_1/
image1.dicom
image2.dicom
image3.dicom
image4.dicom
patient_2/
image1.dicom
image2.dicom
image3.dicom
image4.dicom
train.csv
valid.csv
test.csv
This layout is recommended because existing dataset classes can:
- read CSV annotations (
train.csv,valid.csv,test.csv) - build full image paths as
dataset_dir / data / <relative_path_from_csv> - apply shared preprocessing and collate logic
Yes — you can use any folder structure / annotation format, but then you will need to:
- implement your own
torch.utils.data.Dataset(or subclass our base classes) - implement the parsing/processing logic (paths, labels, metadata, etc.)
- (optionally) provide a custom
collate_fn
- Create a file, e.g.:
src/ctl/data/datasets/my_dataset.py - Inherit from a base class:
BaseDataset— full manual controlBaseClassificationDataset— CSV-based classificationBaseMammographyDataset— mammography-specific logic (e.g. windowing / mask crop)
- Implement at least:
__len____getitem__collate_fn- (if you do not use the provided
_setup_annotation)_setup_annotation
Important: setup_data() resolves dataset classes by name using globals() after
from ctl.data.datasets import *.
Therefore your dataset class must be imported in src/ctl/data/datasets/__init__.py.
Open src/ctl/data/datasets/__init__.py and add your import and class name:
__all__ = [
"MammographyClassification",
"BaseConcatDataset",
"BaseClassificationDataset",
"MyDataset",
]
from .my_dataset import MyDatasetCreate a file, e.g. configs/datasets/my_dataset.yaml:
my_dataset:
dataset: MyDataset
dataset_dir: /path/to/your_dataset
ann_files:
train: ${datasets.my_dataset.dataset_dir}/train.csv
valid: ${datasets.my_dataset.dataset_dir}/valid.csv
test: ${datasets.my_dataset.dataset_dir}/test.csv
kwargs: {}
train_kwargs: {}
valid_kwargs: {}
test_kwargs: {}
ignore_classes: []
class_map: {}Then include it in a Hydra experiment:
defaults:
- datasets:
- my_dataset- Create a module, e.g.:
src/ctl/models/classification/my_model.py - Implement a
torch.nn.Module.
initialize_model() calls getattr(ctl.models, config_model["model_name"]), so you must:
- Add an import to
src/ctl/models/__init__.py - Add the name to
__all__
Example:
__all__ = [
"EfficientNet",
"MyModel",
]
from .classification.efficientnet import EfficientNet
from .classification.my_model import MyModelCreate configs/model/my_model.yaml:
runner: SimpleClassificationRunner
model_name: MyModel
model_kwargs:
num_classes: 1
weights_path: null
thresholds:
cls: 0.5Now you can refer to the model via model: my_model in a Hydra experiment config.
You can enable training data corruption or noise additions by specifying the attack config group in your experiment.
# configs/my_experiment.yaml
mode: train
defaults:
- model: efficientnet
- datasets:
- inbreast_cls
- transforms: simple_augs
- attack: pgd # enable attack here
- run: classification
- _self_Available attacks are defined in configs/attack/.
Each attack has its own YAML file inside configs/attack/.
For example, configs/attack/pgd.yaml may contain:
type: PGDAttack
attack_runner: AttackMultiClassificationRunner
attack_params:
nb_classes: 2
max_iter: 5
eps: 0.05
eps_step: 0.01
verbose: False
is_color: TrueYou can modify these values directly in the YAML file.
For evasion attacks, you must use a compatible runner:
SimpleClassificationRunner— for standard single-label classificationAttackMultiClassificationRunner— for multi-label or specific attack-aware setups
This is defined in your model config in type field.
