Skip to content

Commit 2cea2c0

Browse files
committed
Simplify packed PEE production controls
1 parent af56578 commit 2cea2c0

7 files changed

Lines changed: 17 additions & 53 deletions

File tree

nemo/collections/asr/modules/ggemm_transformer_encoder.py

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -525,7 +525,6 @@ def forward_grouped_sequence_packed(
525525
backend: str = 'baddbmm',
526526
moe_mode: str = 'dense',
527527
fused_qkv: bool = False,
528-
strict: bool = False,
529528
) -> Dict[str, PackedEncoderOutput]:
530529
"""Run all experts in layer lockstep using native THD grouped kernels.
531530
@@ -541,7 +540,6 @@ def forward_grouped_sequence_packed(
541540
backend=backend,
542541
moe_mode=moe_mode,
543542
fused_qkv=fused_qkv,
544-
strict=strict,
545543
)
546544

547545
def forward_grouped(
@@ -1433,7 +1431,6 @@ def _forward_grouped_sequence_packed(
14331431
backend: str,
14341432
moe_mode: str,
14351433
fused_qkv: bool,
1436-
strict: bool,
14371434
) -> Dict[str, PackedEncoderOutput]:
14381435
if backend not in GROUPED_GEMM_BACKENDS:
14391436
raise ValueError(f"Unknown grouped-GEMM backend '{backend}'; expected one of {GROUPED_GEMM_BACKENDS}.")
@@ -1503,24 +1500,6 @@ def _forward_grouped_sequence_packed(
15031500
}
15041501
del prepared, padded, packed, output_lengths, shared_mask
15051502

1506-
if strict:
1507-
incompatibilities = []
1508-
if any(expert.self_attention_model == 'rel_pos' for expert in encs.values()):
1509-
incompatibilities.append('relative-position attention')
1510-
if len({expert.d_model // expert.n_heads for expert in encs.values()}) != 1:
1511-
incompatibilities.append('head dimensions')
1512-
if len({expert.attn_mode for expert in encs.values()}) != 1:
1513-
incompatibilities.append('attention modes')
1514-
if len({state[name]['metadata_key'] for name in self.expert_names}) != 1:
1515-
incompatibilities.append('packed sequence boundaries')
1516-
if len({(state[name]['x'].device, state[name]['x'].dtype) for name in self.expert_names}) != 1:
1517-
incompatibilities.append('devices/dtypes')
1518-
if incompatibilities:
1519-
raise ValueError(
1520-
"Strict grouped sequence-packed execution requires one compatible attention bucket; "
1521-
f"incompatible {', '.join(incompatibilities)}."
1522-
)
1523-
15241503
trace = {
15251504
'mode': 'grouped_thd',
15261505
'layers': n_layers,

nemo/collections/asr/modules/parallel_expert_encoder.py

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -931,25 +931,21 @@ def forward_sequence_packed(
931931
length,
932932
spk_targets=None,
933933
return_experts: bool = False,
934-
*,
935-
grouped: bool = True,
936934
) -> PackedEncoderOutput | tuple[PackedEncoderOutput, dict[str, object]]:
937935
"""Encode offline while keeping expert Transformer activations token-flat.
938936
939937
Online/windowed inference retains its established prefix/cache path. Existing
940938
:meth:`forward` remains the Conformer-compatible padded API.
941939
942940
Set ``return_experts=True`` to also return packed speech/sound states and
943-
the padded Sortformer speaker predictions. ``grouped=False`` retains a
944-
serial THD numerical/benchmark oracle; production execution is grouped.
941+
the padded Sortformer speaker predictions. Production execution is always
942+
layer-synchronous and grouped; the low-level container retains a serial oracle.
945943
"""
946944
if self.online_inference_enabled:
947945
raise RuntimeError(
948946
"forward_sequence_packed is an offline API and cannot run while online_inference() is enabled."
949947
)
950-
return self._forward_sequence_packed(
951-
audio_signal, length, spk_targets, return_experts=return_experts, grouped=grouped
952-
)
948+
return self._forward_sequence_packed(audio_signal, length, spk_targets, return_experts=return_experts)
953949

954950
def train(self, mode: bool = True) -> "ParallelExpertEncoder":
955951
"""Set training mode, but keep frozen experts in eval.
@@ -1139,7 +1135,6 @@ def _forward_all_sequence_packed_training(self, audio_signal, length):
11391135
'backend': self.sequence_packed_ggemm_backend,
11401136
'moe_mode': self._sequence_packed_moe_execution_mode(),
11411137
'fused_qkv': True,
1142-
'strict': False,
11431138
}
11441139
if not self.activation_checkpointing or not torch.is_grad_enabled():
11451140
return grouped_forward(audio_signal, length, **grouped_kwargs)
@@ -1633,7 +1628,6 @@ def _forward_sequence_packed(self, audio_signal, length, spk_targets=None, *, re
16331628
backend=self.sequence_packed_ggemm_backend,
16341629
moe_mode=self._sequence_packed_moe_execution_mode(),
16351630
fused_qkv=True,
1636-
strict=False,
16371631
)
16381632

16391633
_validate_packed_expert_lengths(packed)

nemo/collections/asr/parts/packed_sequence.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ def _normalize_lengths(lengths: Tensor, *, batch_size: int, max_length: int, dev
151151
raise ValueError(f"lengths must have shape ({batch_size},), got {tuple(lengths.shape)}.")
152152
if lengths.device != device:
153153
raise ValueError(f"lengths must be on {device}, got {lengths.device}.")
154-
if lengths.is_floating_point() or lengths.is_complex():
154+
if lengths.dtype == torch.bool or lengths.is_floating_point() or lengths.is_complex():
155155
raise TypeError(f"lengths must have an integer dtype, got {lengths.dtype}.")
156156
lengths = lengths.to(torch.int64)
157157
# Varlen kernels need a host max length. Validate the same host copy so the

packed_sequence_asr_encoders_design.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ Opt-in entry points are collision-free:
3030
- `TransformerEncoder.forward_sequence_packed(...)` (also used by `MoETransformerEncoder`).
3131
- `GGEMMTransformerEncoder.forward_all_sequence_packed(...)`: serial THD oracle.
3232
- `GGEMMTransformerEncoder.forward_grouped_sequence_packed(...)`: layer-synchronous grouped THD.
33-
- `ParallelExpertEncoder.forward_sequence_packed(..., grouped=True)`: grouped production default; `grouped=False` is an
34-
explicit numerical/benchmark oracle.
33+
- `ParallelExpertEncoder.forward_sequence_packed(...)`: layer-synchronous grouped production path; the low-level GGEMM
34+
container retains the serial numerical/benchmark oracle.
3535
- `AudioPerceptionModule.forward_sequence_packed(...)`.
3636

3737
Capability selection requires `supports_sequence_packed_output=True` plus the exact method. `packed_encoder_sequences`
@@ -94,8 +94,8 @@ only fields receive defaults during construction/restoration.
9494
the common production case.
9595
- Layer state stores metadata separately from initial packed data, and padded pre-encoder tensors are released before the
9696
Transformer loop.
97-
- Production uses non-strict attention bucketing, allowing old rel-pos, causal, mixed-head-dimension, and mixed-mode
98-
checkpoints to use correct multiple THD buckets. `strict=True` remains an explicit diagnostic asserting one bucket.
97+
- Automatic attention bucketing allows old rel-pos, causal, mixed-head-dimension, and mixed-mode checkpoints to use
98+
the correct number of THD buckets without exposing a diagnostic-only strict mode.
9999
- Unsupported custom experts keep the serial packed capability protocol. Existing legacy
100100
`GGEMMTransformerEncoder.forward_packed` keeps its older head-packed meaning and behavior.
101101

scripts/speech_recognition/benchmark_packed_asr_encoders.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -479,14 +479,17 @@ def _run_iteration(encoder_name, model, inputs, lengths, speaker_targets, implem
479479

480480
def _valid_output(encoder_name, model, inputs, lengths, speaker_targets, implementation):
481481
if encoder_name == "pee":
482-
if implementation in ('serial_thd', 'grouped_thd'):
483-
packed = model.forward_sequence_packed(
482+
if implementation == 'serial_thd':
483+
packed = model._forward_sequence_packed(
484484
inputs,
485485
lengths,
486486
spk_targets=speaker_targets,
487-
grouped=implementation == 'grouped_thd',
487+
grouped=False,
488488
)
489489
return packed.data, packed.lengths
490+
if implementation == 'grouped_thd':
491+
packed = model.forward_sequence_packed(inputs, lengths, spk_targets=speaker_targets)
492+
return packed.data, packed.lengths
490493
output, output_lengths = model(inputs, lengths, spk_targets=speaker_targets)
491494
elif implementation == 'native_thd':
492495
packed = model.forward_sequence_packed(inputs, lengths, bypass_pre_encode=True)

tests/collections/asr/test_packed_pee_grouped.py

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ def test_pee_grouped_and_serial_thd_fusion_match():
6565
lengths = torch.tensor([40, 17])
6666
targets = torch.zeros(2, 5, _N_SPK)
6767
with torch.no_grad():
68-
serial = encoder.forward_sequence_packed(mels, lengths, spk_targets=targets, grouped=False)
68+
serial = encoder._forward_sequence_packed(mels, lengths, spk_targets=targets, grouped=False)
6969
grouped = encoder.forward_sequence_packed(mels, lengths, spk_targets=targets)
7070
assert torch.equal(grouped.lengths, serial.lengths)
7171
assert torch.equal(grouped.cu_seqlens, serial.cu_seqlens)
@@ -240,7 +240,7 @@ def test_grouped_sequence_packed_preserves_independent_ffn_dropout_sites():
240240

241241

242242
@pytest.mark.unit
243-
def test_grouped_sequence_packed_non_strict_buckets_mixed_attention_features():
243+
def test_grouped_sequence_packed_buckets_mixed_attention_features():
244244
speech_config = toy_speech_expert_cfg()
245245
sound_config = toy_sound_expert_cfg()
246246
speaker_config = toy_speaker_expert_cfg()
@@ -265,7 +265,6 @@ def test_grouped_sequence_packed_non_strict_buckets_mixed_attention_features():
265265
signal,
266266
signal_lengths,
267267
fused_qkv=True,
268-
strict=False,
269268
)
270269

271270
for name in encoder.pee.expert_names:
@@ -391,18 +390,6 @@ def forward_sequence_packed(self, audio_signal, length, bypass_pre_encode=False)
391390
container.forward_all_sequence_packed(data, torch.tensor([3]), fused_qkv=True)
392391

393392

394-
@pytest.mark.unit
395-
def test_strict_grouped_sequence_packed_rejects_multiple_attention_buckets():
396-
encoder = build_toy_pe_encoder().eval()
397-
encoder.pee.experts['speaker'].attn_mode = 'causal'
398-
signal, signal_lengths = encoder._prepare_input(
399-
torch.randn(2, _MEL_FEATURES, 32),
400-
torch.tensor([32, 17]),
401-
)
402-
with pytest.raises(ValueError, match='one compatible attention bucket'):
403-
encoder.pee.forward_grouped_sequence_packed(signal, signal_lengths, strict=True)
404-
405-
406393
@pytest.mark.unit
407394
def test_pee_defaults_to_sparse_ragged_grouped_packed_execution():
408395
encoder = build_toy_pe_encoder()

tests/collections/asr/test_packed_transformer_encoder.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ def test_packed_encoder_output_all_empty_is_differentiable():
8484
(torch.tensor([-1, 1]), "between"),
8585
(torch.tensor([3, 1]), "between"),
8686
(torch.tensor([1.0, 1.0]), "integer dtype"),
87+
(torch.tensor([True, False]), "integer dtype"),
8788
],
8889
)
8990
def test_pack_encoder_output_rejects_invalid_lengths(lengths, match):

0 commit comments

Comments
 (0)