NVIDIA-NeMo / NVIDIA-NeMo/Automodel
Fused TE RoPE produces NaN on MLA k_pe in THD/packed-sequence mode (Moonlight, GLM-4.7-Flash)
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 963
- Forks
- 318
- Avg merge
- 3d 20h
- Merged PRs (30d)
- 143
Description
Summary
For MLA models trained with backend.attn: te + packed sequences (THD), the fused Transformer-Engine RoPE path produces non-finite values on the single-head k_pe at the very first forward. The resulting NaN loss/grad at step 0 corrupts all weights through the optimizer step, and at step 1 the all-NaN router collapses MoE routing onto one expert-parallel rank, which surfaces as a CUDA OOM in the token-permute. The OOM is a symptom; the root cause is the RoPE NaN.
This was originally reported as an OOM (moonlight_16b_te_packed_sequence), but it is not a memory/capacity problem and not a checkpoint-loading problem (weights load 100% clean).
Affected configs (confirmed)
| Recipe | Model | Arch |
|---|---|---|
examples/llm_finetune/moonlight/moonlight_16b_te_packed_sequence.yaml |
moonshotai/Moonlight-16B-A3B | DeepSeek-V3 MLA |
examples/llm_finetune/glm/glm_4.7_flash_te_packed_sequence.yaml |
zai-org/GLM-4.7-Flash | GLM-4.x MLA |
Both reproduce loss nan | grad_norm nan at step 0 → step-1 MoE-dispatch OOM. Any other MLA + TE + packed-sequence MoE recipe is expected to hit this too (e.g. glm_moe_dsa, DeepSeek-V3.2).
Root cause
rope_fusion defaults to True when TE+CUDA are present (nemo_automodel/components/models/common/utils.py:199), and these recipes do not override it. The MLA forward then calls:
deepseek_v3/layers.py: apply_rotary_emb_qk(q_pe, k_pe, ...)
-> rope_utils.apply_rotary_emb_qk(rope_fusion=True)
-> transformer_engine.pytorch.attention.rope.apply_rotary_pos_emb(tensor_format="thd", interleaved=True, fused=True)
In THD/packed mode this corrupts only the single-head k_pe (shape (T, 1, 64)); q_pe (multi-head) under the identical freqs is fine.
Instrumented repro on the exact failing commit (d94e9110 = r0.5.0 HEAD), 8×H100, ep_size=8:
ROPE call#1 IN q_pe[nan=0 finmax=1.0e1] k_pe[nan=0 finmax=1.65e1] freqs[finmax=4.096e3]
ROPE call#1 OUT q_pe[nan=0 finmax=1.0e1] k_pe[nan=30 finmax=3.390e38] <-- bf16 max + NaN
- Loaded-weight scan:
bad_tensors=0on all ranks (not a loading bug). - First non-finite in the whole forward:
layers.0.self_attn...fused_attention,bad_input=True. - The THD fused positions are built as a contiguous
arange(num_tokens)(rope_utils.freqs_cis_from_position_ids,for_fused_rope=True), sofreqsmax ≈ the pack length rather than per-sequence positions.
Downstream chain (why it looks like an OOM)
k_peNaN →kNaN → attention NaN →loss nan/grad_norm nanat step 0.- NaN grad → Adam (bf16) corrupts every weight.
- Step 1: embedding/router all-NaN →
torch.topkof all-NaN scores returns experts0..k-1, all on EP-rank 0 → entire global batch routed to one rank → TE permute buffer OOM.
Workaround (this PR)
Set model.backend.rope_fusion: false in the affected recipes. The non-fused path does the rotation as a unit-magnitude complex multiply in fp32 → bounded. Validated on cw-dfw (8×H100, commit d94e9110):
| Recipe | rope_fusion=True (current) | rope_fusion=false (fix) |
|---|---|---|
| Moonlight-16B | loss nan → OOM |
loss 2.57 → 2.47 → 2.16, val 2.07, no NaN, balanced routing, no OOM |
| GLM-4.7-Flash | loss nan → OOM |
loss 3.03 → 2.84 → 2.67, val 2.50, no NaN, balanced routing, no OOM |
Proper fix (tracked here, beyond the workaround PR)
Make the fused TE RoPE path numerically correct for THD so fusion can stay on:
- Fix handling of the single-head
k_pe(T,1,D)tensor in the fused THD kernel (the layout that overflows), and/or - Build per-sequence positions from
cu_seqlensforfor_fused_rope=TrueTHD instead of a globalarange(num_tokens).
Possibly related (not confirmed)
qwen3_moe_30b_te_packed_sequence_lora fails with finite loss but grad_norm nan (backward NaN) → same step-1 collapse/OOM. It is packed-specific (non-packed Qwen3-MoE variants have finite grad_norm) but Qwen3-MoE is not MLA, so it is a separate failure (possibly the fused-RoPE backward on THD); needs its own investigation.
Repro pointers
CI pipeline 55312948 (main-mirror): jobs 344336851 (moonlight), 344336811 (glm_4.7_flash).
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the affected Moonlight and GLM recipe files, then trace rope_fusion from common/utils.py:199 through deepseek_v3/layers.py and rope_utils.apply_rotary_emb_qk. Reproduce with CI pipeline 55312948 or the listed jobs and inspect the THD q_pe/k_pe behavior. Done means the affected packed-sequence recipes produce finite loss and gradients, balanced routing, and no OOM while preserving fused RoPE where correct.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python, pytorch
- Domain
- distributed-systems, machine-learning, performance
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 32/100