Skip to content

[Megatron Lite] Fix qwen3_moe BSHD forward under context parallelism (#5617)#5661

Open
conver334 wants to merge 1 commit into
NVIDIA:devfrom
conver334:pr/issue-5617-bshd-cp
Open

[Megatron Lite] Fix qwen3_moe BSHD forward under context parallelism (#5617)#5661
conver334 wants to merge 1 commit into
NVIDIA:devfrom
conver334:pr/issue-5617-bshd-cp

Conversation

@conver334

Copy link
Copy Markdown
Contributor

What

Fixes #5617.

In the experimental Megatron-Lite runtime, a Qwen3-MoE eval-only BSHD (use_thd=false) forward with context parallelism and pipeline parallelism enabled together (CP>1, PP>1) crashes in vocab-parallel cross entropy: the final pipeline stage produces CP-local logits but still receives full-sequence labels.

Root cause

_forward_step_bshd() forwarded batch.input_ids / batch.labels unchanged, but:

  • GQAttention's non-THD branch (and its RoPE frequency slicing) assumes its input is already CP-zigzag-sharded (see the "q is CP-zigzag pre-sliced" comment in primitive/modules/gqa.py).
  • The pipeline runtime's _infer_pipeline_tensor_shape() sizes PP activations from the CP-local sequence length.

So with CP>1 the last PP stage's CP-local logits disagreed in shape with the still-full-sequence labels, crashing vocab_parallel_cross_entropy (and, with PP=1, silently misaligning the loss). Only the THD path did CP splitting (via pack_thd_forward_kwargs); the BSHD input side was missing it.

Fix

_forward_step_bshd() now zigzag-splits input_ids / labels / loss_mask with zigzag_slice_for_cp() — the same primitive other models (mla.py, dsa.py, gated_delta_net.py) already use for CP — so the input layout matches what attention/RoPE expect. unpack_forward_output() already reconstructs zigzag-CP outputs back to full-sequence order via unpack_thd_forward_output(), so only the input side needed the change.

Testing

  • Existing tests/unit/model/test_qwen_config_unit.py passes (CPU-only, no GPU required).
  • Numerically validated separately: BSHD CP=2, PP=2 eval-only forward now completes and matches the CP-off baseline for both dist_opt and FSDP2.

Scope

BSHD-only (use_thd=false) path, as reported. The THD path already handled CP and is unchanged.

_forward_step_bshd() forwarded batch.input_ids/labels unchanged, but
GQAttention's non-THD branch (and its RoPE frequency slicing) assumes its
input is already CP-zigzag-sharded, and the pipeline runtime sizes PP
activations from the CP-local sequence length. With CP>1 this left the
final PP stage's CP-local logits disagreeing in shape with the still
full-sequence labels, crashing vocab-parallel cross entropy (and, with
PP=1, silently misaligning loss).

Zigzag-split input_ids/labels/loss_mask with zigzag_slice_for_cp() — the
same primitive other models (mla.py, dsa.py, gated_delta_net.py) already
use for CP — so both sides stay consistent. unpack_forward_output()
already reconstructs zigzag-CP outputs, so only the input side was
missing.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: conver334 <conver334@gmail.com>
@conver334 conver334 requested review from a team as code owners July 6, 2026 05:15
@copy-pr-bot

copy-pr-bot Bot commented Jul 6, 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant