NeMo Speech 中 ASR 模型微调实战:speech_to_text_finetune.py 的初始化、换词表与关键配置解析
【免费下载链接】SpeechA scalable generative AI framework built for researchers and developers working on Large Language Models, Multimodal, and Speech AI (Automatic Speech Recognition and Text-to-Speech)项目地址: https://gitcode.com/GitHub_Trending/nem/Speech
本篇指南基于 NeMo Speech 仓库中的微调文档(docs/source/asr/fine_tuning.rst)与配套脚本、配置文件展开,讲清在 NeMo 中对预训练 ASR 模型做领域/语言适配微调的完整路径:何时该微调、如何用 speech_to_text_finetune.py 一条命令启动训练、三种权重初始化方式的区别、更换分词器时模型内部的联动逻辑,以及 HuggingFace 数据集直连微调的参数配置。读完本文,你可以直接复制仓库内的默认配置与命令行参数完成一次可复现的 ASR 微调实验,并理解每个关键参数在源码层面的作用。
何时选择微调而非从头训练
文档给出的适用场景有三类:
- 拥有领域特定数据(医疗、法律、呼叫中心等),希望在该领域上提升识别准确率;
- 需要适配新的口音、说话风格或声学环境;
- 想基于预训练的多语言模型增加对某个新语言的支持。
如果你手上有一个大规模、多样化的数据集并打算从零开始训练,则不走微调入口,而是使用完整的训练配置(见文档中的 Configuration Files 章节,对应 docs/source/asr/configs.rst)。脚本 speech_to_text_finetune.py 的 docstring 也明确定位了自己:它用于"在核心架构不变、可能更换分词器"的前提下微调任意类型的现有 ASR 模型;如果还要修改模型架构,官方建议改用speech_to_text_rnnt/ctc_*.py等主训练脚本。
微调脚本:一条命令启动
文档推荐的调用方式,使用默认配置examples/asr/conf/asr_finetune/speech_to_text_finetune.yaml:
python examples/asr/speech_to_text_finetune.py \ --config-path=../conf/asr_finetune \ --config-name=speech_to_text_finetune \ init_from_pretrained_model="nvidia/parakeet-tdt-0.6b-v2" \ model.train_ds.manifest_filepath=/path/to/train_manifest.json \ model.validation_ds.manifest_filepath=/path/to/val_manifest.json \ trainer.devices=1 \ trainer.max_epochs=50必须且只能指定init_from_pretrained_model(NGC/HuggingFace 模型名)与init_from_nemo_model(本地.nemo路径)二者之一来加载预训练权重。从源码结构看,这一约束在 speech_to_text_finetune.py 的get_base_model中强制校验:两者同时给出会抛出ValueError,两者都为空同样报错。加载路径分两种:
- 本地
.nemo文件走ASRModel.restore_from(restore_path=...),直接把检查点还原为模型实例; - 预训练模型名走
ASRModel.from_pretrained(model_name=...)。多卡场景下有一段值得注意的工程细节(speech_to_text_finetune.py):为避免集群上首次下载模型时各 rank 并发拉取,rank 0 负责下载,其余 rank 睡眠至少 60 秒(exp_manager.seconds_to_sleep可配置,最小 60s)后再从缓存加载。
main的完整训练流水线是(speech_to_text_finetune.py):
- 构建
pl.Trainer(经resolve_trainer_cfg解析 trainer 配置)并挂上exp_manager(提供日志与 checkpoint 回调); get_base_model还原基座模型;check_vocabulary检查并按需更新词表/分词器;setup_dataloaders依次调用setup_training_data、setup_multiple_validation_data、setup_multiple_test_data;setup_optimization按model.optim配置优化器与调度器;- 若配置了
model.spec_augment,用ASRModel.from_config_dict重建增广模块; trainer.fit开始训练。
另有一个历史限制:该脚本出于"一个脚本适配所有模型类型"的考虑,只支持上述两种初始化,传入init_from_ptl_ckpt会直接NotImplementedError(speech_to_text_finetune.py)。
三种权重初始化方式
从预训练模型名初始化(NGC/HuggingFace)
init_from_pretrained_model: "nvidia/parakeet-tdt-0.6b-v2"从本地 .nemo 检查点初始化
init_from_nemo_model: "/path/to/checkpoint.nemo"默认的 speech_to_text_finetune.yaml 中init_from_nemo_model默认为null,两个初始化入口均需在命令行或配置中显式给定。
部分加载:选择性保留/排除模型组件
当更换 decoder 架构或分词器、但要保留预训练 encoder 时,可以按文档使用include/exclude列表指定要加载的模型组件:
init_from_nemo_model: "/path/to/checkpoint.nemo" init_from_nemo_model_include: - encoder - preprocessor init_from_nemo_model_exclude: - decoder需要说明的是,当前仓库的 speech_to_text_finetune.py 与默认配置中并未显式解析include/exclude这两个键,文档将其作为初始化选项给出;从源码结构看,部分加载能力更直接地体现在下面要讲的换词表流程——change_vocabulary系列方法本身就是一种"encoder 保留、decoder/joint 重建"的选择性加载。若在实际版本中未观察到 include/exclude 生效,可回退到下一节的分词器/词表方案达到同等效果。
更换分词器:同一词表直接微调,不同词表重建输出层
同一分词器(相同词表):无需任何特殊处理,直接微调。此时check_vocabulary会走logging.info("Reusing the vocabulary from the pre-trained model.")分支。
新分词器(不同词表):例如为新语言或新领域更换分词器时,文档给出两步:
- 在配置中提供新分词器目录;
- 排除初始化的 decoder/joint(Transducer 模型)或最终的线性层(CTC 模型):
model: tokenizer: dir: /path/to/new/tokenizer type: bpe init_from_nemo_model: "/path/to/pretrained.nemo" init_from_nemo_model_exclude: - decoder - joint默认配置里对应的是model.tokenizer.update_tokenizer: true+dir/type三个键(speech_to_text_finetune.yaml),type支持bpe(SentencePiece)与wpe(WordPiece);对字符级模型则用model.char_labels.update_labels+labels列表,且两者不能同时开启(脚本中会抛错,见 speech_to_text_finetune.py)。
脚本内部的update_tokenizer函数(speech_to_text_finetune.py)实现了关键的"词表变化探测"逻辑:先记录旧 decoder/joint 的state_dict与新词表大小,调用asr_model.change_vocabulary(...)后比较vocab_size——若词表大小改变,则警告"将以新词表继续微调,decoder 将被重新初始化";若词表大小未变,则把先前保存的 decoder(以及 joint,如果模型有的话)权重回填,避免预训练输出层被无谓重置。而各模型类的change_vocabulary实现(如 rnnt_bpe_models.py、ctc_bpe_models.py、aed_multitask_models.py)的 docstring 一致强调:该方法只改变 decoder(及 joint/loss),encoder 与前端预处理模块保持不变——这正是"保留预训练声学特征、只重训语言侧"的实现基础。多语言场景下type还可以取agg,直接使用AggregateTokenizer的目录式配置(见下文 Tips)。
微调后强制单语言解码(抑制 phonetic drift)
文档指出:对多语言的EncDecMultiTaskModel(如 Canary)在单一语言上微调后,推理时模型仍可能出现 phonetic drift——一句话中途切换输出语言。解决办法是在解码时把source_lang与target_lang显式设为同一语言:
results = model.transcribe( audio=["audio.wav"], source_lang="de", target_lang="de", )这与推理文档 docs/source/asr/inference.rst 中 "Enforcing a Single Language" 一节的建议一致:双语向设置为同一语言后,模型只按该语言转录,从而消除中途切语言的现象。
使用 HuggingFace 数据集微调
NeMo 支持直接从 HuggingFace 加载数据集(注意:当前不支持与 Lhotse dataloader 组合使用)。文档给出的命令形如:
python examples/asr/speech_to_text_finetune_with_hf.py \ --config-path=<path to config directory> \ --config-name=<config name> \ model.train_ds.hf_data_cfg.path="mozilla-foundation/common_voice_11_0" \ model.train_ds.hf_data_cfg.name="en" \ model.train_ds.hf_data_cfg.split="train" \ model.validation_ds.hf_data_cfg.path="mozilla-foundation/common_voice_11_0" \ model.validation_ds.hf_data_cfg.name="en" \ model.validation_ds.hf_data_cfg.split="validation"对应配置模板是 speech_to_text_hf_finetune.yaml,其要点值得逐一了解:
hf_data_cfg支持ListConfig或DictConfig,参数直接透传给 HuggingFace 的load_dataset();默认示例按 LibriSpeech 的train.clean.360/100、train.other.500、validation.other、test.clean/other组织,使用 Common Voice 时按文档替换path/name/split即可;audio_key/sample_rate_key/text_key指定 HF 数据集中音频、采样率、文本字段,支持用.访问嵌套字段(如audio.array),不同数据集的文本键可能是sentence或text;- 文本清洗:
normalize_text: true默认转小写并只保留字母数字,symbols_to_keep列出需要保留的符号(默认["'"]); streaming: true开启流式读取(不等数据全部下载,但首 epoch 每步更慢,且需要改用trainer.max_steps+trainer.limit_train_batches代替trainer.max_epochs)。
底层实现位于 nemo/collections/asr/data/huggingface/,该功能也有对应的功能性测试脚本 tests/functional_tests/Optional_ASR_dev_run_Speech_To_Text_HF_Finetuning.sh。需要提醒:当前仓库examples/asr/目录下并未包含文档中提到的speech_to_text_finetune_with_hf.py入口脚本,实际使用时以仓库当前提供的入口为准,或参照上述 YAML 自行以speech_to_text_finetune.py同款 Hydra 方式挂接 HF 数据配置。
关键配置参数详解
文档列出的最重要参数与默认配置的对应关系如下(默认值取自 speech_to_text_finetune.yaml):
| 参数 | 说明 | 默认配置中的值 |
|---|---|---|
trainer.max_epochs | 微调 epoch 数(领域适配通常 50-100) | 50 |
model.optim.lr | 学习率(应低于从头训练,典型 1e-4~1e-5) | 1e-4 |
model.train_ds.manifest_filepath | 训练 manifest 路径(NeMo JSON 格式) | ???(必填占位符) |
model.train_ds.batch_size | 每 GPU batch size | 16(显存允许可调大) |
init_from_pretrained_model | NGC/HF 预训练模型名 | 无(命令行给定) |
init_from_nemo_model | 本地 .nemo 文件路径 | null |
默认配置中与调优效果直接相关的其余参数:
- 优化器:
adamw,betas: [0.9, 0.98],weight_decay: 1e-3;调度器为CosineAnnealing,warmup_steps: 5000、min_lr: 5e-6。注意文档 Tips 中的提醒:若预训练配置用的是 Noam(warmup + decay)调度器,微调时应覆盖为恒定或 cosine-annealing 调度,避免 warmup 阶段把学习率重新拉高; - 数据侧:
max_duration: 20/min_duration: 0.1过滤过短/过长音频,支持 tarred 数据集(is_tarred+tarred_audio_filepaths)与 bucketing(bucketing_strategy); - 增广:
spec_augment默认开启freq_masks: 2、time_masks: 10(置零即关闭),作用于SpectrogramAugmentation模块; - trainer:
devices: -1表示用满所有 GPU,precision: 32(可选 16/bf16),val_check_interval: 1.0(设 0.25 可每 epoch 验证 4 次); - 实验管理:
exp_manager默认开启 TensorBoard logger 与 checkpoint 回调,监控指标为val_wer(min 模式)、save_top_k: 5、always_save_nemo: True(每个 PTL checkpoint 同时保存 .nemo 文件);enable_checkpointing/logger在 trainer 里置 false,因为由 exp_manager 统一提供。
实践建议(Tips)
- 从低学习率开始:过高的学习率会破坏预训练特征,微调典型区间为 1e-4 到 1e-5;配合前述 Noam 调度器的覆盖建议使用。
- 使用 Lhotse 数据加载:获得基于动态 batching 的高效训练(对应文档 Lhotse Dataloading 章节)。
- 微调时保留 spec augmentation:提升模型对声学扰动的鲁棒性(对应文档 Augmentation Configurations 章节,默认配置中
freq_masks: 2/time_masks: 10)。 - 多语言微调使用多语言分词器:文档指出 NeMo 支持两种方案——统一的 SentencePiece 多语言 BPE 模型(覆盖所有目标语言的单一模型,Canary v2/Flash 所用),以及
AggregateTokenizer(type: "agg")组合多个单语言分词器并按语言路由,配置形如:
model: ... tokenizer: type: "agg" # aggregate tokenizer langs: en: dir: "<path to the directory that contains the tokenizer files>" type: "bpe" es: dir: "<path to the directory that contains the tokenizer files>" type: "bpe"agg模式下每个语言关联一个预训练分词器,按列出顺序分配 token id 区间;训练时需在 manifest 中填写lang字段用于样本路由,推理时则按推断出的 token id 区间路由(见 docs/source/asr/configs.rst 中 Aggregate tokenizer 小节)。
小结
NeMo Speech 的微调路径围绕 speech_to_text_finetune.py 与 speech_to_text_finetune.yaml 这对入口展开:两种且仅两种权重初始化方式、由change_vocabulary家族实现"保留 encoder、重建 decoder/joint"的换词表机制、面向 HF 数据集的hf_data_cfg配置、以及 adamw + CosineAnnealing + spec_augment 的默认训练配方。结合文档给出的单语言强制解码与多语言分词器方案,可以覆盖从领域适配到新语言扩展的绝大多数 ASR 微调需求。深入完整配置项时,可继续参考 docs/source/asr/configs.rst。
【免费下载链接】SpeechA scalable generative AI framework built for researchers and developers working on Large Language Models, Multimodal, and Speech AI (Automatic Speech Recognition and Text-to-Speech)项目地址: https://gitcode.com/GitHub_Trending/nem/Speech
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考