[megatron] Fix TE sliding-window attention crash at micro_batch>1 on the non-packed forward path#1848
Conversation
…the non-packed forward path On the non-packed forward path, remove_left_padding returns a 2-D [batch, seq] keep-mask passed straight to model(). For models using TE sliding-window attention, get_full_mask does torch.logical_or(swa_mask, attention_mask), which is not broadcastable against a 2-D mask: at micro_batch_size > 1 the batch dim collides with the sequence dim and the forward crashes. Add a pure-torch helper to_te_attention_mask that converts the 2-D keep-mask to a [batch, 1, 1, seq] padding mask and route the model() call sites through it. No-op on the packed path and for non-SWA attention. Adds tests/backends/skyrl_train/distributed/test_mask_utils.py. Reproduced on Qwen/Qwen3.6-35B-A3B (tp=2, mb=2, non-packed): stock crashes in get_full_mask, patched forward passes. Qwen/Qwen3-0.6B passes on both.
There was a problem hiding this comment.
Code Review
This pull request introduces a helper function to_te_attention_mask to convert 2-D keep-masks into 4-D padding masks, preventing dimension collisions in Transformer Engine attention when the micro-batch size is greater than one. The helper is integrated into the Megatron model wrapper's forward step, and comprehensive unit tests have been added to verify its behavior. There are no review comments, and I have no additional feedback to provide.
|
hey @john-hahn, tested and the fix makes sense, running gpu ci now, we can likely get this merged tomorrow pending CI just checking is there a reason you can't use the packed path? |
|
hey @erictang000, just pulled the latest changes and saw your PR #1769 for qwen packing when i first hit this issue, GDN couldnt pack, which is why i went the unpacked route. for some context, i was hitting an error like:
i would say this PR fix is still relevant because anyone who uses thanks for adding the packed support! |
[megatron] Fix TE sliding-window attention crash at micro_batch>1 on the non-packed forward path
Summary
On the non-packed Megatron forward path (
remove_microbatch_padding=False),megatron_model_wrapper.pypasses the 2-D[batch, seq]keep-mask returned byremove_left_padding()straight tomodel(...). For models whose attention uses a TransformerEngine sliding-window mask, TE's
get_full_maskcombines that mask with the SWA mask viatorch.logical_or(swa_mask, attention_mask). A 2-D[batch, seq]mask is not broadcastable againstthe
[..., seq, seq]SWA mask, so at micro_batch_size > 1 the batch dimension collides with thesequence dimension and the forward crashes.
This adds a pure-torch helper
to_te_attention_maskthat converts the 2-D keep-mask to a[batch, 1, 1, seq]padding mask (Truemarks padding), and routes themodel(...)call sitesthrough it. The original 2-D mask is left untouched for the downstream
recover_left_padding().Reproduction (current
main,0a18dd72)Real model
Qwen/Qwen3.6-35B-A3B(hybrid GatedDeltaNet + MoE; its full-attention layers use TEsliding-window attention),
tp=2, pp=1,micro_batch_size=2,remove_microbatch_padding=False,left-padded batch:
maincrashes in the megatron forward:24= sequence length,2=micro_batch_size.)Qwen/Qwen3-0.6B, non-packed,micro_batch_size=2,left-padded) passes the HF-vs-megatron parity test identically on stock and patched — the
helper is a no-op there.
Lifting
micro_batch_sizefrom 1 to 4 on this path (once unblocked) increased training throughput~3.3× at unchanged reward in our multi-LoRA RL runs.
Changes
skyrl/backends/skyrl_train/distributed/megatron/mask_utils.pywithto_te_attention_mask.model(...)call sites inmegatron_model_wrapper.py(4 invocations after [megatron] fused linear cross-entropy for 23x memory savings at 262k context #1841)through the helper.
tests/backends/skyrl_train/distributed/test_mask_utils.py.Design notes
megatronextra.new_attention_maskisNoneandpacked_seq_paramsdrivesmasking, so the helper returns the input unchanged. An already higher-rank mask is also returned
unchanged, so non-SWA TE attention (which already accepts the 2-D mask) is unaffected.
remove_left_padding()because that function's 2-D return is also consumed byrecover_left_padding(), which needs the 2-D form.True = paddingmatches TE's mask convention. GatedDeltaNet / linear-attention layers ignoreattention_mask. No config or API change; behavior is identical atmicro_batch_size = 1.Testing
uv run --extra dev pytest tests/backends/skyrl_train/distributed/test_mask_utils.py— 5 passed.
test_megatron_forward[tp2_pp1_policy]withQwen/Qwen3.6-35B-A3B— fails on stock atget_full_mask, passes with this patch;Qwen/Qwen3-0.6Bpasses on both (regression-safe).Checklist
.claude/docs; no example-script or doc changes needed.bash format.sh(ruff 0.11.9 + black 24.10.0 + gitleaks) clean — verified, zero auto-fixes.