Skip to content

fix(model): store Muse Glimmer final norm uncentered for exact bf16 round trip - #6256

Draft
kamran-nvidia wants to merge 3 commits into
mainfrom
kjafarisadeg/fix-muse-glimmer-final-norm-roundtrip
Draft

kamran-nvidia wants to merge 3 commits into
mainfrom
kjafarisadeg/fix-muse-glimmer-final-norm-roundtrip

Conversation

@kamran-nvidia

@kamran-nvidia kamran-nvidia commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

What does this PR do ?

Makes the Muse Glimmer HF ↔ Megatron round trip bit-exact again. Before this change, the final RMSNorm gain was rounded on import.

Changelog

  • modeling_muse_glimmer.py: MuseGlimmerModel rebuilds decoder.final_norm as a TENorm with layernorm_zero_centered_gamma=False. HF Muse applies the final-norm gain directly (w); only the decoder-layer norms use 1 + w. Those layer norms stay zero-centered.
  • muse_glimmer_bridge.py: decoder.final_norm.weight now uses ReplicatedMapping instead of RMSNorm2ZeroCenteredRMSNormMapping, so the released weights are stored unchanged.
  • test_muse_glimmer.py: updated the final-norm assertions and added test_final_norm_mapping_round_trips_bf16_exactly, which round-trips bf16 gains in [-5, -3) through the mapping and checks them with torch.equal.

Root cause

RMSNorm2ZeroCenteredRMSNormMapping does w -= 1 on import and w += 1 on export in the tensor's own dtype (bf16). When w - 1 falls in a coarser bf16 range than w, the stored value is rounded and adding 1 can't recover it.

The released final-norm gains (model.language_model.norm.weight, 6,656 elements) range from -4.94 to 4.38, split almost evenly between negative and positive values. After a round trip, 1,120 elements change and the other 1,435 tensors are exact:

  • 1,061 elements are in [-4, -3). There, w - 1 falls in [-5, -4), where the bf16 step is 0.03125 instead of 0.015625 (max error 0.015625).
  • 59 elements are in [-2, 1), where w - 1 also lands in a coarser range (errors up to 0.0078125).
  • Gains of 1 or more, including all the large positive ones, are unaffected.

The final norm was switched to the zero-centered mapping by refactor(muse-glimmer): use native normalization modules inside #5479, after the verification card's last exact audit. This PR restores the setup that audit covered: an uncentered final norm whose weights are stored as-is. RMSNorm2ZeroCenteredRMSNormMapping itself is unchanged, so the Qwen3.5 and Qwen3-Next bridges are not affected.

Reviewer notes

  • Checkpoint compatibility: Megatron checkpoints imported with the previous code store the final norm as w - 1. With this change they would load as w - 1 and be used as w. Re-import those checkpoints from HF.
  • Forward pass: w and 1 + (w - 1) are equal in exact arithmetic, but with bf16 storage the old model applied 1 + round(w - 1). That differed from HF's w on the same 1,120 channels (max 0.015625). The final norm now applies the HF gain exactly.

Validation

Environment: NeMo 26.10.rc0 container (Transformer Engine 2.18, Transformers 5.15.0) with MCore at this PR's pinned 07147d69.

  • Unit tests: python -m pytest tests/unit_tests/models/muse_glimmer/test_muse_glimmer.py passes 22/22. As a control, running this PR's test file against the pre-fix base (c4067f5) fails the three updated tests.
  • Muse-Glimmer-30B round trip (revision f84ecc3a, bf16): ran scripts/conversion/run_conversion.py with the card's import / export arguments. Each run was followed by an audit comparing every exported tensor with the source (keys, dtypes, shapes, bytes) and a strict Transformers reload.
Code Path Bitwise-exact tensors Strict reload
this PR (ffcf2d6) GPU, TP8 1,436 / 1,436 (29,776,626,688 elements) no missing, unexpected, mismatched, or error keys; 29,776,626,688 parameters
this PR (ffcf2d6) CPU 1,436 / 1,436 (29,776,626,688 elements) same as above
pre-fix base (c4067f5) GPU, TP8 1,435 / 1,436: model.language_model.norm.weight differs in 1,120 / 6,656 elements, max 0.015625 not run
  • ruff lint and format pass.

Remaining before this leaves draft:

  • python -m pytest tests/unit_tests/models/muse_glimmer/test_muse_glimmer.py
  • Muse-Glimmer-30B HF → Megatron → HF round trip is bit-exact for all 1,436 tensors (GPU TP8 and CPU)
  • Update bridge_commit / last_verified for the conversion items in examples/model_verification_cards/muse-glimmer-30b/card.yaml

GitHub Actions CI

See the CI section in the Contributing doc for how to trigger the CI. A Nvidia developer will need to approve and trigger the CI for external contributors.

Before your PR is "Ready for review"

Pre checks:

  • Make sure you read and followed Contributor guidelines
  • Did you write any new necessary tests?
  • Did you add or update any necessary documentation? (conversion re-verified, see Validation; card metadata not yet updated; no doc text changes needed)
  • Does the PR affect components that are optional to install? (Ex: Numba, Pynini, Apex etc)
    • Reviewer: Does the PR have correct import guards for all optional libraries?

Additional Information

  • Related to NVBug 6848228

🤖 Generated with Claude Code

…ound trip

HF Muse applies its final RMSNorm gain directly (w), unlike the decoder-layer
norms (1 + w). The bridge mapped decoder.final_norm.weight through
RMSNorm2ZeroCenteredRMSNormMapping, which stores w - 1 in bf16. For Muse's
final-norm gains (about -5 to -3) that subtraction rounds, so 1,120 of 6,656
elements of model.language_model.norm.weight changed by one bf16 step after an
HF -> Megatron -> HF round trip.

Build the Megatron final norm with zero_centered_gamma=False and map it with
ReplicatedMapping so the released weights are stored as-is. Add a regression
test that round-trips bf16 gains in [-5, -3) through the mapping bit-exactly.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Kamran Jafari <kjafarisadeg@nvidia.com>
@copy-pr-bot

copy-pr-bot Bot commented Sep 30, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@kamran-nvidia kamran-nvidia added bug Something isn't working area:model Model implementations and HF bridge logic labels Sep 30, 2026
@kamran-nvidia

Copy link
Copy Markdown
Contributor Author

/ok to test ffcf2d6

@cuichenx cuichenx added the breaking-change Public behavior or API compatibility changes label Sep 30, 2026
The released final-norm gains span about -4.9 to 4.4, not [-5, -3). Most
elements that drifted under the old zero-centered mapping (1,061 of 1,120)
lie in [-4, -3), where bf16 w - 1 rounds to a coarser step. The test input
range is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Kamran Jafari <kjafarisadeg@nvidia.com>
@kamran-nvidia

Copy link
Copy Markdown
Contributor Author

/ok to test c9b763d

@kamran-nvidia

Copy link
Copy Markdown
Contributor Author

/ok to test a2a3025

This branch was successfully deployed

1 active deployment
test — a2a3025a Deployed Sep 30, 2026 by copy-pr-bot[bot] via cicd-wait-in-queue #22348
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:model Model implementations and HF bridge logic breaking-change Public behavior or API compatibility changes bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants