Skip to content

Commit 3b58f76

Browse files
committed
Simplify packed PEE production controls
1 parent 3e98ad2 commit 3b58f76

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
@@ -512,7 +512,6 @@ def forward_grouped_sequence_packed(
512512
backend: str = 'baddbmm',
513513
moe_mode: str = 'dense',
514514
fused_qkv: bool = False,
515-
strict: bool = False,
516515
) -> Dict[str, PackedEncoderOutput]:
517516
"""Run all experts in layer lockstep using native THD grouped kernels.
518517
@@ -528,7 +527,6 @@ def forward_grouped_sequence_packed(
528527
backend=backend,
529528
moe_mode=moe_mode,
530529
fused_qkv=fused_qkv,
531-
strict=strict,
532530
)
533531

534532
def forward_grouped(
@@ -1324,7 +1322,6 @@ def _forward_grouped_sequence_packed(
13241322
backend: str,
13251323
moe_mode: str,
13261324
fused_qkv: bool,
1327-
strict: bool,
13281325
) -> Dict[str, PackedEncoderOutput]:
13291326
if backend not in GROUPED_GEMM_BACKENDS:
13301327
raise ValueError(f"Unknown grouped-GEMM backend '{backend}'; expected one of {GROUPED_GEMM_BACKENDS}.")
@@ -1394,24 +1391,6 @@ def _forward_grouped_sequence_packed(
13941391
}
13951392
del prepared, padded, packed, output_lengths, shared_mask
13961393

1397-
if strict:
1398-
incompatibilities = []
1399-
if any(expert.self_attention_model == 'rel_pos' for expert in encs.values()):
1400-
incompatibilities.append('relative-position attention')
1401-
if len({expert.d_model // expert.n_heads for expert in encs.values()}) != 1:
1402-
incompatibilities.append('head dimensions')
1403-
if len({expert.attn_mode for expert in encs.values()}) != 1:
1404-
incompatibilities.append('attention modes')
1405-
if len({state[name]['metadata_key'] for name in self.expert_names}) != 1:
1406-
incompatibilities.append('packed sequence boundaries')
1407-
if len({(state[name]['x'].device, state[name]['x'].dtype) for name in self.expert_names}) != 1:
1408-
incompatibilities.append('devices/dtypes')
1409-
if incompatibilities:
1410-
raise ValueError(
1411-
"Strict grouped sequence-packed execution requires one compatible attention bucket; "
1412-
f"incompatible {', '.join(incompatibilities)}."
1413-
)
1414-
14151394
trace = {
14161395
'mode': 'grouped_thd',
14171396
'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
@@ -883,25 +883,21 @@ def forward_sequence_packed(
883883
length,
884884
spk_targets=None,
885885
return_experts: bool = False,
886-
*,
887-
grouped: bool = True,
888886
) -> PackedEncoderOutput | tuple[PackedEncoderOutput, dict[str, object]]:
889887
"""Encode offline while keeping expert Transformer activations token-flat.
890888
891889
Online/windowed inference retains its established prefix/cache path. Existing
892890
:meth:`forward` remains the Conformer-compatible padded API.
893891
894892
Set ``return_experts=True`` to also return packed speech/sound states and
895-
the padded Sortformer speaker predictions. ``grouped=False`` retains a
896-
serial THD numerical/benchmark oracle; production execution is grouped.
893+
the padded Sortformer speaker predictions. Production execution is always
894+
layer-synchronous and grouped; the low-level container retains a serial oracle.
897895
"""
898896
if self.online_inference_enabled:
899897
raise RuntimeError(
900898
"forward_sequence_packed is an offline API and cannot run while online_inference() is enabled."
901899
)
902-
return self._forward_sequence_packed(
903-
audio_signal, length, spk_targets, return_experts=return_experts, grouped=grouped
904-
)
900+
return self._forward_sequence_packed(audio_signal, length, spk_targets, return_experts=return_experts)
905901

906902
def train(self, mode: bool = True) -> "ParallelExpertEncoder":
907903
"""Set training mode, but keep frozen experts in eval.
@@ -1070,7 +1066,6 @@ def _forward_all_sequence_packed_training(self, audio_signal, length):
10701066
'backend': self.sequence_packed_ggemm_backend,
10711067
'moe_mode': self._sequence_packed_moe_execution_mode(),
10721068
'fused_qkv': True,
1073-
'strict': False,
10741069
}
10751070
if not self.activation_checkpointing or not torch.is_grad_enabled():
10761071
return grouped_forward(audio_signal, length, **grouped_kwargs)
@@ -1507,7 +1502,6 @@ def _forward_sequence_packed(self, audio_signal, length, spk_targets=None, *, re
15071502
backend=self.sequence_packed_ggemm_backend,
15081503
moe_mode=self._sequence_packed_moe_execution_mode(),
15091504
fused_qkv=True,
1510-
strict=False,
15111505
)
15121506

15131507
_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)