Logging in nnU-Net v2
March 24, 2026 ยท View on GitHub
Prefer the migrated reference page for the recommended path:
Introduction
Logging in nnU-Net is intentionally simple and centralized in
nnunetv2/training/logging/nnunet_logger.py.
The trainer talks to one object, MetaLogger, and MetaLogger fans out logs to:
LocalLogger(always enabled): the source of truth for training curves, checkpoint logging state, andprogress.png- optional external loggers (currently
WandbLogger)
This keeps training code clean while still allowing external tracking backends.
Default behaviour
Without any setup, nnU-Net uses only LocalLogger.
Per epoch, it stores:
mean_fg_diceandema_fg_dice(EMA is computed automatically)dice_per_class_or_regiontrain_losses,val_losseslrsepoch_start_timestamps,epoch_end_timestamps
From these values, progress.png is updated in the fold output folder.
On checkpoint save/load, the local logging state is also saved/restored, so curves continue correctly after resume.
How to enable W&B
- Install W&B:
pip install wandb
- Enable the backend via environment variables:
export nnUNet_wandb_enabled=1
export nnUNet_wandb_project=nnunet
export nnUNet_wandb_mode=online # or offline
- Run training normally:
nnUNetv2_train DATASET_NAME_OR_ID 3d_fullres 0
Notes:
nnUNet_wandb_enabledaccepts0/1andfalse/true(case-insensitive). Other values raise an error.- When resuming (
--c), W&B resume metadata infold_x/wandb/latest-runis reused and duplicate older steps are skipped.
How to integrate a custom logger
Add a new logger class with the same minimal interface used by MetaLogger:
update_config(self, config: dict)log(self, key, value, step: int)log_summary(self, key, value)
Example skeleton:
class MyLogger:
def __init__(self, output_folder, resume):
self.output_folder = output_folder
self.resume = resume
def update_config(self, config: dict):
...
def log(self, key, value, step: int):
...
def log_summary(self, key, value):
...
Then register it in MetaLogger.__init__ (for example behind an env var switch), similar to how WandbLogger is added.
Important integration detail:
MetaLogger.log(...)always writes toLocalLoggerfirst.- If you introduce a brand-new per-epoch key, also add that key to
LocalLogger.my_fantastic_logging, otherwise the local assertion will fail.