Skip to content

Develop ssl diffusion v2 - #2922

Open
clessig wants to merge 126 commits into
developfrom
develop-ssl-diffusion-v2
Open

clessig wants to merge 126 commits into
developfrom
develop-ssl-diffusion-v2

Conversation

@clessig

@clessig clessig commented Oct 13, 2026

Copy link
Copy Markdown
Collaborator

Description

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
  • 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

Sophie Xhonneux and others added 30 commits February 18, 2026 19:11
We identified a bug whereby  context loss is only computed on the last
layer. This will be fixed in the next commits, but we need to save the
current state for reproducibility.
javak87 and others added 7 commits September 23, 2026 17:22
* optimze zarr reading

* add test

* rm tests/test_obs_get.py

---------

Co-authored-by: kasravi1 <j.kasravi@fz-jeulich.de>
Co-authored-by: Florian Scheidl <36441589+florianscheidl@users.noreply.github.com>
* vectorize hpy splits

* add test for vectorize hpy splits

* add test_hpy_splits_known_layout_hl1

* move comments to docstring

* Removed the equivalence tests

---------

Co-authored-by: kasravi1 <j.kasravi@fz-jeulich.de>
Co-authored-by: Florian Scheidl <36441589+florianscheidl@users.noreply.github.com>
…#2793)

* vectorize normalize_target_channels

* add test to vectorize normalize_target_channels

* Type _normalize/_denormalize stats as arrays and drop the unused dict branch

* Remove the dict-stats normalize test; production only passes arrays

---------

Co-authored-by: kasravi1 <j.kasravi@fz-jeulich.de>
Co-authored-by: Florian Scheidl <36441589+florianscheidl@users.noreply.github.com>
* implement stochastic churn (#2799)

(cherry picked from commit dc5d610)

* Fix load_decoder_chkpt silently loading no decoder under FSDP (#2800)

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.

Claude-Session: https://claude.ai/code/session_01NsFj2pVh4pxs2UxthxewWy

Co-authored-by: Claude Opus 5
(cherry picked from commit 980f640)

* Register MLP norm only in self.layers, not also as self.lnorm (#2817)

* Register MLP norm only in self.layers, not also as self.lnorm

MLP.__init__ bound one norm module to two attribute paths for non-DiT MLPs:

    self.lnorm = norm(dim_in, eps=norm_eps)
    if not is_dit:
        self.layers.append(self.lnorm)

state_dict() therefore emitted the same tensor twice, as "lnorm.*" and as
"layers.0.*". Consequences:

  - FSDP2 state_dict() reports duplicate parameter paths for the shared module,
    while named_modules() deduplicates and keeps only the canonical "layers.0"
    path, so the alias has no module to initialise.
  - Every checkpoint written this way carries two identical copies of each norm
    tensor. Loading such a checkpoint into de-aliased code reports the "lnorm.*"
    copies as unmatched -- harmless in itself, but it buries real mismatches in
    noise.

Register the norm only in self.layers. forward() already applies it through the
self.layers loop and special-cases AdaLayerNorm to pass ada_ln_aux, so behaviour
is unchanged. The DiT branch keeps self.lnorm: it is an AdaLNZero applied
explicitly in forward() and never appended to self.layers, so it was never
aliased.

Verified: a non-DiT MLP now exposes only layers.* in state_dict(), with and
without dim_aux; forward passes unchanged; ruff clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NsFj2pVh4pxs2UxthxewWy

* remove comments

* added short comment

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
(cherry picked from commit 455a76a)

* Add load_encoder_chkpt, and do not re-apply either overlay on continuation (#2821)

Two changes to the checkpoint-overlay machinery in model_interface.py.

1. load_encoder_chkpt -- the mirror of load_decoder_chkpt. Filters a
   checkpoint to `encoder.*` (stream embedders, ae_* engines, learned queries)
   and overlays it after the primary load, leaving the forecast engine and
   decoder from the primary checkpoint untouched. This allows pairing one run's
   forecast engine with a different run's encoder. The two selections are
   disjoint, so both overlays may be configured at once.

   The shared body of both loaders is factored into _load_part_from_checkpoint
   rather than duplicated. This is deliberate: the decoder overlay was
   originally written by copying only the *unsharded* half of load_model's
   `module.` prefix handling, so every sharded lookup missed and the run
   silently trained a randomly initialised decoder. One body means the two
   parts cannot drift apart that way again.

2. Neither overlay is re-applied when continuing a run. The primary load is an
   if/elif: a continuation loads from `run_id_contd` -- the run's own
   checkpoint, which already contains whatever training that part has had --
   and never consults `load_chkpt`. Re-overlaying afterwards reverted a
   decoder-finetune's decoder to the borrowed original on every chained-job
   restart, discarding the finetuning done since the previous restart. All 12
   decoder finetunes of an in-flight sweep were affected, 4-6 reverts each, and
   this is also the cause of their loss spikes at chain handoffs (2-3.4x,
   recovering over 300-1150 steps): the learning rate is continuous across
   restarts and optimizer momentum is fully restored, so neither explains them;
   the partial decoder revert does.

Known limitation: `run_id_contd` is not a train_continue signal -- inference
passes `--from-run-id` through it -- so both overlays are now also skipped
during inference. If the overlay path is needed at inference, gate on an
explicit `overlay_decoder` / `overlay_encoder` parameter instead of on
`run_id_contd`; note target_and_aux_utils.py is a third call site that also
passes a non-None `run_id_contd`.

Claude-Session: https://claude.ai/code/session_01NsFj2pVh4pxs2UxthxewWy

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
(cherry picked from commit 6d358af)

* shorten comments

* Address PR comments 1

* remove test file

* remove comment and duplicated line

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
@github-actions github-actions Bot added data Anything related to the datasets used in the project eval anything related to the model evaluation pipeline infra Issues related to infrastructure model Related to model training or definition (not generic infra) labels Oct 13, 2026
@shmh40
shmh40 self-requested a review October 14, 2026 07:34
clessig and others added 11 commits October 14, 2026 09:47
…2962)

* Introduce keyword arguments for MLP and cleanup if/else clauses

* Incorporated review comments. Upgraded diffusion conditioning class to value object for simpler access
* Intermediate state of refactoring. Writing separated from validation_io.py and plotting outsourced to dedicated scripts

* Some fixes and adding an explicit dimension for 'denoising_step' for diffusion dev

* Change dev outputs format from zarr to zip to save inodes

* Intermediate fixes

* Fixed separate decoder loading

* Implemented Christian's PR review suggestions and cleaned var names

* Incorporated 2nd round of comments. Strict separation of train and inference loggers
* Fix data loader RNG seed collisions across ranks, workers and stages

The seed was computed as
    rng_seed * (rank+1)*73 * (worker+1)*37 * (mini_epoch+13)*7
which depends only on the product (rank+1)*(worker+1)*(mini_epoch+13).
Different combinations therefore shared a seed, e.g. rank 0/worker 1 and
rank 1/worker 0 (with 8 ranks x 10 workers x 2 mini_epochs only 78 of 160
seeds were distinct). With num_workers=0 the rank did not enter the seed
at all, and train and validation samplers used the same seed.

Create the generator from np.random.SeedSequence with rng_seed, rank,
loader worker, mini_epoch, iteration count and stage as separate entropy
values, so that every combination yields an independent stream. The
iteration count covers persistent loader workers, whose mini_epoch is not
advanced.

* Update docstring

* Rename _make_rng to _reset_rng and clarify the reset counter

- Rename _make_rng() to _reset_rng() and move it next to reset()
- Rename _num_iters to _num_rng_resets: it counts _reset_rng() calls
  (passes over the dataset) in this process and keeps the seed unique per
  pass for persistent loader workers, whose mini_epoch is not advanced
- Shorten the _reset_rng() docstring

* rm diffusion config
* Add configurable noise embedding with log-spaced Fourier features

The DiT timestep embedding (frequencies 10000^(-k/half) in [1e-4, 1]) was designed
for integer timesteps in [0, 1000]; over the ~1-3 unit range of c_noise = ln(sigma)/4
~220/256 channels are constant and the embedding is effectively rank 2.

New `noise_embedding: {type, f_min, f_max, include_raw}` config block. `log_fourier`
uses log-spaced frequencies in [f_min, f_max] (fp32, computed in forward so meta-device
init is safe) plus the raw c_noise channel. Default `dit_legacy` is unchanged, so
existing checkpoints and configs are unaffected.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S3h3zrLh9S8VWCEi2M4h1T

* Warn when noise_embedding.type is unset and recommend log_fourier (8pi)

The default stays dit_legacy for now so existing checkpoints keep loading.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S3h3zrLh9S8VWCEi2M4h1T

* shorten comments + remove tests

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

data Anything related to the datasets used in the project eval anything related to the model evaluation pipeline infra Issues related to infrastructure model Related to model training or definition (not generic infra)

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

10 participants