Skip to content

Commit a890d75

Browse files
Audio: make FLEURS notebook CUDA-portable
1 parent 6abf897 commit a890d75

6 files changed

Lines changed: 156 additions & 345 deletions

File tree

benchmarking/scripts/audio_fleurs_benchmark.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ def run_audio_fleurs_benchmark( # noqa: PLR0913, PLR0915
116116
audio_filepath_key="audio_filepath",
117117
batch_size=16,
118118
fail_on_audio_error=True,
119+
adapter_kwargs={"use_cuda_graph_decoder": False},
119120
).with_(resources=Resources(gpus=gpus))
120121
)
121122
pipeline.add_stage(

nemo_curator/models/asr/nemo_asr.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
from __future__ import annotations
1818

1919
import gc
20+
from copy import deepcopy
2021
from dataclasses import dataclass, field
2122
from numbers import Integral
2223
from typing import Any
@@ -71,6 +72,10 @@ class NeMoASRAdapter:
7172
enable_local_attention: Convert a compatible FastConformer checkpoint
7273
from global to local attention after loading.
7374
local_attention_context_size: Left and right local-attention context.
75+
use_cuda_graph_decoder: Override NeMo's RNNT CUDA-graph decoder. Leave
76+
as ``None`` to preserve the checkpoint default. Set to ``False``
77+
on GPU/driver combinations that do not support NeMo's label-loop
78+
CUDA graph implementation.
7479
refresh_cache: Forward NeMo's checkpoint cache refresh flag.
7580
strict: Forward NeMo's strict checkpoint loading flag.
7681
"""
@@ -80,6 +85,7 @@ class NeMoASRAdapter:
8085
verbose: bool = False
8186
enable_local_attention: bool = False
8287
local_attention_context_size: tuple[int, int] = (128, 128)
88+
use_cuda_graph_decoder: bool | None = None
8389
refresh_cache: bool = False
8490
strict: bool = True
8591
_model: Any = field(default=None, init=False, repr=False)
@@ -94,6 +100,9 @@ def __post_init__(self) -> None:
94100
if not isinstance(self.enable_local_attention, bool):
95101
msg = "NeMoASRAdapter.enable_local_attention must be a boolean"
96102
raise TypeError(msg)
103+
if self.use_cuda_graph_decoder is not None and not isinstance(self.use_cuda_graph_decoder, bool):
104+
msg = "NeMoASRAdapter.use_cuda_graph_decoder must be a boolean or None"
105+
raise TypeError(msg)
97106
try:
98107
context_size = tuple(self.local_attention_context_size)
99108
except TypeError as exc:
@@ -132,6 +141,8 @@ def load_model(self, *, num_gpus: int) -> None:
132141
model = self._load_checkpoint(device)
133142
if self.enable_local_attention:
134143
self._configure_local_attention(model)
144+
if self.use_cuda_graph_decoder is not None:
145+
self._configure_rnnt_cuda_graph_decoder(model)
135146
self._model = model
136147

137148
def _configure_local_attention(self, model: Any) -> None: # noqa: ANN401
@@ -154,6 +165,23 @@ def _configure_local_attention(self, model: Any) -> None: # noqa: ANN401
154165
)
155166
change_subsampling_chunking(1)
156167

168+
def _configure_rnnt_cuda_graph_decoder(self, model: Any) -> None: # noqa: ANN401
169+
"""Override the CUDA-graph setting on a compatible NeMo RNNT decoder."""
170+
from omegaconf import open_dict
171+
172+
change_decoding_strategy = getattr(model, "change_decoding_strategy", None)
173+
model_cfg = getattr(model, "cfg", None)
174+
decoding_cfg = getattr(model_cfg, "decoding", None)
175+
greedy_cfg = getattr(decoding_cfg, "greedy", None)
176+
if not callable(change_decoding_strategy) or decoding_cfg is None or greedy_cfg is None:
177+
msg = f"NeMo checkpoint {self.model_id!r} does not expose a configurable RNNT decoder"
178+
raise TypeError(msg)
179+
180+
decoding_cfg = deepcopy(decoding_cfg)
181+
with open_dict(decoding_cfg.greedy):
182+
decoding_cfg.greedy.use_cuda_graph_decoder = self.use_cuda_graph_decoder
183+
change_decoding_strategy(decoding_cfg=decoding_cfg)
184+
157185
def unload_model(self) -> None:
158186
"""Release worker-local model and CUDA cache state."""
159187
self._model = None

tests/models/asr/test_nemo_asr.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
import numpy as np
2323
import pytest
24+
from omegaconf import OmegaConf
2425

2526
from nemo_curator.models.asr import nemo_asr
2627
from nemo_curator.models.asr.base import ASRAdapter
@@ -95,6 +96,25 @@ def test_load_model_configures_local_attention_when_enabled() -> None:
9596
model.change_subsampling_conv_chunking_factor.assert_called_once_with(1)
9697

9798

99+
@pytest.mark.parametrize("enabled", [False, True])
100+
def test_load_model_configures_rnnt_cuda_graph_decoder_when_requested(enabled: bool) -> None:
101+
adapter = NeMoASRAdapter(use_cuda_graph_decoder=enabled)
102+
model = _mock_model([])
103+
model.cfg = OmegaConf.create({"decoding": {"strategy": "greedy_batch", "greedy": {}}})
104+
105+
with patch.object(adapter, "_load_checkpoint", return_value=model):
106+
adapter.load_model(num_gpus=0)
107+
108+
decoding_cfg = model.change_decoding_strategy.call_args.kwargs["decoding_cfg"]
109+
assert decoding_cfg.strategy == "greedy_batch"
110+
assert decoding_cfg.greedy.use_cuda_graph_decoder is enabled
111+
112+
113+
def test_nemo_adapter_rejects_invalid_cuda_graph_decoder_value() -> None:
114+
with pytest.raises(TypeError, match="use_cuda_graph_decoder must be a boolean or None"):
115+
NeMoASRAdapter(use_cuda_graph_decoder="false") # type: ignore[arg-type]
116+
117+
98118
def test_transcribe_batch_uses_one_exact_nemo_batch() -> None:
99119
model = _mock_model([SimpleNamespace(text="alpha"), SimpleNamespace(text="beta")])
100120
adapter = NeMoASRAdapter(num_workers=2)

tutorials/audio/fleurs/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ python tutorials/audio/fleurs/main.py \
8181
| `data_split` | FLEURS split: `train`, `dev`, or `test` |
8282
| `wer_threshold` | Keep samples with `wer_pct ≤` this value (default: `5.5`) |
8383
| `stages.1.model_id` | NeMo ASR model for inference |
84+
| `stages.1.adapter_kwargs.use_cuda_graph_decoder` | RNNT CUDA-graph decoder override. The FLEURS hybrid-RNNT default is `false` for broad driver compatibility. |
8485
| `stages.1.resources.gpus` | GPUs for ASR (`0` for CPU) |
8586
| `backend` | `xenna` (default) or `ray_data` |
8687

@@ -233,6 +234,7 @@ finally:
233234
|---|---|---|
234235
| Output directory already exists | Previous run left `${raw_data_dir}/result/${lang}/` | Remove the directory before re-running |
235236
| OOM during ASR inference | GPU VRAM too small for model + batch | Reduce `stages.1.batch_size` or use a smaller model |
237+
| `CUDA error: invalid argument` in RNNT label-loop decoding | NeMo CUDA-graph decoder is unsupported by the local CUDA runtime/driver combination | Set `stages.1.adapter_kwargs.use_cuda_graph_decoder=false` (the supplied FLEURS config already does this) |
236238
| CPU inference very slow | CPU is 10–50x slower than GPU | Set `stages.1.resources.gpus=1`; CPU is only for testing |
237239
| Empty output JSONL | `wer_threshold` too strict for the model+language pair | Increase `wer_threshold` or use a better-matching ASR model |
238240
| HuggingFace download fails | Network/auth issue | Check connectivity; some splits may need `huggingface-cli login` |

0 commit comments

Comments
 (0)