LabHub
开始
学习 学习路径 课程

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

何时回答 — VAD·话轮结束判定·插话

在 LabHub 中继续学习

一句话总结

语音助手什么时候开口,由话说完了的判定(端点,endpointing)来决定。VAD(语音活动检测)逐帧判断声音是不是说话,而轮次判定则用“说话停止了多久”把 VAD 区间合并成轮次。停顿阈值(gap)设得短,就会打断说话的中途,设得长,回答就会晚。如果在助手说话期间用户插话(barge-in),必须先去掉扬声器的声音回传到麦克风形成的回声,才能看见人的声音。

为什么需要它

人与人对话时,轮到下一个人说话的间隔很短。一项测量了 10 种语言对话的研究(Stivers et al., 2009, PNAS)报告说,回答开始之前的间隔大体集中在 0–200 ms 附近。人在对方说完之前,就已经预测到了结尾。机器做不到,只有确认安静下来之后才知道结束了。那段确认时间,就是回答的最小延迟——无论把模型做得多快,都不会减少(在模块 8 中单独测量这一部分)。

也不是一个阈值就能解决的。在“我预约了周二……(思考的停顿)……想改成周四”中,停顿可能超过 0.5 秒。如果在那里断开,助手就会去回答“我预约了周二”。

工作原理

能量 VAD。如果帧的 dBFS 大于阈值(例如 -35),就当作说话。它快,又容易解释,但所有的大声音都算说话。实验中的通话,在 6 秒处放进了敲门的短噪声,能量 VAD 会把它当成一个说话区间(误报)。反过来,说话小声就会漏掉。

神经网络 VAD(silero)。小型神经网络逐帧(16 kHz 下是 512 个采样点,32 ms)给出“是说话的概率”,概率超过 threshold 就算说话,安静超过 min_silence_duration 就关闭区间。这个镜像中的 silero VAD(onnx,MIT)处理 16.7 秒的声音,在 CPU 2 核的 Pod 上只用了 56 ms(nuc1 实测)。在实验数据中,它没有把敲门当成说话。不过,它关闭区间的时刻,比实际话尾晚 0.5 秒左右(在这份数据中实测为 0.5–0.7 秒)——因为它把话尾的余音和呼吸声也当作说话。这个延迟同样会原样加到回答延迟上。

合并为轮次。VAD 每遇到停顿就切断一个区间。轮次,是把这些区间中间隔短于 gap 的连接起来得到的。如果把 gap 设为 0.2 秒,在思考的停顿处也会切断轮次(打断说话的中途),设为 1.2 秒,则会把下一个人的发言也连成同一个轮次,或者每次都要等一秒以上。在实验中,遍历 gap 从 0.2 到 1.2 秒,找出轮次数正确的范围,并测量其中最短值的回答延迟——(VAD 看到的话尾 + gap)− 实际话尾。

在实际工作中,还要加上语义信号。根据 ASR 的部分结果是否以完整的句子结束(“……想改成”)、语调是否下降,来缩短或延长 gap。流式 ASR 模型也有端点规则——sherpa-onnx 的 rule2 的含义是“识别出内容之后,安静 N 秒就结束”。

打断与回声。助手说话期间,麦克风里不仅有人的声音,还有从扬声器漏进来的助手自己的声音。如果就这样运行 VAD,一开始播放就会判断为“有人插话”,自己把话打断了。解决办法是利用我们知道自己发出了什么声音(参考信号)这一点。把麦克风看作 = 人声 + g·(延迟 d 之后的参考信号),在人开口之前的区间里,求出互相关最大的 d 和最小二乘比例 g,再把它减掉,剩下的就是人的声音。真正的回声消除器(AEC)是连房间的反射也能跟踪的自适应滤波器,在浏览器中通过 getUserMedia 的 echoCancellation 约束来开启。实验中的回声是用一个延迟、一个比例做成的简单模型,所以只看原理。

在现场相遇的样子

LabHub 的口语练习把端点判定交给人来做——按下再松开录音按钮的方式(static/lang/talk.js 的 MediaRecorder)。不会出错,实现也简单,但要等人说完话之后按下按钮,然后整段录音才被传送。像电话那样不用手的语音助手,必须由机器来做这个判定,这时本模块中的权衡就出现了。

确定阈值时最常见的错误,是照着一段录音死记硬背。只看本课程的两段通话,VAD 区间之间的间隔,在一段通话中是 0.29 秒(说话中途)和 1.06 秒(换人说话),区分得很宽,但在另一段通话中,换人说话处的间隔窄到了 0.73 秒。如果用一个文件选定 0.74 秒,在另一个文件中,两个人的发言就会被连成一个轮次。所以规则要在没见过的数据上测试,在生产环境中,则要把被转交的轮次和被打断的轮次收集成样本,重新审视阈值。

下一项实验要做什么

在通话 call_a 上,把能量 VAD 和 silero VAD 的区间与标准答案重叠,统计误报和漏检。制作把 VAD 区间合并成轮次的 turns.py,由评分器在第一次见到的通话上测试它是否正确,并遍历 gap,测量轮次正确的范围和回答延迟。最后,在混有回声的麦克风上估计延迟和比例并减掉,找出人插话的时刻。