fix(model): keep full rotary_percent for DeepSeek-V4 MLA rope - #4271
Conversation
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 |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
…-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>
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>
What
One-line fix: force
rotary_percent = 1.0in 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 tohead_dim(512). The genericpartial_rotary_factor → rotary_percentmapping applies it on top ofqk_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:fused_mla_rope_inplace, called withemb_dim=64) reads cos/sin out of bounds, producing huge garbage activations (q jumps ~5 → ~1e14, captured in-situ) → the iteration-2found NaN in local forward loss calculationin 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)
Unit test: mocks the parent
provider_bridgeto injectrotary_percent=0.125(what the generic mapping produces) and asserts the DSv4 bridge forces it back to 1.0.Notes
apply_rope_fusionfor DSv4 SFT ([megatron] feat: add DeepSeek-V4-Flash SFT example (Megatron-Bridge backend) verl-project/verl#6603 ships it off as a workaround).fused_mla_rope_inplaceshould assertcos.shape[-1] == emb_diminstead of reading out of bounds.🤖 Generated with Claude Code