Skip to content

fix(model): keep full rotary_percent for DeepSeek-V4 MLA rope - #4271

Merged
Meirtz merged 1 commit into
NVIDIA-NeMo:mainfrom
Meirtz:fix/dsv4-rotary-percent
Jun 10, 2026
Merged

Meirtz merged 1 commit into
NVIDIA-NeMo:mainfrom
Meirtz:fix/dsv4-rotary-percent

Conversation

@Meirtz

@Meirtz Meirtz commented Jun 10, 2026

Copy link
Copy Markdown
Member

What

One-line fix: force rotary_percent = 1.0 in the DeepSeek-V4 bridge, plus a regression test.

Why — root cause of the DSv4-Flash SFT NaN (NVIDIA/Megatron-LM#4468)

HF's partial_rotary_factor (0.125) is relative to head_dim (512). The generic partial_rotary_factor → rotary_percent mapping applies it on top of qk_pos_emb_head_dim (64) — which already encodes the rope split (448 nope + 64 rope). The double-applied factor shrinks the rope cos/sin cache to 64 × 0.125 = 8 dims, and then:

  • the unfused path silently rotates only 8 of 64 rope dims (no crash — masked correctness issue on every DSv4 bridge run);
  • the fused MLA yarn-rope kernel (fused_mla_rope_inplace, called with emb_dim=64) reads cos/sin out of bounds, producing huge garbage activations (q jumps ~5 → ~1e14, captured in-situ) → the iteration-2 found NaN in local forward loss calculation in DeepSeek-V4-Flash SFT, reproduced on both H100 and GB300.

Debug trail (single-variable isolations, in-situ capture, and replay) is in NVIDIA/Megatron-LM#4468. Pretrain never hit it because the mock data path uses fixed-length sequences and the validated pretrain configs didn't exercise the same cache shape.

Validation (GB300, proxy DSv4-Flash SFT, EP4)

Run rotary_percent apply_rope_fusion Result
control 0.125 (status quo) False trains, but rotates 8/64 dims
repro 0.125 True NaN at iteration 2
fixed 1.0 False clean, 10/10 iters
fixed 1.0 True clean, matches unfused control to 4 decimals (12.46808→7.554 vs 12.46882→7.5535)

Unit test: mocks the parent provider_bridge to inject rotary_percent=0.125 (what the generic mapping produces) and asserts the DSv4 bridge forces it back to 1.0.

Notes

🤖 Generated with Claude Code

HF's partial_rotary_factor (0.125) is relative to head_dim (512), but the generic partial_rotary_factor -> rotary_percent mapping applies it on top of qk_pos_emb_head_dim (64), which already encodes the rope split. The resulting 8-dim cos/sin cache makes the unfused path silently rotate only 8 of 64 rope dims, and makes the fused MLA yarn-rope kernel read cos/sin out of bounds — producing huge garbage activations (~1e14) and the iteration-2 NaN in DeepSeek-V4-Flash SFT (NVIDIA/Megatron-LM#4468). Force rotary_percent=1.0 in the DSv4 bridge.

Validated on GB300 (proxy DSv4-Flash SFT): with rotary_percent=1.0, fused-rope SFT trains clean through 10 iters and matches the unfused control to 4 decimal places; without it, the same run NaNs at iteration 2.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Lingrui Mei <lmei@nvidia.com>
# head_dim = 512 (nope_dim + rope_dim = 448 + 64)
provider.v_head_dim = hf_config.head_dim # 512
provider.qk_pos_emb_head_dim = hf_config.qk_rope_head_dim # 64
# HF's partial_rotary_factor (0.125) is relative to head_dim (512); the rope split is

@yaoyu-33 yaoyu-33 Jun 10, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this a different definition issue of HF vs. Megatron, if so, how can we avoid in the future? we can do a auto mapping still. or guard with a check

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes — definition mismatch: HF's partial_rotary_factor is relative to head_dim (512×0.125 = the 64 rope dims), while Megatron's rotary_percent applies to qk_pos_emb_head_dim (64), which already is the rope dim — so for MLA it gets applied twice.

For the future, both of your options work: (1) keep the auto-mapping but skip it for MLA providers (multi_latent_attention=True), where the factor is always redundant — happy to send that as a follow-up; (2) guards: fused_mla_rope_inplace asserting cos.shape[-1] == emb_dim (suggested in NVIDIA/Megatron-LM#4468), so the misconfig fails loudly instead of OOB-reading.

@Meirtz
Meirtz merged commit 00174bc into NVIDIA-NeMo:main Jun 10, 2026
104 checks passed
vasunvidia pushed a commit to vasunvidia/Megatron-Bridge that referenced this pull request Jun 10, 2026
…-NeMo#4271)

Signed-off-by: Lingrui Mei <lmei@nvidia.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Vasudevan Rengasamy <vrengasamy@nvidia.com>
Meirtz added a commit to Meirtz/verl that referenced this pull request Jun 11, 2026
The fused-rope SFT NaN was a Megatron-Bridge config-mapping bug fixed in NVIDIA-NeMo/Megatron-Bridge#4271 (already on Megatron-Bridge main, which this example clones). Full-model DSv4-Flash SFT on 8xGB300 with rope fusion now matches the unfused control (iter-10 lm loss 2.3319 vs 2.3306, 0 NaN). Restores the varlen perf path.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Lingrui Mei <lmei@nvidia.com>

This branch was previously deployed

2 inactive deployments
public — fe305c76 Deployed Jun 10, 2026 by copy-pr-bot[bot] via release / finalize / publish-docs #1319
test — fe305c76 Deployed Jun 10, 2026 by copy-pr-bot[bot] via cicd-wait-in-queue #16080
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 bug Something isn't working needs-review PR is ready for code review and waiting on a reviewer

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants