Checkpointing¶
Distributed checkpoints via torch.distributed.checkpoint (DCP):
what’s saved, how resharding works, auto-resume rules, and
HuggingFace interchange.
At a glance¶
Every checkpoint lands in {config.checkpoint.dir}/step_{N}/ and
contains two kinds of state:
File(s) |
Contents |
Format |
|---|---|---|
DCP shards ( |
Model + optimizer state, one shard per rank |
|
|
step, tokens_seen, scheduler, per-rank dataloader/RNG state, extras (e.g. |
|
|
Human-readable |
Plain JSON, see below |
|
Points at the most recent |
metadata.json¶
Written by rank 0 beside train_state.pt, and readable with a plain json.load (no torch).
It holds step and tokens_seen, plus the run’s optimizer.weight_decay_scope as
weight_decay_scope when the checkpoint manager was built with it, as the training entry does
(VLM runs add vlm_freeze, and lr_multipliers with the lr_module_patterns of its
modules and the module of each optimizer param group, lr_group_modules, when
optimizer.lr_multipliers lists any). A load reads it on rank 0, once, for the checks of
those records. When a logging backend has a live run at save time it also holds a
tracking list, one entry per backend:
{
"step": 1000,
"tokens_seen": 4096000,
"tracking": [
{"backend": "wandb", "run_id": "<run id>", "experiment": "<project>",
"entity": "<entity>", "url": "<run url>"},
{"backend": "tensorboard", "log_dir": "tb_logs"},
{"backend": "mlflow", "run_id": "<run id>", "experiment": "<experiment>",
"tracking_uri": "<tracking uri>"}
]
}
Each backend describes its own run, as it knows it: the resolved tracking URI and the experiment name for MLflow, the project for wandb, the writer’s directory for TensorBoard.
entityandurlappear when the wandb run has them (an offline run has no URL). The MLflow entry always has all four fields shown. With checkpoint registration on it also hasmodel_name, and getsmodel_idonce the committed checkpoint is registered. Every write ofmetadata.jsonreplaces the file atomically, so a reader never sees a partial one.A backend contributes only while its run is live, and runs start on the first log. A checkpoint saved before that (step 0, or soon after a resume when
checkpoint.intervalis shorter thanmetrics.log_interval) lacks the entry, and the key is omitted when no backend is live.The training entry hands
CheckpointManager.set_tracking_sourcethe tracker’stracking_entries, whichsave()reads on rank 0, andset_checkpoint_registrarthe tracker itself (Registering checkpoints). A source that raises, or returns what JSON cannot hold, costs thetrackingkey with a warning, never the save; a registrar that fails costs only its fields or its registration.Values are recorded as resolved. Credentials embedded in the tracking URI (
scheme://user:token@host) are stripped at write time; authentication comes from the environment, a profile or OAuth, never from this file. The URI, experiment path and run ids remain, so drop thetrackingkey before sharing a checkpoint outside your team.The key is additive. Older checkpoints lack it, nothing on the resume path reads it, and run ids still resume from
train_state.pt. Offline tools read it:kempnerforge.metrics.mlflow.publishappends eval metrics to the MLflow run itsmlflowentry names.
Key modules¶
kempnerforge/checkpoint/manager.py—CheckpointManager.save()/load()/wait(),latestsymlink maintenance, retention cleanup.kempnerforge/checkpoint/async_save.py—AsyncCheckpointer: sync / async / pinned-memory modes.kempnerforge/checkpoint/state.py—build_train_state/restore_train_state, RNG capture.kempnerforge/resilience/elastic.py—resolve_resume_path()(checks thelatestsymlink and falls back to the neweststep_Nwhose save completed).scripts/convert_checkpoint.py—dcp-to-hfandhf-to-dcpCLI.
Read next¶
New reader: DCP model + optimizer → Train state.
Resuming a job: Auto-resume first, then Resharding if you’re changing GPU count.
Exporting for inference or HF checkpoints: HF conversion.
Config knobs: Configuration § CheckpointConfig (search for
interval,dyn_ckpt_window,async_mode,keep_last_n,load_path).