Skip to content

Fix load_decoder_chkpt silently loading no decoder under FSDP - #2800

Merged
MatKbauer merged 1 commit into
ecmwf:mk/jk/kt/dev-ssl-diff/omit-target-writingfrom
moritzhauschulz:mh/fix/load-decoder-chkpt-module-prefix
Sep 8, 2026
Merged

Fix load_decoder_chkpt silently loading no decoder under FSDP#2800
MatKbauer merged 1 commit into
ecmwf:mk/jk/kt/dev-ssl-diff/omit-target-writingfrom
moritzhauschulz:mh/fix/load-decoder-chkpt-module-prefix

Conversation

@moritzhauschulz

@moritzhauschulz moritzhauschulz commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Description

Decoder modules were not loaded correctly with FSDP – this PR checks for the prefix and normalizes the loaded module names. It also raises an error if the decoder is not loaded – i.e. no weights are loaded for the decoder.

Issue Number

Is this PR a draft? Mark it as draft.

Checklist before asking for review

  • I have performed a self-review of my code
  • My changes comply with basic sanity checks:
    • I have fixed formatting issues with ./scripts/actions.sh lint
    • I have run unit tests with ./scripts/actions.sh unit-test
    • I have documented my code and I have updated the docstrings.
    • I have added unit tests, if relevant
  • in progress, but runs I have tried my changes with data and code:
    • I have run the integration tests with ./scripts/actions.sh integration-test
    • (bigger changes) I have run a full training and I have written in the comment the run_id(s): launch-slurm.py --time 60
    • (bigger changes and experiments) I have shared a hegdedoc in the github issue with all the configurations and runs for this experiments
  • I have informed and aligned with people impacted by my change:
    • for config changes: the MatterMost channels and/or a design doc
    • for changes of dependencies: the MatterMost software development channel

FastEvaluation

  • I have updated the public documentation if necessary

load_model normalises the "module." state_dict prefix in both of its branches
(sharded and unsharded). load_decoder_from_checkpoint was written later and
copied only the unsharded half, so its sharded path -- taken whenever
with_ddp and with_fsdp, i.e. every decoder finetune here -- looked each
parameter up in model.state_dict() using the raw checkpoint key.

For a DDP-saved backbone (keys prefixed "module.") against an unprefixed
model state dict, every one of the 131 lookups missed, each was skipped
individually, and load_state_dict({}) succeeded. The run then trained a
randomly initialised decoder. Three things hid it: the per-parameter warning
fired once per rank (thousands of lines), the unexpected-keys report was
empty because nothing was loaded, and "Loaded 0 decoder tensors" was a single
INFO line.

Six completed decoder finetunes off cw6a4szu, plus one off nhv6tkln, trained
random decoders this way; the two that worked (new73gkw, oliqngmf backbones)
did so only because those checkpoints happen to be saved without the prefix.

- Extract _align_module_prefix / _strip_module_prefix and use them in all
  four places the prefix logic was duplicated, including the decoder
  overlay's sharded path, which is the actual fix.
- _strip_module_prefix removes only a leading prefix; the previous
  key.replace("module.", "") stripped the substring anywhere in the path.
- Raise instead of loading nothing: matching N tensors but loading 0 is now
  a RuntimeError, as is requesting an overlay from a checkpoint with no
  decoder. The condition is identical on every rank, so all ranks raise
  together rather than one hanging the others.
- Collapse the per-parameter skip warning into one rank-0 summary and report
  loaded/matched counts.

Verified against all four backbone checkpoints: the old raw lookup gives
0/131 for cw6a4szu and nhv6tkln against an unprefixed model (reproducing the
bug) and 99/99 for new73gkw and oliqngmf (reproducing why those worked); with
the fix all four match 100% against both key conventions.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NsFj2pVh4pxs2UxthxewWy
@moritzhauschulz
moritzhauschulz marked this pull request as ready for review September 7, 2026 21:33
@github-actions github-actions Bot added the model Related to model training or definition (not generic infra) label Sep 7, 2026
@clessig

clessig commented Sep 8, 2026 via email

Copy link
Copy Markdown
Collaborator

@MatKbauer
MatKbauer merged commit 980f640 into ecmwf:mk/jk/kt/dev-ssl-diff/omit-target-writing Sep 8, 2026
3 checks passed
@moritzhauschulz

moritzhauschulz commented Sep 8, 2026

Copy link
Copy Markdown
Contributor Author

@clessig @MatKbauer I think it might affect both diffusion and ssl branches, and potentially others. For example, the relevant decoder loading function exists here but the prefix is not aligned in the fsdp case. I am not sure which ssl branch is currently the main one.

In principle, any branch that has this commit will need the fix.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

model Related to model training or definition (not generic infra)

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

3 participants