Add generator argument to forward for reproducible noise in stochastic mode - #202
Open
Syota-Sasaki (s-sasaki-earthsea-wizard) wants to merge 1 commit into
Conversation
Support passing a torch.Generator, or a tuple with one generator per batch element, to Aurora.forward, Swin3DTransformerBackbone.forward, and rollout, so that the noise injected by stochastic models can be reproduced per ensemble member (microsoft#191). A single generator drives one stream for the whole batch, while a tuple draws each batch element from its own stream, making a member's noise sequence independent of the batch composition. The noise cache is now also invalidated on device or dtype changes, not only shape changes.
Author
|
@microsoft-github-policy-service agree |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #191.
This implements the design suggested by Wessel (@wesselb) in #191 (comment) (see the discussion there): instead of a constructor-level seed,
Aurora.forwardaccepts a keyword-onlygenerator: torch.Generator | tuple[torch.Generator | None, ...] | None = None, which is passed through toSwin3DTransformerBackbone.forwardand used in thetorch.randncalls. RNG state is owned entirely by the caller, so no reset method is needed: re-seeding the generator(s) restores the noise sequence.Semantics
(B, L, D)call. The stream therefore depends on the batch size, matching the semantics of a plain seededtorch.randn.(L, D)from its own generator, so a given member's noise sequence is independent of the batch composition: memberiproduces the same sequence whether it runs in a batch of 1 or a batch of N. Entries may beNoneto fall back to the global RNG for that member, and passing the same generator object in several slots deliberately shares one stream. Because the two modes draw with different shapes, a single generator and a tuple are not interchangeable.generator=None(default) preserves the current behaviour exactly (global RNG).Design decisions
ValueErrorcannot leave some generators already advanced. For a single generator, a device mismatch surfaces as PyTorch's usualRuntimeError.Aurora.reset_noise(): noise cached by noise accumulation in a previous run would otherwise contaminate the reproduced sequence. This is documented onforward,reset_noise, androllout.generatorto a non-stochastic model warns once (UserWarning) and ignores the argument, so the mistake is surfaced without spamming roll-outs.rollout()also accepts and passes throughgenerator. This goes slightly beyond the design suggested in the issue, but reproducing an inference run in practice means reproducing a roll-out; the generators advance across (sub-)steps and a tuple stays bound to the batch-dim order throughout. Happy to drop this part if you prefer a smaller PR.Verification
tests/v1p5/test_forward_generator.pycovers: re-seeding a generator (or a fresh same-seed generator on a fresh model instance) reproduces the exact noise sequence, also with noise accumulation enabled; without re-seeding the stream keeps advancing; per-member reproducibility with a tuple, including independence from the batch composition (batch of 1 vs batch of 3) and ofNoneentries from their neighbours' generators; a length mismatch raises before consuming any randomness; a non-stochastic model warns once and does not consume the generator;generator=Nonepreserves the current global-RNG semantics; and the roll-out pass-through. Two CUDA-only tests additionally check that an index-lesstorch.Generator(device="cuda")is accepted on acuda:0model (matching PyTorch's own device semantics) and that a device mismatch raises before consuming any randomness.All 49 tests under
tests/v1p5/pass (34 existing + 15 new; the two CUDA tests are skipped without a GPU), as do the existing roll-out, batch, and header tests.docs/models.mdgains a short "Reproducible Noise" subsection in the Aurora 1.5 Ensemble section with usage examples for both modes.As in the prototype discussed in the issue, the tests record the sampled noise directly instead of comparing model outputs, because with randomly initialised weights the adaptive-LN modulation is zero-initialised and the noise context does not affect the output at initialization.
Minimal reproduction
With the pretrained checkpoint, the effect is visible directly in the forecasts. The following script rolls out two ensemble members with per-member generators, then reproduces the exact same forecasts by re-seeding the generators:
Both checks print
Truewith the released checkpoint (verified on an RTX 5080): the reproduced forecasts are exactly equal, and a run without re-seeding is not. The same works on CPU by dropping.cuda()and creating CPU generators.Disclosure
This PR was developed with AI assistance. I have reviewed, tested, and take responsibility for all of the changes.