Skip to content

Commit b42152d

Browse files
yuekaizhangclaude
andauthored
feat: mixture of mutiple ASR datasets training recipe (#2414)
* chore: ignore local agent tooling/state (.codex, .humanize) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Yuekai Zhang <zhangyuekai@foxmail.com> * feat(datasets): add multi_en ASR recipe and audio datasets package Port result/data/build_train_mix.py into a first-class make_multi_en_asr_dataset builder (six-corpus English mix; torchcodec-free soundfile decode) and reorganize audio/ASR dataset code into a new components/datasets/audio/ package. - New components/datasets/label_utils.py holds the shared label/marker chain (incl. default_stop_tokens), so audio never imports vlm. vlm/collate_fns.py re-imports those names (string-path monkeypatches keep working) and imports the omni-ASR collates from audio to populate COLLATE_FNS (one-directional). - audio/datasets.py: moved make_hf_audio_asr_dataset, make_cv17_dataset, _decode_audio_cell_to_mono_float32, _build_asr_conversation; extracted a reusable _attach_asr_transform. - audio/multi_en.py: make_multi_en_asr_dataset (+ build_multi_en_source_mix), prototype normalize_text, six-source spec, empty-after-normalization filter. - audio/collate_fns.py: moved qwen2_5/qwen3 omni ASR collates + helpers. - examples/audio_finetune/qwen2_5_omni_asr/multi_en_sft_3b.yaml + train_multi_en_qwen2.5_omni_3b.sh (single-node 8-GPU launcher). - Updated all moved-symbol references (YAMLs, docs + Fern mirrors); no shims. - Relocated audio/ASR + label-builder tests; new test_multi_en.py. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Yuekai Zhang <zhangyuekai@foxmail.com> * fix(datasets): address review for multi_en/audio (sources override, cv17 decode, type hints) Round 1 RLCR review fixes for the multi_en ASR / audio package work: - audio/multi_en.py: add `_coerce_source` so `dataset.sources` overrides written in YAML/CLI (passed by the config loader as plain dicts) are accepted, not just `Source` instances. Fixes the `AttributeError: 'dict' object has no attribute 'config'` the launcher's documented `dataset.sources` trim would have hit. - audio/datasets.py: make `make_cv17_dataset` torchcodec-free — cast with `Audio(decode=False)` and decode lazily via soundfile in a `with_transform` callback (no eager `example["audio"]["array"]`), matching the package contract. - Complete public-API type annotations across `audio/*` and `label_utils.py` (modern `X | None` / built-in generics; `processor: Any`); set new-file copyright year to 2026. - Tests: update the CV17 test to assert no decode at construction; add `_coerce_source` + dict-`sources`-override coverage to test_multi_en.py. - Normalize a stray ruff-format slice spacing in test_mock_vlm.py. Verified: 258 affected unit tests pass; ruff/pre-commit clean; a 3-step 8-GPU smoke of train_multi_en_qwen2.5_omni_3b.sh on an ami_ihm train[:256] subset ran to completion (finite losses 0.8390 -> 0.3260 -> 0.3476). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Yuekai Zhang <zhangyuekai@foxmail.com> * docs(datasets): drop forbidden Audio(sampling_rate=...) literal from multi_en docstring Round 2 RLCR review fix for AC-2: the runtime audio path was already torchcodec-free, but the multi_en module docstring still contained the literal forbidden ``Audio(sampling_rate=...)`` callable pattern, which AC-2's negative grep/AST check flags. Reword the sentence (keeping the allowed ``Audio(decode=False)`` reference) and add a regression test asserting no ``Audio(sampling_rate=...)`` / ``Audio(decode=True)`` literal appears in the new audio modules. Verified: rg over nemo_automodel/components/datasets/audio returns no matches; 46 audio + label_utils unit tests pass; ruff/pre-commit clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Yuekai Zhang <zhangyuekai@foxmail.com> * docs(datasets): point qwen3-omni-asr guide import snippet at datasets.audio Round 3 RLCR review fix for AC-8: the qwen3-omni-asr guide's Python snippet still imported make_hf_audio_asr_dataset from the old vlm.datasets path (the earlier reference sweep only matched fully-qualified dotted _target_ strings, not import-form references). Repoint it at nemo_automodel.components.datasets.audio.datasets so a user copying the snippet does not hit ImportError. Verified: dotted, multi-line import-form, single-line import-form, and catch-all greps over the repo (excluding result/) all return no stale moved-symbol references. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Yuekai Zhang <zhangyuekai@foxmail.com> * chore(audio): checkpoint multi_en every 2k steps; revert .gitignore tooling entries - multi_en_sft_3b.yaml: ckpt_every_steps 200 -> 2000. - Restore .gitignore to its prior state (drop the local .codex / .humanize entries). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Yuekai Zhang <zhangyuekai@foxmail.com> * chore(audio): untrack train_multi_en_qwen2.5_omni_3b.sh launcher Remove the launcher script from version control (kept on disk as a local untracked helper) per request. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Yuekai Zhang <zhangyuekai@foxmail.com> * feat(audio): add max_audio_duration_seconds cap to bound activation memory Long clips in the multi_en mix inflate the Whisper feature extractor's input_features and OOM a rank at larger batch sizes (observed: rank OOM at local_batch_size=8 on the AMI+earnings22+voxpopuli mix, which then stalled the other ranks' FSDP all-gather). Add an upper duration bound: - Generalize the duration filter to _filter_audio_duration(min_seconds, max_seconds) (header-only soundfile.info, no decode); add max_audio_duration_seconds to make_hf_audio_asr_dataset and make_multi_en_asr_dataset/build_multi_en_source_mix (multi_en defaults to 30.0s). - multi_en_sft_3b.yaml: set max_audio_duration_seconds=30.0 and local_batch_size=4 for headroom on 80GB. - Add a unit test for the max-duration cap. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Yuekai Zhang <zhangyuekai@foxmail.com> * fix(audio): make multi_en recipe runnable at scale (FSDP grad-sync + timeout) Two fixes found while running the full six-source mix on 8x80GB: - distributed.defer_fsdp_grad_sync: false — with grad accumulation (GBS 64 / LBS 4 / 8 GPUs = 2 microbatches), the bundled torch 2.11 FSDP2 build crashes at the step-0 backward on non-final microbatches (AttributeError: 'FSDPParam' object has no attribute '_unsharded_param'). Syncing gradients every microbatch avoids the buggy deferred-resharding path. - dist_env.timeout_minutes: 10 -> 60 — the first (cold-cache) run materializes and caches the ~500k-sample mix per rank (downloads + per-source maps + the duration filter), serialized by HF's cache lock, which exceeded the 10-min process-group timeout. Pre-warming the HF cache once (single process) makes subsequent launches build in seconds; the larger timeout is a safety margin. Verified: full 6-source run trains past the crash point (loss 0.55 -> 0.11 by ~step 780) with no OOM/timeout/FSDP error. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Yuekai Zhang <zhangyuekai@foxmail.com> * fix(tools): backfill base thinker keys when wrapping NeMo Omni checkpoints wrap_thinker_ckpt_as_omni.py copied only the trained thinker.* shards plus the base talker/token2wav, so any thinker key present in the base but dropped by the NeMo export (e.g. thinker.audio_tower.audio_bos_eos_token.weight on Qwen2.5-Omni) was missing from the wrapped model — vLLM/transformers then refuse to load it ("weights were not initialized from checkpoint"). Now compute thinker keys that exist in base but not in the ckpt and write them from base as a backfill shard. Verified: wrapped Qwen2.5-Omni-3B (multi_en epoch_0_step_6955) loads in vLLM 0.20 and decodes AMI IHM test end-to-end. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Yuekai Zhang <zhangyuekai@foxmail.com> * feat(audio): add qwen3-omni multi_en ASR recipe; extend qwen2.5 run to 3 epochs Add a Qwen3-Omni-30B-A3B six-corpus English ASR recipe (multi_en_sft.yaml), the multi-corpus companion to ami_sft.yaml: same MoE model / FSDP2+EP8 / freeze / optimizer setup, swapping only the dataset block for the multi_en builder and disabling deferred FSDP grad-sync for the grad-accum path. Bump the Qwen2.5-Omni-3B multi_en schedule for the resume run: checkpoint every 4000 steps, 3 epochs, max_steps 30000. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Yuekai Zhang <zhangyuekai@foxmail.com> * refactor(datasets): decouple audio collate from VLM registry and remove label_utils Audio ASR recipes already specify collate_fn via _target_ in YAML, so the Qwen2_5OmniProcessor entry in VLM's COLLATE_FNS was dead code. Remove it together with the _audio_collate_fns cross-import that originally forced the label_utils extraction. Restore label/marker functions inline in vlm/collate_fns.py (matching main) and have audio/collate_fns.py import build_labels_from_template from there directly — no circular import since VLM no longer imports audio. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Yuekai Zhang <zhangyuekai@foxmail.com> * fix(tests): restore default_stop_tokens tests to test_vlm_utils.py The previous refactor moved default_stop_tokens back to vlm/utils.py but forgot to restore its tests in test_vlm_utils.py. Restore them and remove the duplicate copies from test_label_utils.py. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Yuekai Zhang <zhangyuekai@foxmail.com> * refactor(tests): move build_labels tests back to test_collate_fns and delete test_label_utils The label functions are back in vlm/collate_fns.py, so their tests belong in test_collate_fns.py where they originally lived. Remove the now-unnecessary test_label_utils.py file entirely. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Yuekai Zhang <zhangyuekai@foxmail.com> * docs(audio): document multi_en mixture composition and WER results Add a 'Mixture of Datasets: multi_en' section (6-source ~500k-clip English composition, gating notes, sources/max_audio_duration_seconds overrides) and a 'Results: multi_en mixture' section comparing zero-shot base vs the 3-epoch multi_en fine-tune across the 7 open-ASR-leaderboard test subsets (Qwen3-Omni-30B macro 6.24% -> 5.83%; Qwen2.5-Omni-3B 8.97% -> 6.55%). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Yuekai Zhang <zhangyuekai@foxmail.com> * fix(tests): remove stale COLLATE_FNS dispatch test for Qwen2.5-Omni The audio ASR collate was decoupled from the VLM COLLATE_FNS registry in 0ce6a16; selection now goes through the YAML _target_. The dispatch test asserted COLLATE_FNS["Qwen2_5OmniProcessor"] which is no longer registered. The audio collate's actual behavior is covered by test_collate_fn_is_alias_of_qwen3_omni_asr. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Yuekai Zhang <zhangyuekai@foxmail.com> --------- Signed-off-by: Yuekai Zhang <zhangyuekai@foxmail.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 1f4f83e commit b42152d

24 files changed

Lines changed: 2350 additions & 1134 deletions

File tree

docs/guides/audio/qwen3-omni-asr.md

Lines changed: 61 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ The workflow has two stages:
2020

2121
### Built-In Builder: `make_hf_audio_asr_dataset`
2222

23-
`nemo_automodel.components.datasets.vlm.datasets.make_hf_audio_asr_dataset`
23+
`nemo_automodel.components.datasets.audio.datasets.make_hf_audio_asr_dataset`
2424
returns a Hugging Face `Dataset` whose `__getitem__` lazily produces a single
2525
`{"conversation": [...]}` dict suitable for `qwen3_omni_asr_collate_fn`. Key
2626
design points:
@@ -41,7 +41,7 @@ design points:
4141
WenetSpeech out of the box; per-dataset overrides go in the recipe YAML.
4242

4343
```python
44-
from nemo_automodel.components.datasets.vlm.datasets import (
44+
from nemo_automodel.components.datasets.audio.datasets import (
4545
make_hf_audio_asr_dataset,
4646
)
4747

@@ -62,7 +62,7 @@ dataset = make_hf_audio_asr_dataset(
6262

6363
### Built-In Collate: `qwen3_omni_asr_collate_fn`
6464

65-
`nemo_automodel.components.datasets.vlm.collate_fns.qwen3_omni_asr_collate_fn`
65+
`nemo_automodel.components.datasets.audio.collate_fns.qwen3_omni_asr_collate_fn`
6666
batches the lazy samples into model inputs without depending on
6767
`qwen_omni_utils`:
6868

@@ -98,7 +98,7 @@ YAML override snippet for CommonVoice (note `text_column: sentence`):
9898

9999
```yaml
100100
dataset:
101-
_target_: nemo_automodel.components.datasets.vlm.datasets.make_hf_audio_asr_dataset
101+
_target_: nemo_automodel.components.datasets.audio.datasets.make_hf_audio_asr_dataset
102102
path_or_dataset: mozilla-foundation/common_voice_18_0
103103
name: en
104104
text_column: sentence
@@ -109,6 +109,33 @@ dataset:
109109
Audio columns are universally named `audio` across these datasets, so the
110110
default `audio_column="audio"` rarely needs an override.
111111

112+
### Mixture of Datasets: `multi_en`
113+
114+
For a stronger general-purpose English model, train on a **mixture** of public
115+
ASR corpora rather than a single dataset.
116+
`nemo_automodel.components.datasets.audio.multi_en` concatenates several HF
117+
sources into one training set, normalizing each to `{audio, text, source}` with
118+
per-source transcript cleanup (e.g. stripping GigaSpeech bracket tags such as
119+
`<COMMA>` / `<SIL>`). The default English composition is ~500k clips:
120+
121+
| Source | HF repo | config / split | clips |
122+
|---|---|---|---:|
123+
| AMI IHM | `edinburghcstr/ami` | `ihm` / `train` | 108,502 |
124+
| Earnings22 | `sanchit-gandhi/earnings22_split` | `train` | 52,006 |
125+
| VoxPopuli (en) | `facebook/voxpopuli` | `en` / `train` (capped) | 4,000 |
126+
| GigaSpeech (s) | `speechcolab/gigaspeech` | `s` / `train` | 230,068 |
127+
| SPGISpeech (S) | `kensho/spgispeech` | `S` / `train` | 77,073 |
128+
| LibriSpeech | `openslr/librispeech_asr` | `clean` / `train.100` | 28,539 |
129+
| **Total** | | | **~500,188** |
130+
131+
GigaSpeech and SPGISpeech are gated on the Hub — accept their terms (and allow
132+
`trust_remote_code` for GigaSpeech) before launching. The source list is fully
133+
overridable from YAML via `dataset.sources` (pass a trimmed list to drop gated
134+
corpora), and `dataset.max_audio_duration_seconds` caps clip length to bound
135+
activation memory. Ready-to-run recipe:
136+
`examples/audio_finetune/qwen3_omni_asr/multi_en_sft.yaml` (and
137+
`examples/audio_finetune/qwen2_5_omni_asr/multi_en_sft_3b.yaml` for the 3B).
138+
112139
---
113140

114141
## Train
@@ -251,3 +278,33 @@ roughly half:
251278
|-----------------|-------------------------------------------------------|--------------------|
252279
| Before training | Base `Qwen/Qwen3-Omni-30B-A3B-Instruct` (zero-shot) | 15.81% |
253280
| After training | 1 epoch full FT (audio tower trainable) | **8.31%** |
281+
282+
## Results: `multi_en` mixture
283+
284+
Training the same model for 3 epochs on the ~500k-clip `multi_en` mixture (see
285+
[Mixture of Datasets](#mixture-of-datasets-multi_en)) generalizes across all 7
286+
[open-ASR-leaderboard](https://huggingface.co/datasets/hf-audio/open-asr-leaderboard)
287+
English test subsets, not just AMI. WER below is Whisper-normalized
288+
(`EnglishTextNormalizer`), greedy decode, comparing the zero-shot base against
289+
the `multi_en` fine-tune:
290+
291+
| Subset | N | Base (zero-shot) | `multi_en` FT |
292+
|---|---:|---:|---:|
293+
| LibriSpeech test.clean | 2,620 | 1.49 | 1.89 |
294+
| LibriSpeech test.other | 2,939 | 2.62 | 3.54 |
295+
| SPGISpeech | 39,341 | 3.12 | **2.11** |
296+
| VoxPopuli | 1,842 | 7.07 | **6.67** |
297+
| GigaSpeech | 19,931 | 8.54 | 9.46 |
298+
| Earnings22 | 2,741 | 9.79 | **8.89** |
299+
| AMI (IHM) | 12,643 | 11.07 | **8.22** |
300+
| **Macro avg** || 6.24 | **5.83** |
301+
302+
Fine-tuning concentrates its gains on the harder conversational / domain sets
303+
(AMI −2.85, Earnings22 −0.90, SPGISpeech −1.01, VoxPopuli −0.40), while the
304+
strong base keeps a small edge on clean read speech (LibriSpeech, GigaSpeech) —
305+
a hint that the mix can be rebalanced toward those styles. Net macro WER
306+
improves from 6.24% to **5.83%**.
307+
308+
The same recipe on **Qwen2.5-Omni-3B** (`multi_en_sft_3b.yaml`) shows a much
309+
larger fine-tuning gain, since the small model's zero-shot baseline is weaker:
310+
macro WER **8.97% → 6.55%** (−2.42).

docs/guides/dataset-overview.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -451,7 +451,7 @@ Built-in dataset makers (return lists of `conversation` dicts):
451451
- **RDR items**: `nemo_automodel.components.datasets.vlm.datasets.make_rdr_dataset` (HF: `quintend/rdr-items`)
452452
- **CORD-V2 receipts (Consolidated Receipt Dataset for Post-OCR Parsing)**: `nemo_automodel.components.datasets.vlm.datasets.make_cord_v2_dataset` (HF: `naver-clova-ix/cord-v2`)
453453
- **MedPix-VQA (Medical Pixel Question Answering)**: `nemo_automodel.components.datasets.vlm.datasets.make_medpix_dataset`
454-
- **CommonVoice 17 (CV17) (audio)**: `nemo_automodel.components.datasets.vlm.datasets.make_cv17_dataset`
454+
- **CommonVoice 17 (CV17) (audio)**: `nemo_automodel.components.datasets.audio.datasets.make_cv17_dataset`
455455

456456

457457
Each example follows the conversation schema expected by `apply_chat_template`, e.g.:

examples/audio_finetune/qwen2_5_omni_asr/ami_sft_3b.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ loss_fn:
7171
# user( text=user_prompt + audio=<waveform> )
7272
# assistant( text=<transcript> )
7373
dataset:
74-
_target_: nemo_automodel.components.datasets.vlm.datasets.make_hf_audio_asr_dataset
74+
_target_: nemo_automodel.components.datasets.audio.datasets.make_hf_audio_asr_dataset
7575
path_or_dataset: edinburghcstr/ami
7676
name: ihm
7777
split: train
@@ -91,7 +91,7 @@ dataloader:
9191
_target_: torchdata.stateful_dataloader.StatefulDataLoader
9292
num_workers: 2
9393
collate_fn:
94-
_target_: nemo_automodel.components.datasets.vlm.collate_fns.qwen2_5_omni_asr_collate_fn
94+
_target_: nemo_automodel.components.datasets.audio.collate_fns.qwen2_5_omni_asr_collate_fn
9595

9696
optimizer:
9797
_target_: torch.optim.AdamW

examples/audio_finetune/qwen2_5_omni_asr/ami_sft_7b.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ loss_fn:
7474
# user( text=user_prompt + audio=<waveform> )
7575
# assistant( text=<transcript> )
7676
dataset:
77-
_target_: nemo_automodel.components.datasets.vlm.datasets.make_hf_audio_asr_dataset
77+
_target_: nemo_automodel.components.datasets.audio.datasets.make_hf_audio_asr_dataset
7878
path_or_dataset: edinburghcstr/ami
7979
name: ihm
8080
split: train
@@ -94,7 +94,7 @@ dataloader:
9494
_target_: torchdata.stateful_dataloader.StatefulDataLoader
9595
num_workers: 2
9696
collate_fn:
97-
_target_: nemo_automodel.components.datasets.vlm.collate_fns.qwen2_5_omni_asr_collate_fn
97+
_target_: nemo_automodel.components.datasets.audio.collate_fns.qwen2_5_omni_asr_collate_fn
9898

9999
optimizer:
100100
_target_: torch.optim.AdamW
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
# Copyright (c) 2026, NVIDIA CORPORATION. All rights reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
# Full-FT English ASR of Qwen/Qwen2.5-Omni-3B on a six-corpus English mix
16+
# (AMI + earnings22 + voxpopuli_en + gigaspeech + spgispeech + librispeech).
17+
#
18+
# NOTE: gigaspeech and spgispeech are gated on the Hugging Face Hub. Accept
19+
# their dataset terms (and allow trust_remote_code for gigaspeech) before
20+
# launching, or trim the mix by passing a `dataset.sources` subset.
21+
22+
recipe: FinetuneRecipeForVLM
23+
24+
step_scheduler:
25+
global_batch_size: 64
26+
local_batch_size: 4 # long clips inflate audio activations; 4 keeps headroom on 80GB
27+
ckpt_every_steps: 4000
28+
num_epochs: 3
29+
max_steps: 30000 # safety cap (~3 epochs of the ~500k-sample mix at GBS 64)
30+
31+
dist_env:
32+
backend: nccl
33+
# Generous timeout: the first run materializes + caches the ~500k-sample mix
34+
# (per-rank, serialized by the HF cache lock). Pre-warm the cache once (single
35+
# process) so subsequent launches build in seconds. See the guide.
36+
timeout_minutes: 60
37+
38+
rng:
39+
_target_: nemo_automodel.components.training.rng.StatefulRNG
40+
seed: 1111
41+
ranked: true
42+
43+
model:
44+
_target_: nemo_automodel.NeMoAutoModelForMultimodalLM.from_pretrained
45+
pretrained_model_name_or_path: Qwen/Qwen2.5-Omni-3B
46+
backend:
47+
_target_: nemo_automodel.components.models.common.BackendConfig
48+
attn: sdpa
49+
linear: te
50+
rms_norm: torch_fp32
51+
rope_fusion: false
52+
enable_hf_state_dict_adapter: true
53+
54+
checkpoint:
55+
enabled: true
56+
checkpoint_dir: result/checkpoints/qwen2_5_omni_asr_multi_en
57+
model_save_format: safetensors
58+
save_consolidated: true
59+
60+
distributed:
61+
strategy: fsdp2
62+
tp_size: 1
63+
cp_size: 1
64+
pp_size: 1
65+
sequence_parallel: false
66+
# GBS 64 / (LBS 4 x 8 GPUs) = 2 grad-accum microbatches. The bundled torch 2.11
67+
# FSDP2 build crashes in the deferred-grad-sync path on non-final microbatches
68+
# (AttributeError: 'FSDPParam' has no attribute '_unsharded_param'), so sync
69+
# gradients every microbatch instead of deferring.
70+
defer_fsdp_grad_sync: false
71+
72+
# Full fine-tune: audio tower + language model trainable; vision tower frozen
73+
# (we never feed images on these English ASR corpora).
74+
freeze_config:
75+
freeze_embeddings: false
76+
freeze_vision_tower: true
77+
freeze_audio_tower: false
78+
freeze_language_model: false
79+
80+
loss_fn:
81+
_target_: nemo_automodel.components.loss.masked_ce.MaskedCrossEntropy
82+
83+
# Conversation shape (per sample):
84+
# user( text=user_prompt + audio=<waveform> )
85+
# assistant( text=<transcript> )
86+
#
87+
# The builder owns the six-source list, the prototype-faithful transcript
88+
# normalization, and the torchcodec-free soundfile decode; it needs no
89+
# path_or_dataset / name here.
90+
dataset:
91+
_target_: nemo_automodel.components.datasets.audio.multi_en.make_multi_en_asr_dataset
92+
sampling_rate: 16000
93+
system_prompt: null
94+
user_prompt: >-
95+
Transcribe the following English audio verbatim. Output only the raw
96+
transcript text with no leading or trailing commentary.
97+
# Qwen-Omni's Whisper feature extractor has a known off-by-one between
98+
# ``input_features`` and ``feature_attention_mask`` on sub-second clips.
99+
# Filtering at 1.0 s costs only the header-only sf.info probe per sample.
100+
min_audio_duration_seconds: 1.0
101+
# Cap clip length so long-audio batches don't OOM a rank (the audio encoder's
102+
# input_features scale with duration). Header-only probe; no decode.
103+
max_audio_duration_seconds: 30.0
104+
105+
dataloader:
106+
_target_: torchdata.stateful_dataloader.StatefulDataLoader
107+
num_workers: 2
108+
collate_fn:
109+
_target_: nemo_automodel.components.datasets.audio.collate_fns.qwen2_5_omni_asr_collate_fn
110+
111+
optimizer:
112+
_target_: torch.optim.AdamW
113+
betas: [0.9, 0.95]
114+
eps: 1e-8
115+
lr: 2.0e-5
116+
weight_decay: 0.0
117+
118+
wandb:
119+
project: ${oc.env:WANDB_PROJECT,qwen2_5-omni-asr-multi-en}
120+
name: ${oc.env:WANDB_NAME,qwen2_5_omni_3b_asr_multi_en_fullft}
121+
dir: ${oc.env:WANDB_DIR,result/wandb}
122+
123+
ci:
124+
recipe_owner: yuekaizhang
125+
time: "00:30:00"

examples/audio_finetune/qwen3_omni_asr/ami_sft.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ loss_fn:
101101
# assistant( text=<transcript> )
102102
# No system turn (system_prompt: null).
103103
dataset:
104-
_target_: nemo_automodel.components.datasets.vlm.datasets.make_hf_audio_asr_dataset
104+
_target_: nemo_automodel.components.datasets.audio.datasets.make_hf_audio_asr_dataset
105105
path_or_dataset: edinburghcstr/ami
106106
name: ihm
107107
split: train
@@ -118,7 +118,7 @@ dataloader:
118118
_target_: torchdata.stateful_dataloader.StatefulDataLoader
119119
num_workers: 2
120120
collate_fn:
121-
_target_: nemo_automodel.components.datasets.vlm.collate_fns.qwen3_omni_asr_collate_fn
121+
_target_: nemo_automodel.components.datasets.audio.collate_fns.qwen3_omni_asr_collate_fn
122122

123123
optimizer:
124124
_target_: torch.optim.AdamW

0 commit comments

Comments
 (0)