fix: Do not pass is_first_microbatch to TE when it is not maintained - #6965
Open
ZhiyuLi-Nvidia wants to merge 1 commit into
Open
fix: Do not pass is_first_microbatch to TE when it is not maintained#6965ZhiyuLi-Nvidia wants to merge 1 commit into
ZhiyuLi-Nvidia wants to merge 1 commit into
Conversation
ZhiyuLi-Nvidia
force-pushed
the
zhiyul/ckpt-is-first-microbatch
branch
2 times, most recently
from
August 31, 2026 00:10
65c071e to
3abefa0
Compare
ZhiyuLi-Nvidia
marked this pull request as ready for review
August 31, 2026 05:38
ZhiyuLi-Nvidia
force-pushed
the
zhiyul/ckpt-is-first-microbatch
branch
from
August 31, 2026 06:11
3abefa0 to
7db7e7b
Compare
TEGroupedLinear.is_first_microbatch is process-local: True at construction,
False after the first forward, and never written to a checkpoint. It is only
re-armed per iteration for quantized configs, because set_is_first_microbatch
is called from the fp8/fp4/kitchen paths.
Transformer Engine derives the weight-gradient accumulation mode from it:
accumulate = fuse_wgrad_accumulation and not is_first_microbatch
so a stale True makes the wgrad GEMM overwrite main_grad with beta=0 instead
of accumulating into it. That is harmless for a module invoked once per
microbatch -- it overwrites a freshly zeroed buffer -- but destructive for one
invoked twice, such as an MTP block sharing a layer across depths under
--mtp-use-repeated-layer. Backward runs in reverse there, so the is_first=True
invocation lands second and discards the other depth's contribution.
The result is a checkpoint resume that is not bit-exact: a freshly loaded
process has is_first_microbatch=True while a process that has been training
has False, and only the fresh one corrupts its gradients. A run-to-run A/A
check cannot detect this, because both fresh processes make the same mistake
and agree.
Add is_first_microbatch_tracked(config) next to set_is_first_microbatch, which
returns whether the flag is actually maintained for this config, and pass None
to TE when it is not. TE then falls back to its own accumulate decision rather
than acting on a value nobody updates.
Verified on a three-leg resume check (train-through vs save vs resume) at 2 and
64 nodes: without this the resumed leg diverges in grad norm one step after the
resume; with it all checkpoint state shards are byte-identical.
Signed-off-by: Zhiyu Li <zhiyul@nvidia.com>
ZhiyuLi-Nvidia
force-pushed
the
zhiyul/ckpt-is-first-microbatch
branch
from
August 31, 2026 06:55
7db7e7b to
293a85b
Compare
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.
What does this PR do?
TEGroupedLinear.is_first_microbatch resets to True after checkpoint resume, causing the first shared-layer call to overwrite main_grad instead of accumulating into it. When an MTP layer is reused across depths, this discards one depth’s gradient and makes resumed training differ from uninterrupted training.
This only becomes destructive when the same layer is invoked multiple times, such as MTP repeated-layer sharing.
Restore or initialize the state deterministically so resume matches uninterrupted training:
Why no existing test catches it
A run-to-run A/A check cannot detect this. Both fresh processes make the same mistake and agree. It only appears between a process that has trained N steps and one that has just loaded a checkpoint, so it takes three separate processes to observe.
Fix
Add
is_first_microbatch_tracked(config)next toset_is_first_microbatch, returning whether the flag is actually maintained for this config, and passNoneto TE when it is not. TE then falls back to its own accumulate decision rather than acting on a value nobody updates.Verification
Three-leg check — train-through to step 10 / save at 5 / resume from 5 — all legs in one allocation, comparing checkpoint shards byte for byte:
iter_10Confirming arm: disabling gradient-accumulation fusion also makes the divergence vanish, which is what the mechanism predicts — with fusion off the
andshort-circuits andis_first_microbatchcan no longer select overwrite.Issue tracking
For PRs from open-source community contributors:
Linked issue:
Contribution process
Pre-checks
Code review
Feel free to message or comment @NVIDIA/mcore-oncall to help accelerate your merge into main. The less complex your PR is, the faster it will be approved and merged!
All PRs start as draft. If you open a non-draft PR, it will be automatically converted to draft.
Step 1: Mark PR as "Ready for Review"
.github/CODEOWNERS.Final Review might get declined if these requirements are not fulfilled.
Step 2: Final Review
For PRs that change
megatron/core, once all expert reviewers have approved, theFinal Reviewlabel is applied automatically and final reviewers are assigned.For PRs outside
megatron/core, this step is skipped.Step 3: Approved
Once all required reviewers have approved, the
Approvedlabel is applied automatically.Merge
Any member of mcore-engineers will be able to merge your PR.