Checkpoint Pipeline Configuration
This is the everyday guide to telling Anemoi which trained model to start from, and how. You do not need to be a software engineer to use it: most of the time you copy one of the recipes below, change a path or a run id, and you are done.
If you only read one thing, read “I just want to…” recipes.
What is a checkpoint?
A checkpoint is a saved snapshot of a model taken during training. It is a
single file (often called last.ckpt or similar) that contains:
the weights — the numbers the model has learned (this is “the model”),
the optimizer state — the bookkeeping the training algorithm uses to keep improving smoothly (think: momentum, running averages),
the training progress — which epoch and step the run had reached,
some metadata — e.g. which variables the model was trained on.
Starting a new training run from a checkpoint is extremely common. You might want to:
pick up an interrupted run exactly where it stopped,
fine-tune a published model on your own data,
reuse a model’s weights but start the training clock from zero,
freeze part of the model so it does not change while you train the rest.
The checkpoint pipeline is the one place where you describe what you want, and Anemoi does the rest.
The pipeline in one picture
Think of loading a checkpoint as a short assembly line with three stations. A checkpoint travels left to right; each station is optional.
┌─────────────┐ ┌──────────────┐ ┌───────────────┐
│ 1. SOURCE │ ──▶ │ 2. LOADING │ ──▶ │ 3. MODIFIERS │
│ get the │ │ apply it │ │ adjust the │
│ checkpoint │ │ to the model│ │ model │
└─────────────┘ └──────────────┘ └───────────────┘
where from? which parts? freeze? adapt?
(file, run, (weights only, (one or more,
S3, URL) warm start, …) in order)
Station 1 — Source: where the checkpoint comes from (a local file, a previous run, an S3 bucket, an HTTP URL).
Station 2 — Loading strategy: how the checkpoint is applied to your model (just the weights? a full resume? skip parts that do not fit?).
Station 3 — Modifiers: what to change about the model afterwards (e.g. freeze the encoder). This is a list — you can apply several, in order.
You configure these three stations under one place in your config:
training.checkpoint. You only declare the stations you want; Anemoi always
runs them in the fixed order source → loading → modifiers.
training:
checkpoint:
source: { ... } # OPTIONAL — exactly one
loading: { ... } # OPTIONAL — exactly one
modifiers: [ ... ] # OPTIONAL — a list, applied in order
If you leave training.checkpoint out entirely (or set it to null), you
get a fresh run with a randomly-initialised model — the normal “train from
scratch” behaviour.
“I just want to…” recipes
Copy a block, change the path/run id, and go. Each recipe shows the YAML; below each one is the equivalent command line using Hydra “group selection” (see Two ways to turn it on), which is often shorter.
Fine-tune from a pretrained checkpoint file
Load the published weights, then train with a fresh optimizer starting at epoch 0:
training:
checkpoint:
source:
_target_: anemoi.training.checkpoint.sources.local.LocalSource
path: /path/to/pretrained.ckpt
loading:
_target_: anemoi.training.checkpoint.loading.strategies.ColdStartLoader
strict: false
Resume an interrupted run (continue exactly where it stopped)
Restore weights and the optimizer/epoch state so training continues seamlessly. Use the run’s id:
training:
checkpoint:
source:
_target_: anemoi.training.checkpoint.sources.run.RunIdSource
run_id: a1b2c3d4-... # the run you want to resume
fork: false # false = resume the SAME run
loading:
_target_: anemoi.training.checkpoint.loading.strategies.WarmStartLoader
anemoi-training train \
training/checkpoint/source=run +training.checkpoint.source.run_id=a1b2c3d4-... \
training/checkpoint/loading=warm_start
Transfer-learn when the architecture changed a little
Load only the parts that still fit; skip layers that changed shape or no longer exist, instead of crashing:
training:
checkpoint:
source:
_target_: anemoi.training.checkpoint.sources.local.LocalSource
path: /path/to/pretrained.ckpt
loading:
_target_: anemoi.training.checkpoint.loading.strategies.TransferLearningLoader
skip_mismatched: true
Freeze the encoder while fine-tuning
Load the weights, then freeze part of the model so it stays fixed during training (add a modifier after any loading strategy):
training:
checkpoint:
source:
_target_: anemoi.training.checkpoint.sources.local.LocalSource
path: /path/to/pretrained.ckpt
loading:
_target_: anemoi.training.checkpoint.loading.strategies.ColdStartLoader
strict: false
modifiers:
- _target_: anemoi.training.checkpoint.modifiers.freezing.FreezingModifierStage
submodules_to_freeze: ["model.model.encoder.0"] # the encoder key from your model config
Load weights from S3 or a URL
training:
checkpoint:
source:
_target_: anemoi.training.checkpoint.sources.s3.S3Source
url: s3://my-bucket/path/to/model.ckpt
loading:
_target_: anemoi.training.checkpoint.loading.strategies.WeightsOnlyLoader
strict: false
Replace S3Source + url: s3://… with HTTPSource + url:
https://… to download over HTTP(S).
Note
Warm start (resume) is Lightning’s load. WarmStartLoader — or a
source with no loading block at all — tells the trainer that the
checkpoint is loaded once, by PyTorch Lightning’s ckpt_path mechanism at
trainer.fit(), which reads the checkpoint as a file on disk. Use
LocalSource or RunIdSource: they point at a file. S3Source and
HTTPSource delete their download straight after loading, so a resume
from them is rejected when the pipeline is built, with an error that names
LocalSource (see Who restores what (the warm-start ownership rule)).
Start a fresh run (no checkpoint)
Just leave training.checkpoint unset (the default). Nothing to configure.
The vocabulary (plain words)
Term |
What it means |
|---|---|
Checkpoint |
A saved snapshot file of a model (weights + optimizer + progress + metadata). |
Weights |
The learned numbers — effectively “the model”. Loading weights makes your model behave like the saved one. |
Optimizer state |
The training algorithm’s bookkeeping. You need it to continue training smoothly; you do not need it to just reuse a model’s knowledge. |
Epoch / step |
How far a run had progressed. Resuming keeps these; starting fresh resets them to 0. |
Source |
Station 1 — where the checkpoint is fetched from. |
Loading strategy |
Station 2 — how the checkpoint is applied to your model. |
Modifier |
Station 3 — a post-loading change to the model (e.g. freezing). |
Stage |
The general word for any one station (a source, a loading strategy, or a modifier). They are the building blocks the pipeline runs in order. |
|
A Hydra setting that names the exact Python class to use, e.g.
|
Run / run id |
A previous training run, identified by a unique id (from the experiment tracker, e.g. MLflow). |
Two ways to turn it on
There are two equivalent ways to fill the three stations. They produce the same result — pick whichever you find clearer.
Easy path — Hydra group selection
Every shipped training preset wires the three stations as opt-in groups
that default to null (no checkpoint = fresh run). Select a ready-made group
on the command line to switch a station on:
# Just load weights from a local checkpoint
anemoi-training train training/checkpoint/source=local \
+training.checkpoint.source.path=/path/to/model.ckpt \
training/checkpoint/loading=weights_only
# Resume a run, restoring optimizer + epoch
anemoi-training train \
training/checkpoint/source=run +training.checkpoint.source.run_id=<id> \
training/checkpoint/loading=warm_start
The available groups are:
Group selector |
Options |
|---|---|
|
|
|
|
|
|
Explicit path — write the blocks in YAML
Or write the three blocks directly into your config, as in every recipe above.
This is clearer when you want to set several parameters at once or keep the
whole pipeline in one file. Each block is a Hydra _target_ plus its
parameters.
Station 1 — Sources (where the checkpoint comes from)
A source fetches the checkpoint and hands it to the next station. Configure
exactly one under training.checkpoint.source.
Source |
Use it when… |
Key parameters |
|---|---|---|
|
the checkpoint is a file on disk you can point to |
|
|
you want to resume or fork a previous run by its id |
|
|
the checkpoint lives in an S3-compatible bucket |
|
|
the checkpoint is downloadable over HTTP(S) |
|
Important
There is no automatic detection of where a checkpoint lives. You choose
the source explicitly. For example, giving LocalSource an s3://…
string will not fetch from S3 — it will look for a local file literally
named that and fail. Pick the source that matches your checkpoint’s location.
Local files
training:
checkpoint:
source:
_target_: anemoi.training.checkpoint.sources.local.LocalSource
path: /path/to/checkpoint.ckpt
path understands ~ (your home directory) and follows symbolic links. If
the file is not there you get a clear CheckpointNotFoundError.
A relative path is resolved against the directory you launched the training
command from. The shipped config does not set hydra.job.chdir, so Hydra
leaves the working directory alone. Prefer an absolute path: a job script that
cds elsewhere before launching would otherwise look somewhere else.
Tip
path is optional. If you omit it, LocalSource uses a path that an
earlier step put on the pipeline (this is how RunIdSource works
internally). For everyday “load this file” use, set path.
Previous runs (resume or fork)
RunIdSource finds the checkpoint belonging to a previous run and loads it.
The single fork flag chooses what kind of continuation you want:
training:
checkpoint:
source:
_target_: anemoi.training.checkpoint.sources.run.RunIdSource
run_id: a1b2c3d4-...
fork: false # false = resume the SAME run; true = start a NEW run from these weights
fork: false(resume) — keep the same run identity. Combined withWarmStartLoader, training picks up exactly where it left off.fork: true(fork) — mint a new run that starts from the old run’s weights. Use this to branch an experiment without overwriting the original.
See Resume, fork, and warm start in detail for the full story, including how this replaces the
old run_id / fork_run_id settings.
Amazon S3 (and S3-compatible storage)
training:
checkpoint:
source:
_target_: anemoi.training.checkpoint.sources.s3.S3Source
url: s3://my-bucket/path/to/model.ckpt
loading:
_target_: anemoi.training.checkpoint.loading.strategies.WeightsOnlyLoader
strict: false
A remote source cannot resume a run, so it always needs a loading block.
Credentials and endpoint settings are read from your anemoi-utils settings
(~/.config/anemoi/settings.toml), not from the training config. This needs
the S3 extra installed (anemoi-training[s3] / anemoi-utils[s3]).
HTTP / HTTPS
training:
checkpoint:
source:
_target_: anemoi.training.checkpoint.sources.http.HTTPSource
url: https://host.example/path/to/checkpoint.ckpt
expected_checksum: null # optional sha256; if set, the download is verified
loading:
_target_: anemoi.training.checkpoint.loading.strategies.WeightsOnlyLoader
strict: false
As with S3, a loading block is required: HTTPSource cannot resume a run.
Parameter |
Default |
Meaning |
|---|---|---|
|
(required) |
The |
|
|
How many times to retry the download before giving up. |
|
|
Per-attempt timeout, in seconds. |
|
|
Optional SHA-256 checksum. If set, the downloaded file is verified and a mismatch raises an error. If left unset, integrity is not checked. |
Station 2 — Loading strategies (how it is applied)
A loading strategy decides how much of the checkpoint to apply. Configure
exactly one under training.checkpoint.loading.
Strategy |
Use it when… |
Optimizer / epoch |
|---|---|---|
|
you want the weights and nothing else, and another part of your setup owns training progress |
discarded; progress left untouched |
|
you want to fine-tune from pretrained weights as a brand-new run |
discarded; progress reset to 0 |
|
the architecture changed and some layers no longer fit |
discarded; only matching weights loaded |
|
you are resuming the same run and want it to continue seamlessly |
restored (by Lightning — see below) |
Weights-only
Loads the weights and explicitly throws away optimizer/scheduler state. It does not touch training-progress numbers (epoch, step) — that is left to whatever else is configured. It is the simplest building block, ideal when composing larger pipelines.
training:
checkpoint:
loading:
_target_: anemoi.training.checkpoint.loading.strategies.WeightsOnlyLoader
strict: false # false = tolerate extra/missing KEYS; true = require an exact key set
# strict says nothing about tensor SHAPES — a size mismatch raises either way.
# To load a checkpoint whose variable-dependent layers changed shape, add
# skip_mismatched: true, and set training.allow_variable_subset so the
# variable-order check accepts the reduction too.
For the common “fine-tune from pretrained” case, prefer cold start below, which is weights-only plus an explicit reset of the training clock.
Cold start
Fresh training from pretrained weights. It loads weights exactly like
weights-only, then resets epoch and global_step to 0 and records
where the weights came from (pretrained_from) so loggers and checkpoint
names can trace the lineage.
training:
checkpoint:
loading:
_target_: anemoi.training.checkpoint.loading.strategies.ColdStartLoader
strict: false
Transfer learning
Loads only the weights that are compatible with your current model — same name and same shape — and skips the rest, instead of failing. Useful when you changed the architecture (added variables, resized a layer) but still want to reuse what you can.
What transfers is whatever still fits. A change to num_channels reshapes
every linear weight, bias and layer norm in the encoder, processor and decoder,
so only the channel-free tensors survive: processor statistics, coordinate
buffers and the trainable node and edge tensors. The loader logs every skipped
parameter with its reason at WARNING and records them in the run metadata.
training:
checkpoint:
loading:
_target_: anemoi.training.checkpoint.loading.strategies.TransferLearningLoader
skip_mismatched: true
skip_mismatched: true(default) — skip layers whose shapes do not match, log them at WARNING, and report which were transferred vs. skipped in the run metadata.skip_mismatched: false— treat a shape mismatch as an error (CheckpointIncompatibleError) instead of skipping it.
The original checkpoint file is never modified; the filtering builds a new, in-memory subset. To also freeze the transferred layers, add a freezing modifier after this loader (Station 3).
Warm start
Resume an interrupted run so it continues exactly where it stopped. It takes
no parameters, and it is a marker: selecting it tells the trainer to let
PyTorch Lightning load the checkpoint. Leaving out the loading block
altogether means the same thing.
The model has to match the checkpoint exactly. Lightning loads the state dict
strictly, so a model with a different architecture (an extra decoder, a
resized layer) fails with Error(s) in loading state_dict naming the missing
or unexpected keys. To continue from such a checkpoint use weights_only
with skip_mismatched: true or transfer_learning instead; both start a
fresh optimiser.
training:
checkpoint:
loading:
_target_: anemoi.training.checkpoint.loading.strategies.WarmStartLoader
Who restores what (the warm-start ownership rule)
A checkpoint has exactly one loader.
On a resume (
WarmStartLoader, or noloadingblock) the loader is PyTorch Lightning:trainer.fit(ckpt_path=...)reads the file once and restores weights, optimizer, scheduler and the training clock (epoch/step) together. The pipeline’s part is to make the checkpoint reachable as a local file (the source’s resolve step, so onlyLocalSourceandRunIdSourcecan resume) and to run any modifiers. The compatibility steps — format migrations, the trainable-edge-permutation migration, the processor-statistics refresh — run inside the Lightning load hook, on the dict Lightning is about to load.With any other strategy the loader is the pipeline, at the moment the model is built: the strategy applies the weights (and the same compatibility steps) and training starts with a fresh optimizer at epoch 0.
ckpt_pathis withheld from Lightning so the file is not loaded a second time.
Internally each loader declares which case it is via a
restores_training_state flag that the pipeline builder and the checkpoint
runner both read. A resume with no source at all is rejected when the pipeline
is built: there is nothing for Lightning to read.
Station 3 — Modifiers (adjust the model after loading)
Modifiers change the model after the weights are loaded. Unlike the other two
stations, training.checkpoint.modifiers is a list — you can apply
several, and they run in the order you list them.
Freezing
FreezingModifierStage freezes named parts of the model so their weights stay
fixed during training (it sets requires_grad=False):
training:
checkpoint:
modifiers:
- _target_: anemoi.training.checkpoint.modifiers.freezing.FreezingModifierStage
submodules_to_freeze: ["model.model.encoder.0", "model.model.processor.proc.0"]
strict: false
Parameter |
Default |
Meaning |
|---|---|---|
|
(required) |
Full paths of the parts to freeze, in dot notation, from the
LightningModule. The pipeline hands the stage the LightningModule, and
the graph model that owns |
|
|
If |
Tip
To find the right names, print the submodules of the graph model
(print(dict(model.model.model.named_modules()).keys()), where the first
model is the LightningModule) and prefix each dotted path with
model.model..
Freezing is the only modifier shipped today. Adding more (adapters, LoRA, quantisation) is exactly the extensibility story in the next section.
Resume, fork, and warm start in detail
“Resume / fork / warm start” used to be a handful of separate, easy-to-confuse settings. They are now all expressed through the source + loading stations, which makes the intent explicit:
Goal |
Source |
Loading |
|---|---|---|
Continue the same run seamlessly |
|
|
Branch a new run from an old one’s weights, fresh optimizer |
|
|
Start from a specific checkpoint file |
|
any loading strategy |
Behind the scenes, choosing RunIdSource also sets the run identity the
experiment tracker and output paths expect (resume keeps the run id; fork mints
a new one), as the old run_id / fork_run_id settings did. You express
it in one place now.
Write your own stage (the powerful part)
Here is what makes the pipeline genuinely flexible: every station is open. The four sources, four loading strategies, and the freezing modifier that ship with Anemoi are not special — they are simply the built-in examples of three open “shapes”. You can write your own and drop it into any station, and it behaves exactly like a built-in.
Three things make this work, and they are worth knowing because they remove a lot of would-be confusion:
A stage is recognised by what it *is*, not where it lives or what it is named. The pipeline classifies a stage as a source, a loader, or a modifier by checking which base class it inherits from — not by its filename, class name, or package. A loader you write in your own project, called anything you like, is treated identically to
WarmStartLoaderas long as it subclasses the loading base class.You can supply a stage as config *or* as a ready-made object. In a normal training run you point
_target_at your class (config). In a script or a test you can hand the pipeline an already-built Python object. Both work.You do not have to “register” anything. There is a discovery helper (
ComponentCatalog) that lists the built-ins for convenience, but the pipeline never consults it to run. Nothing breaks if your class is not in it.
The minimum contract
Pick the base class for the station you are filling, write one
process method, change the context, and return it:
Station |
Subclass |
Your |
|---|---|---|
Source |
|
set |
Loading |
|
load weights into |
Modifier |
|
change |
A complete custom modifier
This modifier scales the weights of named submodules — something not built in.
It lives in your project, e.g. my_project/modifiers.py:
from anemoi.training.checkpoint.modifiers.base import ModelModifier
from anemoi.training.checkpoint import CheckpointContext
class WeightScalingModifier(ModelModifier):
"""Multiply the weights of the named submodules by a constant."""
def __init__(self, submodules: list[str], factor: float = 0.5) -> None:
self.submodules = submodules
self.factor = factor
def process(self, context: CheckpointContext) -> CheckpointContext:
for name in self.submodules:
module = context.model.get_submodule(name)
for param in module.parameters():
param.data.mul_(self.factor)
# Append (do not overwrite) so several modifiers can compose.
context.metadata.setdefault("modifiers_applied", []).append(
{"type": "weight_scaling", "submodules": self.submodules, "factor": self.factor}
)
return context
Use it exactly like a built-in — point _target_ at it:
training:
checkpoint:
source:
_target_: anemoi.training.checkpoint.sources.local.LocalSource
path: /path/to/pretrained.ckpt
loading:
_target_: anemoi.training.checkpoint.loading.strategies.WeightsOnlyLoader
strict: false
modifiers:
- _target_: my_project.modifiers.WeightScalingModifier
submodules: [decoder]
factor: 0.5
The same recipe works for a custom source (subclass CheckpointSource) or
custom loading strategy (subclass LoadingStrategy). Because the pipeline
identifies stages by their base class, your class slots into the correct station
and the ordering checks below protect it just like a built-in.
Tip
When writing a custom loader, reuse the shared “parity” helpers in
anemoi.training.checkpoint.loading.base (format migrations, processor
refresh, metadata preservation). The built-in strategies call them so that a
checkpoint loads identically whether through the pipeline or through
Lightning. See Checkpoint Pipeline (Developer Guide) for the developer-level details.
Note
A training run can only take stages as config (_target_ strings) —
Hydra cannot serialise a live Python object into the config tree. Handing the
pipeline a ready-made object is for scripts and tests, where you build the
pipeline yourself.
What the pipeline checks for you
You do not have to assemble the stations in the right order — when you use the config, Anemoi always builds them source → loading → modifiers. On top of that, the pipeline guards against mistakes. These are errors: they stop the run with a clear message.
Errors (raise CheckpointConfigError when the pipeline is built):
a source placed after a loader,
a modifier placed before a loader,
more than one loading strategy.
These only happen if you assemble a pipeline by hand in code; the config builder always produces a valid order.
A safety net at the end (raises CheckpointLoadError): if a source was
configured but the model’s weights were never actually loaded, the run refuses
to continue rather than silently training a random model.
The order check does not reject more than one source, or a missing station.
Tip
Seeing more detail helps when something is off. Turn on debug logging:
import logging
logging.getLogger("anemoi.training.checkpoint").setLevel(logging.DEBUG)
Migrating from the old settings
The older, separate settings have been removed. If your config still
contains one, the run stops immediately at config load with a message naming the
exact replacement (this happens even if the value is null, and even if you
disabled config validation). Here is the mapping:
Old setting (now removed) |
Modern replacement |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
Where to next
Checkpoint Pipeline (Developer Guide) — the Python API and how stages, the context, and the trainer fit together (for developers writing custom stages or using the pipeline in code).
Checkpoint Pipeline Troubleshooting — error-by-error solutions.