Skip to content

Commit d41d1c9

Browse files
committed
fun_asr_nano: opt-in scoped SDPA backends and compiled decoder for fine-tuning
1 parent 486b4b7 commit d41d1c9

5 files changed

Lines changed: 87 additions & 0 deletions

File tree

‎examples/industrial_data_pretraining/fun_asr_nano/docs/finetune.md‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,13 @@ For more detailed parameters, refer to: [SenseVoice Model Training and Testing](
8787
bash finetune.sh
8888
```
8989

90+
### Training-speed options
91+
92+
Two `llm_conf` keys, both off by default; `finetune.sh` turns them on.
93+
94+
- `++llm_conf.torch_compile=true` runs the LLM decoder stack through `torch.compile(dynamic=True)` for inputs on a CUDA GPU (a CPU decoder and single-sequence batches stay eager); the first compiled step pays a one-time compile.
95+
- `++llm_conf.sdpa_backends=[flash,efficient,math]` runs the LLM forward under `torch.nn.attention.sdpa_kernel` with these attention backends. It matters on sm_90 / sm_100 GPUs, where torch prefers cuDNN and cuDNN builds an execution plan for every new batch shape. The flags it sets are process-wide while the forward runs (restored on return), so a thread running attention concurrently in the same process sees the same selection; leave it unset when several models share one process.
96+
9097
### Recommended Configuration
9198

9299
- For training data less than 1000 hours, it is recommended to fine-tune the audio_adaptor.

‎examples/industrial_data_pretraining/fun_asr_nano/finetune.sh‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,4 +62,6 @@ ${train_tool} \
6262
++audio_encoder_conf.freeze=true \
6363
++audio_adaptor_conf.freeze=true \
6464
++llm_conf.freeze=false \
65+
++llm_conf.torch_compile=true \
66+
++llm_conf.sdpa_backends=[flash,efficient,math] \
6567
++output_dir="${output_dir}" &> ${log_file}

‎examples/industrial_data_pretraining/fun_asr_nano/model.py‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
normalize_checkpoint_state,
2121
)
2222
from funasr.models.fun_asr_nano.device_utils import resolve_autocast_device_type
23+
from funasr.models.fun_asr_nano.llm_forward_opts import configure_llm_forward
2324

2425

2526
from ctc import CTC
@@ -93,6 +94,11 @@ def __init__(
9394

9495
self.llm_dtype = llm_conf.get("llm_dtype", "fp32")
9596
self.llm = model.to(dtype_map[self.llm_dtype])
97+
# Opt-in training-speed switches, both off by default (finetune.sh turns them on):
98+
# llm_conf.sdpa_backends sets the process-wide SDPA backend flags while this forward
99+
# runs and restores them on return; llm_conf.torch_compile compiles the decoder stack
100+
# for inputs on a CUDA device (a CPU decoder stays eager). See llm_forward_opts.py.
101+
configure_llm_forward(self.llm, llm_conf)
96102
llm_dim = model.get_input_embeddings().weight.shape[-1]
97103

98104
# adaptor
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
"""Opt-in switches for the Fun-ASR-Nano LLM forward, read from ``llm_conf``; both default to off.
2+
3+
``llm_conf.sdpa_backends``: a list of ``flash`` / ``efficient`` / ``math``. The decoder forward runs
4+
under ``torch.nn.attention.sdpa_kernel`` with these backends: the process-wide SDPA flags are set on
5+
entry and restored on return, so another thread calling scaled_dot_product_attention meanwhile sees
6+
the same selection (leave it unset when several models share one process concurrently).
7+
``llm_conf.torch_compile``: run the decoder stack through ``torch.compile(dynamic=True)`` for
8+
inputs on a CUDA device, decided per call; a CPU decoder and a 1-sequence batch stay eager.
9+
"""
10+
11+
import torch
12+
13+
SDPA_BACKENDS = {"flash": "FLASH_ATTENTION", "efficient": "EFFICIENT_ATTENTION", "math": "MATH"}
14+
15+
16+
def resolve_sdpa_backends(names):
17+
"""``llm_conf.sdpa_backends`` (a list of names, or ``None``) -> ``SDPBackend`` members."""
18+
if names is None:
19+
return None
20+
from torch.nn.attention import SDPBackend # a pybind enum: getattr, not subscript
21+
22+
backends = []
23+
for name in names:
24+
member = SDPA_BACKENDS.get(name.lower())
25+
if member is None:
26+
raise ValueError(
27+
f"llm_conf.sdpa_backends: unknown backend {name!r}, "
28+
f"choose from {sorted(SDPA_BACKENDS)}"
29+
)
30+
backends.append(getattr(SDPBackend, member))
31+
return backends
32+
33+
34+
def configure_llm_forward(llm, llm_conf):
35+
"""Install the switches ``llm_conf`` asks for on ``llm.model.forward`` (nothing by default)."""
36+
backends = resolve_sdpa_backends(llm_conf.get("sdpa_backends", None))
37+
compile_requested = bool(llm_conf.get("torch_compile", False))
38+
if backends is None and not compile_requested:
39+
return
40+
41+
decoder = llm.model
42+
forward = eager = decoder.forward
43+
44+
if compile_requested:
45+
compiled = torch.compile(eager, dynamic=True)
46+
47+
def forward(*args, **kw):
48+
# Per call, not at construction: the trainer builds the model on the CPU and moves it
49+
# to the GPU afterwards. A 1-sequence batch would get its own specialised graph.
50+
x = kw.get("inputs_embeds", kw.get("input_ids"))
51+
if x is None and args:
52+
x = args[0]
53+
if x is None or x.device.type != "cuda" or x.shape[0] == 1:
54+
return eager(*args, **kw)
55+
return compiled(*args, **kw)
56+
57+
if backends is not None:
58+
from torch.nn.attention import sdpa_kernel
59+
60+
inner = forward
61+
62+
def forward(*args, **kw):
63+
with sdpa_kernel(backends): # wraps the compiled call, so Dynamo traces under it
64+
return inner(*args, **kw)
65+
66+
decoder.forward = forward

‎funasr/models/fun_asr_nano/model.py‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
from .ctc import CTC
2525
from .checkpoint_utils import disable_incomplete_ctc, normalize_checkpoint_state
2626
from .device_utils import resolve_autocast_device_type
27+
from .llm_forward_opts import configure_llm_forward
2728
from .tools.utils import forced_align
2829

2930
dtype_map = {"bf16": torch.bfloat16, "fp16": torch.float16, "fp32": torch.float32}
@@ -127,6 +128,11 @@ def __init__(
127128

128129
self.llm_dtype = llm_conf.get("llm_dtype", "fp32")
129130
self.llm = model.to(dtype_map[self.llm_dtype])
131+
# Opt-in training-speed switches, both off by default (finetune.sh turns them on):
132+
# llm_conf.sdpa_backends sets the process-wide SDPA backend flags while this forward
133+
# runs and restores them on return; llm_conf.torch_compile compiles the decoder stack
134+
# for inputs on a CUDA device (a CPU decoder stays eager). See llm_forward_opts.py.
135+
configure_llm_forward(self.llm, llm_conf)
130136
llm_dim = model.get_input_embeddings().weight.shape[-1]
131137

132138
# lora: inject LoRA adapters into the LLM target Linear layers

0 commit comments

Comments
 (0)