LabHub
开始
学习 学习路径 课程

语音 AI 智能体 — 会听、会查、会说的流水线

转写 — 流式 Transducer 与 Whisper,以及 WER 掩盖了什么

在 LabHub 中继续学习

一句话总结

流式 ASR 在声音输入的同时不断修正部分结果,等话说完、经过一小段收尾之后,给出最终结果。离线 ASR(Whisper)必须接收到整段语句才能开始。准确度用 WER 来衡量——(替换 + 删除 + 插入)÷ 正确答案的单词数——而这个数字会在很大程度上受评估数据与模型训练数据有多相似的影响。

为什么需要它

语音助手的回答,是在“话说完了”这个判定之后才开始的(模块 2)。如果在那一刻识别结果已经基本出来了,就可以立即进入下一步;如果从那时才开始识别,就要按语句长度成比例地等待。这个差别就是使用流式的原因。同时,部分结果也是表示“正在听”的信号,并且是提前开始检索的依据。

不过,选择模型时最先抓住的那个数字 WER,隐藏的东西比想象的多。我们在为这个镜像挑选模型时,就真实地遇到过这样的事。最初选中的 20M 流式模型在公开评估中表现正常,但输入本课程的查询之后,它把流的最开头 1–2 秒整个丢掉了——“How late can I cancel without paying the fee?”变成了“LE WITH THAT PAIN THE FEET”。同一个文件接连拼接两次时,第二遍是准确的。这是平均 WER 看不出来的失败,也是更换模型的原因(记录在 labs/voice/Dockerfile 中)。

工作原理

Transducer(RNN-T)。这个镜像的流式模型是 icefall 的 zipformer transducer(用 LibriSpeech 训练,Apache-2.0)。编码器接收声音片段并生成表示,joiner 决定“在这个时间点输出什么(或者什么也不输出)”。编码器被训练成只看固定大小的片段和有限的左侧上下文(模型名称中的 chunk-16 · left-128),所以不等待未来,每个片段都会给出结果。因此会产生部分结果,而最后一个片段则在后面加上一小段静音(本课程中是 0.66 秒)来收尾。这个收尾只需几十 ms——在这个镜像中,8 条语句“从最后的声音到最终结果”的中位数是 44 ms(Mac 上的 Docker,2 个 CPU)。

Whisper。OpenAI 的 Whisper(Radford et al., 2022)是用 68 万小时的网络数据训练的编码器—解码器模型。它把声音转换成 30 秒窗口的对数梅尔频谱图输入编码器,再由解码器一个 token 一个 token 地写出文字。由于是看完整段语句之后才写,所以输出带有标点和大小写,但必须话说完才能开始。在相同条件下,一条语句花了 734 ms(中位数)。我们放入的是最小的 tiny.en(3,900 万参数)的 int8 版本。

部分结果会抖动。流式模型也会根据后面的上下文来修正前面的词。如果把部分结果显示在界面上,用户就会看到文字在变化。所以通常只把“稳定的前半部分”加粗显示,或者对像检索这样难以撤回的事情,只用最终结果来做。

WER 与规范化。WER 是以单词为单位的编辑距离。比较之前必须先做规范化——大小写、标点、撇号。如果不规范化就比较“Sunday?”和“SUNDAY”,评分的就不是模型,而是书写规则。语料库级 WER 不是逐文件 WER 的平均值,而是错误数之和 ÷ 单词数之和。这是为了避免一条很短的语句 100% 的错误毁掉平均值。

WER 取决于数据。把在这个镜像中测得的三个数字并排放在一起,就很清楚了。在 LibriSpeech test-clean 的 8 条语句上,流式 zipformer 的 WER 是 0.95%,Whisper tiny.en 是 6.7%——因为 zipformer 正是用那个语料库训练的。但是在用合成语音制作的 12 条电话查询(模块 9)上,同一个 zipformer 错了将近 20%(“GUYS”→“GUISE”,“refill”→“ORIFYL”)。评估数据与训练数据越相似,数字看起来就越好。选择模型时,必须用与你的服务相似的数据来测量。

采样率要如实。sherpa-onnx 接收到不是 16 kHz 的声音时,会自己进行转换。把 8 kHz 的电话录音如实告知为 8000,在这份实验数据上 WER 是 0%;但如果谎称是 16000,声音会快一倍,100% 都错了。噪声则不同。在同样的 8 条语句中混入白噪声,SNR 为 20 dB 时是 0%,10 dB 时是 2.9%,0 dB 时升到了 24.8%。

在现场相遇的样子

两遍识别(two-pass)。常见的做法是:用流式模型立即显示部分结果,话说完之后,再用更大的离线模型重新转写一遍来修正最终结果。第二遍只使用延迟预算所允许的部分。LabHub 口语练习的 ASR 是在录音结束时调用一次加载到 GPU 的大模型,权重被卸载时,第一次请求是 78.7 秒,已加载时是 0.4 秒——所以在占到位置的那一刻,就发送静音来提前加载(backend/app/lang_talk.py 的 warm_asr)。使用大模型时,“预热”就成了设计的一部分。

下一项实验要做什么

制作 WER 计算器,接受隐藏用例的测试,并把 LibriSpeech 的 8 条语句按每次 100 ms 以流式方式转写,记录部分结果和最终结果的延迟。再用 Whisper 转写同样的语句,比较 WER 和延迟,测量 8 kHz 电话频段、谎报采样率的情况,以及不同噪声强度下的 WER,然后在报告中写下会选择哪种配置。