๐ ๏ธ Benchmarking a new AL method
January 20, 2026 ยท View on GitHub
:scroll: A Framework for Evaluation of Active Learning in 3D Biomedical Segmentation
published at TMLR (08/2025). To reproduce results from this paper, check out the nnactive/benchmark branch.
โ paper link
:scroll: Finally Outshining the Random Baseline: A Simple and Effective Solution for Active Learning in 3D Biomedical Imaging
published at TMLR (01/2026). โ paper link
Citing This Work
If you use nnActive, please cite our paper
@article{luth2025nnactive,
title={nnActive: A Framework for Evaluation of Active Learning in 3D Biomedical Segmentation},
author={Carsten T. L{\"u}th and Jeremias Traub and Kim-Celine Kahl and Till J. Bungert and Lukas Klein and Lars Kr{\"a}mer and Paul F Jaeger and Fabian Isensee and Klaus Maier-Hein},
journal={Transactions on Machine Learning Research},
issn={2835-8856},
year={2025},
url={https://openreview.net/forum?id=AJAnmRLJjJ},
}
If you use ClaSP PE, please cite our follow-up paper
@article{luth2026finally,
title={Finally Outshining the Random Baseline: A Simple and Effective Solution for Active Learning in 3D Biomedical Imaging},
author={Carsten T. L{\"u}th and Jeremias Traub and Kim-Celine Kahl and Till J. Bungert and Lukas Klein and Lars Kr{\"a}mer and Paul F Jaeger and Klaus Maier-Hein and Fabian Isensee},
journal={Transactions on Machine Learning Research},
issn={2835-8856},
year={2026},
url={https://openreview.net/forum?id=UamXueEaYW},
}
Semantic segmentation is crucial for various biomedical applications, yet its reliance on large annotated datasets presents a significant bottleneck due to the high cost and specialized expertise required for manual labeling. Active Learning (AL) aims to mitigate this challenge by selectively querying the most informative samples, thereby reducing annotation effort. However, in the domain of 3D biomedical imaging, there remains no consensus on whether AL consistently outperforms Random sampling strategies. Current methodological assessment is hindered by the wide-spread occurrence of four pitfalls with respect to AL method evaluation. These are (1) restriction to too few datasets and annotation budgets, (2) training 2D models on 3D images and not incorporating partial annotations, (3) Random baseline not being adapted to the task, and (4) measuring annotation cost only in voxels. In this work, we introduce nnActive, an open-source AL framework that systematically overcomes the aforementioned pitfalls by (1) means of a large scale study evaluating 8 Query Methods on four biomedical imaging datasets and three label regimes, accompanied by four large-scale ablation studies, (2) extending the state-of-the-art 3D medical segmentation method nnU-Net by using partial annotations for training with 3D patch-based query selection, (3) proposing Foreground Aware Random sampling strategies tackling the foreground-background class imbalance commonly encountered in 3D medical images and (4) propose the foreground efficiency metric, which captures that the annotation cost for background- compared to foreground-regions is very low. We reveal the following key findings: (A) while all AL methods outperform standard Random sampling, none reliably surpasses an improved Foreground Aware Random sampling; (B) the benefits of AL depend on task specific parameters like number of classes and their locations; (C) Predictive Entropy is overall the best performing AL method, but likely requires the most annotation effort; (D) AL performance can be improved with more compute intensive design choices like longer training and smaller query sizes. As a holistic, open-source framework, nnActive has the potential to act as a catalyst for research and application of AL in 3D biomedical imaging.
Contents:
Scripts for nnActive installation, usage, and development.
Installation
Install with
# use Pytorch 2.4.0 and CUDA 12.4
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu124
pip install -e nnactive '.[dev]'
We recommend a swap memory size of โฅ128GB to avoid OOM issues.
Set up nnActive
Set up paths as follows:
export nnActive_raw=Path_to_raw_nnunet_data # contains base datasets are derived
export nnActive_data=Path_to_nnactive_data # contains data for Active Learning experiments
export nnActive_results=$nnActive_data # contains results from Active Learning experiments
export nnUNet_raw=$nnActive_raw/nnUNet_raw # base_datasets (ID)
export nnUNet_preprocessed=$nnActive_raw/nnUNet_preprocessed # base datasets (ID)
export nnUNet_results=Path_to_nnUnet_results # base datasets (ID)
Autocompletion
Usage:
nnactive setup_experiment --experiment <tab>
nnactive run_experiment --experiment <tab>
Set up on ZSH:
write completion file
nnactive -s zsh > $HOME/.local/bin/completions/_nnactive
Add to fpath
fpath+="$HOME/.local/bin/completions"
Set up on Oh-my-zsh:
write completion file
nnactive -s zsh > $HOME/.oh-my-zsh/completions/_nnactive
Add a new function to CLI
Add import to file which carries function in: nnactive/cli/__init__.py
Setting up the data
Each dataset needs to be in the standard nnU-Netv2 format. E.g. do so with the Medical Segmentation Decathlon Hippocampus Dataset Create Raw Data:
nnUNetv2_convert_MSD_dataset -i {Path-to}/Task04_Hippocampus
Inside of these paths the following files
$nnUNet_raw
โโโ Dataset004_Hippocampus
โ โโโ dataset.json
โ โโโ imagesTr
โ โโโ labelsTr
...
Now create the seperation into validation and training set for Active Learning.
nnactive init_create_val_split --dataset_id 4
$nnUNet_raw
โโโ Dataset004_Hippocampus
โ โโโ dataset.json
โ โโโ imagesTr
โ โโโ imagesVal
โ โโโ labelsTr
โ โโโ labelsVal
...
Now set everything into the spacing used for the nnU-Net configuration.
nnUNetv2_plan_and_preprocess -d 4 -c 3d_fullres -np 8
nnactive init_resample_from_id --dataset_id 4
$nnUNet_raw
โโโ Dataset004_Hippocampus
โ โโโ dataset.json
โ โโโ imagesTr #resampled data
โ โโโ imagesTr_original
โ โโโ imagesVal #resampled data
โ โโโ imagesVal_original
โ โโโ labelsTr #resampled data
โ โโโ labelsTr_original
โ โโโ labelsVal #resampled data
โ โโโ labelsVal_original
...
Creation of small derivative datasets
nnactive nnactive init_create_small_dataset ----base_dataset_id 4 --target_dataset_id 999
$nnUNet_raw
โโโ Dataset004_Hippocampus
โ โโโ dataset.json
โ โโโ imagesTr #resampled data
โ โโโ imagesTr_original
โ โโโ imagesVal #resampled data
โ โโโ imagesVal_original
โ โโโ labelsTr #resampled data
โ โโโ labelsTr_original
โ โโโ labelsVal #resampled data
โ โโโ labelsVal_original
โโโ Dataset999_Hippocampus_small
โ โโโ dataset.json
โ โโโ imagesTr #resampled data
โ โโโ imagesVal #resampled data
โ โโโ labelsTr #resampled data
โ โโโ labelsVal #resampled data
...
Active Learning Experiment
After the base dataset has been set up we create an experiment with the setup function
nnactive setup_experiment --experiment Hippocampus__patch-20_20_20__sb-random-label2-all-classes__sbs-40__qs-40__unc-random__seed-12345
This creates the following folders:
$nnActive_data
โโโ Dataset004_Hippocampus # base_dataset folder
โ โโโ nnUNet_preprocessed
โ โ โโโ Dataset000_Hippocampus__patch-20_20_20__sb-random-label2-all-classes__sbs-40__qs-40__unc-random__seed-12345
โ โ โ โโโ nnUNetPlans.json
โ โ โ โโโ gt_segmentations
โ โ โ โโโ nnUNetPlans_3d_fullres
โ โโโ nnUNet_raw
โ โ โโโ Dataset000_Hippocampus__patch-20_20_20__sb-random-label2-all-classes__sbs-40__qs-40__unc-random__seed-12345
โ โ โ โโโ loop_000.json # contains annotated patches
โ โ โ โโโ imagesTr -> $nnActive_raw/nnUNet_raw/Dataset004_Hippocampus/imagesTr
โ โ โ โโโ imagesTs -> $nnActive_raw/nnUNet_raw/Dataset004_Hippocampus/imagesTs
โ โ โ โโโ imagesVal -> $nnActive_raw/nnUNet_raw/Dataset004_Hippocampus/imagesVal
โ โ โ โโโ labelsTr # contains data with ignore label where only patches are annotated
โ โ โ โโโ labelsVal -> $nnActive_raw/nnUNet_raw/Dataset004_Hippocampus/labelsVal
โ โ โ โโโ labelsVal -> $nnActive_raw/nnUNet_raw/Dataset004_Hippocampus/labelsVal
โ ...
...
$nnActive_results
โโโ Dataset004_Hippocampus # base_dataset folder
โ โโโ nnActive_results
โ โ โโโ Dataset000_Hippocampus__patch-20_20_20__sb-random-label2-all-classes__sbs-40__qs-40__unc-random__seed-12345
โ โ โ โโโ config.json
โ โ โ โโโ loop_000 # these will be created for validation and performance etc.
After the experiment has been set up, it can now be executed.
nnactive run_experiment --experiment Hippocampus__patch-20_20_20__sb-random-label2-all-classes__sbs-40__qs-40__unc-random__seed-12345
Analysis
Visualizing Results
To get a detailed analysis with plots and computed metrics for each different setting the following command can be used:
nnactive analyze_experiments --base_path $nnActive_results --raw_path $nnActive_data --output_path {OUTPUT_PATH}
This results in the following folder structure.
{OUTPUT_PATH}
โโโ DatasetXXX_NAME
โ โโโ Setting 1
โ โ โโโ results
โ โ โโโ results_statistics
โ โ โ โโโ Class X Dice
...
โ โ โ โโโ Class Y Dice
โ โ โ โโโ Mean Dice
โ โ โโโ statistics
โ โโโ Setting 2
โ โโโ results
โ โโโ results_statistics
โ โ โโโ Class X Dice
...
โ โ โโโ Class Y Dice
โ โ โโโ Mean Dice
โ โโโ statistics
...
Visualizing Queries (Images)
nnactive visualize_queries_images --raw_folder $nnActive_data/DatasetXXX/nnUNet_raw/DatasetXYZ --img_folder $nnActive_raw/nnUNet_raw/ DatasetXXX --output_folder {OUTPUT_FOLDER}
Results in the following folder structure
{OUTPUT_FOLDER}/DatasetXYZ
โโโ loop_000
โ โโโ loop-00__id-00__img-ACDC_01541.png
โ โโโ loop-00__id-01__img-ACDC_00542.png
โ โโโ loop-00__id-02__img-ACDC_01504.png
โ โโโ loop-00__id-19__img-ACDC_00260.png
โโโ loop_001
...
โโโ loop_002
...
โโโ loop_009
...
โโโ overview-loop_000.png
โโโ overview-loop_001.png
...
โโโ overview-loop_002.png
โโโ overview-loop_XXX.png
Requirements
dataset.json in raw data
{
"channel_names": {
"0": "MRI"
},
"description": "Left and right hippocampus segmentation",
"file_ending": ".nii.gz",
"labels": {
"Anterior": 1,
"Posterior": 2,
"background": 0,
"ignore": 3
},
"licence": "CC-BY-SA 4.0",
"name": "Hippocampus-partanno",
"numTest": 130,
"numTraining": 260,
"reference": " Vanderbilt University Medical Center",
"relase": "1.0 04/05/2018",
"tensorImageSize": "3D",
"annotated_id" : 4
// id to annotated dataset
}
Additional labels path
Additional labels can be added in the addTr folder, these will then be written to the labelsTr folder.
Implementation checking overlap will be implemented in the future version.
Active Learning Integration
The annotated data for each loop is saved in the loop_XXX.json file situated in the respective nnUNet_raw folder for each experiment.
These files are used for creating the validation splits for training.
It is structured as follows:
{
"patches": [
{
"file": "hippocampus_361.nii.gz",
"coords": [
0,
0,
0
],
"size": "whole"
},
{
"file": "hippocampus_230.nii.gz",
"coords": [
0,
0,
0
],
"size": "whole"
},...
]
"metafile" : "xxx",
...
}
"patches" is used to save the annotated areas and in loop_XXX.json only the newly annotated areas are saved.
To recreate the dataset for loop_002.json needs to be aggregated with loop_001.json and loop_000.json.
Contributing
- Run
pre-commit installevery time you clone the repo - Turn on
pylintin your editor, if it shows errors:- Fix the error
- If it is a false positive or if you have a good reason to disagree in
this instance add
# pylint: disable=<msg>or# pylint: disable-next=<msg>(see message control and list of checkers) - If you think this error should never be reported add it to
pyproject.toml[tool.pylint] disable = [ <msg 1>, <msg 2>, ... ]
๐ Benchmark Results
We provide all benchmark results on huggingface ๐ค
To download the results for all experiments:
python assets/download_from_huggingface.py --help
๐ ๏ธ Benchmarking a new AL method
Add Query Strategy
Make use of the register_strategy function to register new methods. You can use the existing method implementations as templates, e.g. Expected Entropy:
from pathlib import Path
import torch
from nnactive.strategies.base_uncertainty import AbstractUncertainQueryMethod
from nnactive.strategies.registry import register_strategy
from nnactive.strategies.uncertainties import Probs
# Inherit from AbstractUncertainQueryMethod for QMs based on predicted
# class probability distributions.
@register_strategy("pred_entropy")
class ExpectedEntropy(AbstractUncertainQueryMethod):
# The core of the QM implementation
def get_uncertainty(
self, probs: list[Path] | torch.Tensor | Probs, device: torch.device
) -> torch.Tensor:
# Use the Probs (ProbsFromFiles) class (at strategies/uncertainties.py).
# This automatically combines probability maps from temporary files.
if not isinstance(probs, Probs):
probs = Probs.create(probs)
return probs.exp_entropy(probs, device=device)
Add Experiment Configurations
Register new experiments to make them available in the CLI.
from nnactive.experiments import register, make_hippocampus_config
register(
make_hippocampus_config, # our default Hippocampus configuration
seeds=list(i + 12345 for i in range(4)),
uncertainties=["my_qm"], # as stored in the strategydict
query_size=40, # medium label regime
query_steps=5, # 5 AL cycles
)
Run new Experiments
nnactive setup_experiment --experiment Hippocampus__patch-20_20_20__sb-random-label2-all-classes__sbs-40__qs-40__unc-my_qm__seed-12345
nnactive run_experiment --experiment Hippocampus__patch-20_20_20__sb-random-label2-all-classes__sbs-40__qs-40__unc-my_qm__seed-12345
Acknowledgements
ย ย ย ย