15. 语音识别微调:口音、术语与噪声
上一篇看了通用语音识别模型怎样训练出来。把 Whisper 这样的模型接进资料助手后,问题很快出现:同事说“钉钉审批怎么走”,转写成“盯盯审批怎么走”;“这季度的 OKR”变成“这季度的 OK 啊”;项目代号“青鸾”被写成“清澜”;在会议室开着空调、隔着两米远说话,错字明显变多;带口音的同事说得越快,识别越差。这一篇先判断哪些问题根本不用训练,然后讲需要微调时怎样准备数据、写转写规范、做数据扩增、选择训练方式,最后讲怎样评估才能同时证明“变好了”和“没变坏”。读完你应该能拿到一批错例,判断该用热词、后处理还是微调,并设计出一套能发现回退和幻觉的测试集。
先给错误分类,再决定改哪一层
把上线后收集到的错例按原因分组,会发现它们落在不同的层:
| 错例 | 原因 | 优先的改法 |
|---|---|---|
| “钉钉”写成“盯盯”,“青鸾”写成“清澜” | 少量固定词,模型听对了音,选错了字 | 热词或上下文提示 |
| “二零二六年”与“2026年”混用,标点缺失 | 输出格式,不是听错 | 文本规整(ITN)与后处理 |
| 某种口音整体错字多,会议室远场错字多 | 声学条件和训练数据差距大 | 微调 |
| 某部门的专有说法成片出现、热词几十上百个 | 领域语言分布不同 | 先热词,不够再微调 |
热词(也叫上下文偏置)在解码时给指定词条加分,模型参数不变。实现方式取决于模型:Transducer 模型可在 beam search 搜索时给热词路径加分,推理系列的语音与多模态推理演示过 sherpa-onnx 的这条路线;FunASR 的 Paraformer 接口可以直接传热词;openai/whisper 的 transcribe() 有 initial_prompt 参数,官方说明可以用它提供自定义词汇和专有名词,让模型更可能写对。文本规整(ITN,Inverse Text Normalization)把“二零二六年三月五号”转换成“2026年3月5日”,属于识别之后的文字处理。热词怎样召回、怎样防止误插入,见流式语音实践里的共享热词召回与请求隔离。
这两层改动快、可回滚、不影响其他词,应当先试。它们解决不了的是声学层面的问题:模型压根没听清,候选里就没有正确答案,加分也选不出来。热词太多时还会互相干扰,把普通词强行拉向热词。当错误集中在口音、噪声、远场这些条件上,或者领域词多到热词表维护不过来,才轮到微调。
图的右半边是一个循环:每一轮评估都要回答两个问题,业务场景有没有变好、通用能力有没有变坏。没通过时先做错误分析,判断是数据缺、标注乱还是学过头,再决定补数据还是改规范,而不是直接加大轮数。
采集什么样的录音
微调数据要像模型上线后真正听到的声音。资料助手的用户在工位上用手机说、在会议室对着桌面麦克风说,有人带口音、有人中英混说。照着稿子在安静房间里朗读的录音容易采集,但语速均匀、没有“嗯”“那个”、没有犹豫和重说,和真实口语差别很大。只用朗读数据微调,模型在朗读测试集上分数漂亮,上线后改善有限。实际做法常是两者结合:用朗读稿件定向覆盖术语表里的每个词,再收集一批真实场景的录音覆盖说话方式和环境。
声音能识别出具体的人,采集前要取得说话人的明确同意,说明录音用于训练识别模型、保存多久、谁能访问;真实会议录音里还可能有薪资、绩效这类敏感内容,要先筛查再进入训练集。员工撤回同意时,要能定位并删除对应的录音和派生数据。
数据怎样切分同样关键。同一个人的声音既出现在训练集又出现在测试集,测试分数会偏高,因为模型记住的是这个人的嗓音。应按说话人划分训练集和测试集,口音切片里的测试说话人一个都不进训练集。
需要多少小时,取决于目标场景和模型原有能力的差距,没有通用答案。Whisper large-v3 官方模型卡链接的微调指南只用了约 5 小时数据做演示,这只说明流程能跑通;口音和噪声差距越大,需要的数据越多。更可靠的做法是分批:先用少量数据训练一轮,看业务测试集的错误率随数据量怎样下降,曲线变平时再加数据的收益就很小了。
转写规范比数据量更容易出问题
同一句话,不同转写员可能写出:
text
转写员甲:这季度 OKR 在钉钉审批里提交,截止到 3 月 5 号。
转写员乙:这季度okr在钉钉审批里提交 截止到三月五号两份都“没错”,但混在一起训练,模型会在两种写法之间摇摆;拿乙的写法当参考答案去评估甲风格的输出,又会算出一堆假错误。转写规范要在采集前写好,至少规定:
- 数字:读作“三月五号”时写成汉字还是阿拉伯数字。一种常见做法是转写时按读音写汉字,数字格式统一交给 ITN,这样声学模型只学“听”,格式问题单独可控。
- 中英混说:英文缩写的大小写(OKR)、中英文之间是否留空格、读成中文的英文怎样写。
- 术语表:“钉钉审批”“青鸾”等固定写法,转写员遇到时查表而不是凭听感写。
- 口语现象:“嗯”“啊”这些语气词和重复、改口要不要写。
- 标点:是否标注,用哪几种。
规范定好后,让两名转写员独立转写同一批音频,计算两份转写之间的字错误率作为一致率;不一致的地方往往就是规范没写清楚的地方。
用合成语音补术语:有效但要掺真数据
真实录音里,“青鸾”可能一年只出现几次。一个便宜的办法是用语音合成(TTS)把包含术语的句子读出来,配上文本作为训练数据。这能定向覆盖稀有词,但有几个陷阱:
- 合成语音太干净、太规整,没有真实的噪声、口音和语速变化,模型可能学会“合成腔里的青鸾”,换成真人说话仍然听不出;
- TTS 自己也可能读错术语,比如把 OKR 读成一个单词,错误读音会作为“标准答案”进入训练;
- 合成数据比例过高时,模型会偏向合成声音的分布,真实语音上的表现反而下降。
所以合成数据只作为补充,混入真实录音一起训练,再叠加下一节的扩增;测试集只用真人录音,否则测出来的是模型对合成声音的适应程度。
数据扩增:让每条录音变成多条
扩增在训练时对音频做变换,生成“同一句话的不同版本”,标注文本不变:
- 速度扰动:把音频按 0.9、1.0、1.1 倍速各做一份,训练数据变成 3 倍。Ko 等人 2015 年的论文在四个大词汇量识别任务上报告平均相对提升 4.3%,实现几乎没有成本,成了语音训练的标配。
- SpecAugment(2019):直接在 log-Mel 特征上随机遮住几条连续的频带和几段连续的时间,并轻微扭曲时间轴。模型被迫不依赖某个频段或某一瞬间,对噪声和缺失更稳。
- 加噪声和混响:把空调、键盘、人声嘈杂等噪声按不同信噪比叠加到干净录音上;再与房间冲激响应(RIR)卷积,模拟会议室里声音从墙面反射回来的效果。资料助手的会议室场景正需要这一项。
扩增只用于训练集。测试集要保持原样,它的作用是代表真实输入。
训练方式:全参、冻结一部分,还是 LoRA
Whisper large 约 15.5 亿参数。按第 03 篇的口径,混合精度 AdamW 每参数约 16 字节:
text
全参数训练状态 ≈ 15.5 亿 × 16 字节 ≈ 24.8 GB(不含激活)一张 80 GB 的卡放得下,但 24 GB 的消费级显卡放不下。选择不只是显存问题,还取决于要改的是哪一层能力:
| 方式 | 改动的部分 | 适合 |
|---|---|---|
| 全参数微调 | 编码器和解码器 | 数据多、差距大,能承担遗忘风险 |
| 冻结编码器,只训解码器 | 文字生成习惯 | 听得清但写不对:术语、格式、领域说法 |
| 冻结解码器,只训编码器 | 声学表示 | 口音、噪声、远场让模型听不清 |
| LoRA | 注意力里的低秩旁路 | 显存有限、想保留多个场景的适配器 |
Hugging Face PEFT 的官方指南演示了用 LoRA 微调 Whisper large-v2:底座以 8-bit 加载,LoRA 秩 r=32,只作用在注意力的 q_proj 和 v_proj 上。可训练参数可以手算核对:
text
Whisper large:编码器 32 层自注意力,解码器 32 层自注意力 + 32 层交叉注意力 = 96 个注意力块
每块 2 个投影 × r × (输入维度 + 输出维度) = 2 × 32 × (1280 + 1280) = 163,840
96 × 163,840 = 15,728,640 个参数,占全部约 15.6 亿参数的约 1.01%和指南里打印的 trainable params: 15728640 一致。LoRA 的原理、秩和目标模块怎样选,见第 10 篇。
学习率要比预训练时小得多,轮数也少,具体值用业务验证集和通用验证集一起选。原因是灾难性遗忘:几小时的公司录音会把模型往这一小块分布上拉,学习率大、轮数多时,它会把会议室里的“钉钉”学好,同时忘掉其他场景的能力,比如英文识别变差、普通话新闻也开始出错。缓解办法是在训练数据里混入一部分通用语音、控制学习率和轮数,并且每轮都测通用集。训练曲线上,训练损失一路下降、业务验证集的错误率却开始回升,就是过拟合的信号,判断方法见第 05 篇。
评估:同时证明变好和没变坏
中文语音识别常用字错误率(CER,Character Error Rate),英文用词错误率(WER)。把模型输出和参考文本对齐,统计替换、删除、插入的字数:
text
CER = (替换 S + 删除 D + 插入 I) ÷ 参考文本字数 N
参考:钉钉审批怎么走(N = 7)
输出:盯盯审批怎么走
S = 2,D = 0,I = 0 → CER = 2 ÷ 7 ≈ 28.6%两个字的错误让这句话的 CER 接近 29%,但整个测试集上,几千句里的几个术语错误只让总 CER 变化零点几个百分点。总体 CER 会掩盖术语问题,所以要单独统计术语召回:测试集里所有术语出现的位置中,被正确写出的比例;同时统计误插入:没说某个术语的地方被写成了术语,热词加分过高时尤其常见。
算分前要先规范化文本,否则格式差异会被当成错误。Whisper 的论文专门为此写了文本规范器,指出 WER 会惩罚无害的格式差异,前面“2026”与“二零二六”的写法之争就属于这一类。比较两个模型时,必须用同一套规范化规则。
一套完整的测试集至少包括:
- 业务测试集:真人在实际场景的录音,说话人不进训练集,算 CER 和术语召回;
- 切片:按口音、噪声环境(工位、会议室、手机外放)分别算分,平均值好看可能只是某个切片大幅改善、另一个变差;
- 通用测试集:公开或自建的通用普通话、英文录音,微调前后对比,检查遗忘;
- 静音与纯噪声片段:参考文本为空,任何输出都算错误。
录音回放和逐层对照的工程做法,见流式语音实践的音频回放与质量回归。
失败案例:静音里冒出一句话
资料助手的会议模式里,有人反馈转写稿中凭空出现了一句没人说过的话,位置恰好是一段十几秒的冷场。这不是偶发故障。Whisper 的论文在局限一节写到,模型会出现与音频完全无关的“完全幻觉”,也会陷入重复循环;large-v3 模型卡同样提示输出可能包含音频里没说的内容。2024 年的研究 Careless Whisper 统计发现,约 1% 的转写含有整句幻觉,而且更集中出现在停顿、非语音时长较长的说话人身上。注意力编解码模型的解码器本质上是语言模型,没有可听的内容时,它会按语言习惯继续“写下去”。
微调可能让这个问题变好,也可能变坏。如果训练片段都是切好的、满满的人声,从不包含静音和纯噪声,模型就没有学过“这里什么都没说”该输出什么。判断和缓解的方法:
- 测试集里专门放静音和纯噪声片段,统计非空输出的比例,微调前后对比;
- 识别前用 VAD(语音活动检测)切掉无人声的部分,这是最直接的防线;
- openai/whisper 提供了几个相关参数:
no_speech_threshold与logprob_threshold配合判断“这段没有语音”;关掉condition_on_previous_text可以降低陷入重复循环的概率;hallucination_silence_threshold(需开启词级时间戳)在疑似幻觉时跳过长静音; - 训练数据里保留一部分只有背景声、标注为空的片段。
听错公司里的词,先分清是固定词选错字、输出格式不统一,还是声学条件让模型听不清:前两类用热词和文本规整,后者才微调。微调数据要像真实输入,按说话人划分,转写规范先于采集;合成语音能补稀有术语,但只作补充,测试只用真人录音;速度扰动、SpecAugment、加噪声和混响让少量数据发挥更大作用。训练方式按要改的能力选择,LoRA 用约 1% 的参数就能适配 Whisper,小学习率和混入通用数据用来防遗忘。评估同时看业务 CER、术语召回、各切片、通用集和静音幻觉。识别听准了,资料助手还要把回答用统一的声音播报出来,下一篇看语音合成怎样训练。
