Skip to content

Commit 2ef7854

Browse files
HJSangclaude
andcommitted
Send the packed boundaries for every kernel again, not only FlashAttention-4
This reverts the FlashAttention-4-only gate. True-on-policy's FA3 bitwise break came from max_length counting the pad segment, not from the boundaries themselves: with the longest real document as max_length (previous commit) and the boundaries sent under every kernel, the true-on-policy e2e test is bit-identical again (H200, train_rollout_logprob_abs_diff 0.0 on all five rollouts). Without the gate, the kernels-community/flash-attn4 spelling of --attn-implementation gets the boundaries too. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent 05763b9 commit 2ef7854

8 files changed

Lines changed: 14 additions & 26 deletions

File tree

‎docs/user-guide/training-backend.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -381,8 +381,8 @@ targets are Megatron-only: both `--offload-train-target disk` and
381381
- `--attn-implementation` picks the `transformers` attention backend: `flash_attention_2` by
382382
default, with `flash_attention_3`, `sdpa` and `eager` passed straight through.
383383
`flash_attention_4` uses the FlashAttention-4 that SGLang installs, so it trains without a
384-
`flash-attn` build. With it, the adapter passes the packed-document boundaries (Qwen3,
385-
Qwen3-MoE, Qwen3-VL, GLM-4.7-Flash and Qwen3.5); other kernels keep `transformers`' own path.
384+
`flash-attn` build. It needs an adapter that passes the packed-document boundaries: Qwen3,
385+
Qwen3-MoE, Qwen3-VL, GLM-4.7-Flash and Qwen3.5.
386386
- An architecture with fussier numerics can register its own policy, see
387387
[when an HF model needs help](#going-deeper-when-an-hf-model-needs-help).
388388

‎miles/backends/fsdp_utils/actor.py‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -408,7 +408,6 @@ def _get_model_inputs_args(self, batch: dict) -> dict:
408408
cu_seqlens=batch["cu_seqlens"],
409409
cu_seqlens_host=batch["cu_seqlens_host"],
410410
max_seqlen=real_max_seqlen(batch["cu_seqlens_host"], len(batch["unconcat_tokens"])),
411-
attn_implementation=self.args.attn_implementation,
412411
)
413412
)
414413

‎miles/backends/fsdp_utils/adaptations/arch_adapter.py‎

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,11 @@ def patch_model(self, model: nn.Module, args: Namespace) -> None:
4141
and the ref model, so it must be idempotent."""
4242

4343
def packing_kwargs(
44-
self, *, cu_seqlens: torch.Tensor, cu_seqlens_host: tuple[int, ...], max_seqlen: int, attn_implementation: str
44+
self, *, cu_seqlens: torch.Tensor, cu_seqlens_host: tuple[int, ...], max_seqlen: int
4545
) -> dict[str, object]:
4646
"""Extra forward kwargs that tell this architecture's modeling where the packed documents that
4747
``get_batch`` recorded start."""
48-
# only FlashAttention-4 rejects the boundaries HF re-derives; other kernels keep that path, which
49-
# true-on-policy matches bit for bit
50-
if not (self.passes_attention_packing_kwargs and attn_implementation == "flash_attention_4"):
48+
if not self.passes_attention_packing_kwargs:
5149
return {}
5250
return hf_attention_packing_kwargs(cu_seqlens=cu_seqlens, max_seqlen=max_seqlen)
5351

‎miles/backends/fsdp_utils/adaptations/specs/qwen3_5.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
class Qwen35Adapter(ArchAdapter):
1111
model_types = frozenset({"qwen3_5", "qwen3_5_text", "qwen3_next"})
1212

13-
def packing_kwargs(self, *, cu_seqlens, cu_seqlens_host, max_seqlen, attn_implementation):
13+
def packing_kwargs(self, *, cu_seqlens, cu_seqlens_host, max_seqlen):
1414
# GatedDeltaNet reads seq_idx (causal conv) and cu_seq_lens_q (FLA chunk rule) from **kwargs.
1515
return hf_packing_kwargs(cu_seqlens=cu_seqlens, cu_seqlens_host=cu_seqlens_host, max_seqlen=max_seqlen)
1616

‎tests/fast-gpu/test_fsdp_gated_deltanet_packing.py‎

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,6 @@ def _packed_batch(docs, padded_length):
123123
def _actor_for(model):
124124
actor = object.__new__(FSDPTrainRayActor)
125125
actor.arch_adapter = resolve_arch_adapter(model.config)
126-
actor.args = SimpleNamespace(attn_implementation=model.config._attn_implementation)
127126
return actor
128127

129128

@@ -284,7 +283,6 @@ def test_building_packing_kwargs_never_syncs_the_device():
284283
"""A host-device sync on every micro-batch from sizing `seq_idx` with a device-side length."""
285284
actor = object.__new__(FSDPTrainRayActor)
286285
actor.arch_adapter = resolve_arch_adapter(SimpleNamespace(model_type="qwen3_5"))
287-
actor.args = SimpleNamespace(attn_implementation="flash_attention_2")
288286
docs = [_random_tokens(length, seed) for seed, length in enumerate((37, 64, 129))]
289287
batch = _packed_batch(docs, padded_length=256)
290288

‎tests/fast/backends/fsdp_utils/test_actor.py‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,6 @@ def test_packing_kwargs_follow_the_batch_boundaries_so_padding_stays_one_segment
9191
"""Pad tokens carry position 0; boundaries derived from `position_ids` would make each one a document."""
9292
actor = object.__new__(actor_module.FSDPTrainRayActor)
9393
actor.arch_adapter = resolve_arch_adapter(SimpleNamespace(model_type="qwen3_5_text"))
94-
actor.args = SimpleNamespace(attn_implementation="flash_attention_2")
9594
cu_seqlens_host = (0, 3, 5, 8) # two documents, then the 3-token pad segment get_batch appends
9695
batch = {
9796
"tokens": torch.zeros(1, 8, dtype=torch.long),

‎tests/fast/backends/test_fsdp_hf_compat.py‎

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -360,20 +360,15 @@ def test_real_max_seqlen_leaves_out_the_pad_segment():
360360
def test_each_arch_passes_the_packing_kwargs_its_modeling_reads():
361361
boundaries = _packed_boundaries(2, 3)
362362
for model_type in ("qwen3_5", "qwen3_5_text", "qwen3_5_moe", "qwen3_5_moe_text", "qwen3_next"):
363-
kwargs = _adapter(model_type).packing_kwargs(**boundaries, attn_implementation="flash_attention_2")
364-
assert "seq_idx" in kwargs, model_type
365-
# stock HF attention: varlen boundaries for FlashAttention-4 only, max_length an int; other kernels
366-
# keep HF's position_ids path, which true-on-policy matches bit for bit
363+
assert "seq_idx" in _adapter(model_type).packing_kwargs(**boundaries), model_type
364+
# stock HF attention: varlen boundaries, max_length an int
367365
for model_type in ("glm4_moe_lite", "qwen3", "qwen3_moe", "qwen3_vl"):
368-
adapter = _adapter(model_type)
369-
kwargs = adapter.packing_kwargs(**boundaries, attn_implementation="flash_attention_4")
366+
kwargs = _adapter(model_type).packing_kwargs(**boundaries)
370367
assert set(kwargs) == {"cu_seq_lens_q", "cu_seq_lens_k", "max_length_q", "max_length_k"}, model_type
371368
assert type(kwargs["max_length_q"]) is int and kwargs["max_length_q"] == 3, model_type
372-
for other in ("flash_attention_2", "flash_attention_3", "sdpa"):
373-
assert adapter.packing_kwargs(**boundaries, attn_implementation=other) == {}, (model_type, other)
374369
# NemotronH resets through its own patch; other archs (including remote code) keep their stock inputs.
375370
for model_type in ("nemotron_h", "some_remote_lm"):
376-
assert _adapter(model_type).packing_kwargs(**boundaries, attn_implementation="flash_attention_4") == {}
371+
assert _adapter(model_type).packing_kwargs(**boundaries) == {}, model_type
377372

378373

379374
def test_qwen3_attention_receives_the_packed_boundaries(monkeypatch):
@@ -406,7 +401,7 @@ def attention_stand_in(module, query, key, value, attention_mask, scaling=None,
406401
model(
407402
input_ids=torch.arange(5).view(1, 5),
408403
position_ids=torch.tensor([[0, 1, 2, 0, 1]]),
409-
**_adapter("qwen3").packing_kwargs(**_packed_boundaries(3, 2), attn_implementation="flash_attention_4"),
404+
**_adapter("qwen3").packing_kwargs(**_packed_boundaries(3, 2)),
410405
)
411406

412407
assert len(seen) == cfg.num_hidden_layers
@@ -432,8 +427,7 @@ def forward(self, pixels, **kwargs):
432427
adapter.patch_model(model, None)
433428
assert model.model.visual.forward is wrapped # idempotent across the actor and ref model
434429

435-
packing_kwargs = adapter.packing_kwargs(**_packed_boundaries(3, 2), attn_implementation="flash_attention_4")
436-
model.model.visual(None, grid_thw=1, **packing_kwargs)
430+
model.model.visual(None, grid_thw=1, **adapter.packing_kwargs(**_packed_boundaries(3, 2)))
437431
assert calls == [{"grid_thw": 1}]
438432

439433

@@ -480,7 +474,7 @@ def chunk_rule(*args, cu_seqlens=None, **kwargs):
480474
monkeypatch.setattr(modeling_qwen3_5, "causal_conv1d_fn", conv)
481475
monkeypatch.setattr(modeling_qwen3_5, "torch_chunk_gated_delta_rule", chunk_rule)
482476

483-
packing_kwargs = _adapter("qwen3_5_text").packing_kwargs(**_packed_boundaries(3, 2), attn_implementation="eager")
477+
packing_kwargs = _adapter("qwen3_5_text").packing_kwargs(**_packed_boundaries(3, 2))
484478
with torch.no_grad():
485479
model(input_ids=torch.arange(5).view(1, 5), position_ids=torch.tensor([[0, 1, 2, 0, 1]]), **packing_kwargs)
486480

@@ -533,7 +527,7 @@ def flash_stand_in(module, query, key, value, attention_mask, scaling=None, cu_s
533527
position_ids=torch.tensor([[0, 1, 2, 0, 1, 2, 3]]),
534528
pixel_values=torch.randn(4, 3 * 2 * 2 * 2),
535529
image_grid_thw=torch.tensor([[1, 2, 2]]),
536-
**adapter.packing_kwargs(**_packed_boundaries(3, 4), attn_implementation="eager"),
530+
**adapter.packing_kwargs(**_packed_boundaries(3, 4)),
537531
)
538532
with torch.no_grad():
539533
model(**inputs)

‎tests/fast/backends/test_fsdp_hub_kernels.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -524,7 +524,7 @@ def hub_conv(
524524
inputs = dict(
525525
input_ids=torch.arange(5).view(1, 5),
526526
position_ids=torch.tensor([[0, 1, 2, 0, 1]]),
527-
**_adapter("qwen3_5_text").packing_kwargs(**_packed_boundaries(3, 2), attn_implementation="eager"),
527+
**_adapter("qwen3_5_text").packing_kwargs(**_packed_boundaries(3, 2)),
528528
)
529529
with torch.no_grad():
530530
native_logits = model(**inputs).logits

0 commit comments

Comments
 (0)