Commit b42152d
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
- examples
- audio_finetune
- qwen2_5_omni_asr
- qwen3_omni_asr
- vlm_finetune/phi4
- fern/versions
- nightly/pages/guides
- v0.4/pages/guides
- nemo_automodel/components/datasets
- audio
- vlm
- tests/unit_tests
- datasets
- audio
- vlm
- models/qwen2_5_omni
- tools
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
20 | 20 | | |
21 | 21 | | |
22 | 22 | | |
23 | | - | |
| 23 | + | |
24 | 24 | | |
25 | 25 | | |
26 | 26 | | |
| |||
41 | 41 | | |
42 | 42 | | |
43 | 43 | | |
44 | | - | |
| 44 | + | |
45 | 45 | | |
46 | 46 | | |
47 | 47 | | |
| |||
62 | 62 | | |
63 | 63 | | |
64 | 64 | | |
65 | | - | |
| 65 | + | |
66 | 66 | | |
67 | 67 | | |
68 | 68 | | |
| |||
98 | 98 | | |
99 | 99 | | |
100 | 100 | | |
101 | | - | |
| 101 | + | |
102 | 102 | | |
103 | 103 | | |
104 | 104 | | |
| |||
109 | 109 | | |
110 | 110 | | |
111 | 111 | | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
112 | 139 | | |
113 | 140 | | |
114 | 141 | | |
| |||
251 | 278 | | |
252 | 279 | | |
253 | 280 | | |
| 281 | + | |
| 282 | + | |
| 283 | + | |
| 284 | + | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
| 290 | + | |
| 291 | + | |
| 292 | + | |
| 293 | + | |
| 294 | + | |
| 295 | + | |
| 296 | + | |
| 297 | + | |
| 298 | + | |
| 299 | + | |
| 300 | + | |
| 301 | + | |
| 302 | + | |
| 303 | + | |
| 304 | + | |
| 305 | + | |
| 306 | + | |
| 307 | + | |
| 308 | + | |
| 309 | + | |
| 310 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
451 | 451 | | |
452 | 452 | | |
453 | 453 | | |
454 | | - | |
| 454 | + | |
455 | 455 | | |
456 | 456 | | |
457 | 457 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
71 | 71 | | |
72 | 72 | | |
73 | 73 | | |
74 | | - | |
| 74 | + | |
75 | 75 | | |
76 | 76 | | |
77 | 77 | | |
| |||
91 | 91 | | |
92 | 92 | | |
93 | 93 | | |
94 | | - | |
| 94 | + | |
95 | 95 | | |
96 | 96 | | |
97 | 97 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
74 | 74 | | |
75 | 75 | | |
76 | 76 | | |
77 | | - | |
| 77 | + | |
78 | 78 | | |
79 | 79 | | |
80 | 80 | | |
| |||
94 | 94 | | |
95 | 95 | | |
96 | 96 | | |
97 | | - | |
| 97 | + | |
98 | 98 | | |
99 | 99 | | |
100 | 100 | | |
| |||
Lines changed: 125 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
101 | 101 | | |
102 | 102 | | |
103 | 103 | | |
104 | | - | |
| 104 | + | |
105 | 105 | | |
106 | 106 | | |
107 | 107 | | |
| |||
118 | 118 | | |
119 | 119 | | |
120 | 120 | | |
121 | | - | |
| 121 | + | |
122 | 122 | | |
123 | 123 | | |
124 | 124 | | |
| |||
0 commit comments