VAD 是什么:ASR 前面为什么总要挂个断句器
VAD(Voice Activity Detection,语音活动检测)是判断一段音频里哪些时间段有人在说话的模块,它的产物是一组语音片段的起止时间,作用是把连续不断的音频流切成识别器能处理的一句一句。
在语音识别的四个环节里,VAD 属于最前面的音频前处理。它不认字、不管说了什么,只回答一个二元问题:这一小段是不是人声。但这个看起来简单的判断,决定了后面所有环节吃到的输入长什么样。
VAD 到底在替你省什么
VAD 承担三个职责,重要性从容易理解到容易被忽略排列。
第一,省算力。 一段会议录音、一通客服电话,真正在说话的时间往往只占一部分,剩下的是等待、翻页、走神。静音段送进识别模型,模型照样跑完整的前向计算,出来的却是空结果。提前挡掉它们,是最便宜的一笔优化。
第二,给识别器合理的输入长度。 识别模型对输入长度是敏感的:太长会撞上注意力开销和训练时的长度分布,太短则缺少上下文。VAD 按人说话的自然停顿来切,切出来的片段长度天然贴近”一句话”这个粒度,比按固定秒数硬切合理得多。长音频该怎么切,详见长音频要怎么切才不吞字。
第三,为对话系统提供”用户说完了没有”的信号。 这一条最容易被忽略,但在实时语音对话里是最要命的。
第三个职责:轮次结束的判定
离线转写场景下,VAD 切得早一点晚一点,最多影响转写质量。但在实时对话里,VAD 说”这句结束了”这个瞬间,就是系统开始回应的发令枪。
链路是这样的:VAD 判定用户说完 → 片段交给识别器出文字 → 文字送进模型生成回复 → 合成语音播出去。用户感受到的”这助手反应好慢”,很大一部分其实是在等 VAD 拍板,不是模型算得慢。
这就产生了一个绕不开的两难:
- VAD 判得早,用户中间只是喘了口气、想了个词,系统就抢话打断了。
- VAD 判得晚,用户说完了要干等一会儿,对话节奏黏滞。
这个矛盾没有通用最优解,因为它取决于业务里”抢话”和”迟钝”哪个代价更高。填表播报类的场景可以判得晚一点求稳,闲聊陪伴类的场景则要判得早、并且允许被打断。这也是为什么流式识别和整段识别在 VAD 上的要求完全不同。
两类 VAD 的适用边界
按判断方式,VAD 大致分两类。
能量阈值型:算一算当前帧的能量(有时再加过零率一类的简单统计量),超过阈值就算有人说话。它的优点是几乎不吃算力,实现简单,在安静环境下够用。
它的致命弱点是:在稳态噪声下就会失效。空调外机、风扇、马路上的持续车流、机房的底噪——这类噪声能量稳定且不低,一旦超过阈值,VAD 会认为”一直有人在说话”,于是永远切不出一句的结尾,整个断句机制形同虚设。反过来把阈值调高,小声说话的部分又会被当成静音丢掉。
神经网络型:用一个小模型判断当前帧是不是语音。它学的是语音的谱结构特征,而不只是响度,因此对噪声鲁棒得多,能在有底噪的环境里稳定区分人声与非人声。代价是要跑推理,有额外的计算开销——这类模型通常很小,但在极端受限的端侧设备上仍需要算账。
开源侧的可选项是有的:Silero VAD 自述为预训练的语音活动检测器;pyannote.audio 把语音活动检测列为说话人分离的神经网络构件之一;sherpa-onnx 的能力清单里也包含 VAD。这三者定位不同,前者是专做 VAD 的独立组件,后两者是把 VAD 作为更大工具箱的一部分。
三个关键参数各自管什么
这里只讲参数的作用和调它的后果。具体默认值各实现不同,而且会随版本变,照抄别处的数字没有意义,要以你所用系统的文档为准。
静音时长阈值:连续多久没检测到语音,才判定这一句结束。这是 VAD 里影响最大的一个参数,上一节说的那个两难,主要就体现在它身上。
最短语音时长:短于这个长度的语音段直接丢弃。它的用途是过滤咳嗽、清嗓、敲键盘、鼠标点击这类瞬时噪声——这些声音在能量上可能很像语音的起始,但持续时间极短,用时长门槛滤掉最省事。
前后 padding:在检测到的语音起点之前、终点之后各多留一点音频再切,给检测本身的延迟和误差留余量。人说话的第一个音往往是辅音或轻声,能量爬升需要时间,等 VAD 反应过来时前面已经过去了一小截,padding 就是把这一截补回来。
调参后果表
把上面三个参数和常见症状对起来,就是一张可以照着排查的表:
| 你调的 | 调小会出现 | 调大会出现 |
|---|---|---|
| 静音时长阈值 | 一句话被切成好几段,识别结果支离破碎;实时对话里频繁抢话打断用户 | 用户说完要干等,响应迟钝;两句话甚至两个人的话粘成一段送进识别器 |
| 最短语音时长 | 咳嗽、按键、椅子响都被当成一句送进识别,产出一堆无意义短文本 | 真实的短应答被吞掉,“嗯""好""对”这类词整个消失 |
| 起始 padding | 开头的字被吃掉,“你好”变成”好” | 每句前面带一小段静音或前一句的尾音,多余但通常无害 |
| 结束 padding | 句尾字被截断,尾音丢失 | 片段变长,浪费一点算力;静音阈值判定也会跟着显得更迟 |
用法:先描述症状,在表里反查是哪个参数偏了哪个方向,一次只动一个,改完用同一批音频回归对比。
四种典型故障的排查方向
- 开头字被吃掉。首选查起始 padding 是否不足;其次是 VAD 触发本身偏迟,如果用的是能量阈值型,把阈值调低一点看看是否缓解。
- 一句话被切成好几段。静音时长阈值定得太短,说话人正常的换气和停顿被当成了句子结束。
- 两句话粘成一段。静音时长阈值太长,或者环境底噪把 VAD 顶在”一直有人说话”的状态上——后者是能量阈值型的典型症状,换神经网络型 VAD 通常直接解决。
- 背景人声误触发。这是 VAD 能力边界之外的问题:VAD 判断的是”有没有人在说话”,不是”有没有目标人在说话”。旁边同事的交谈、电视里的对白,在 VAD 看来都是货真价实的语音。要区分说话人身份得靠说话人相关的模块,而不是把 VAD 调严——调严通常会把目标说话人也一起滤掉。
顺带说一句:如果系统自己在放声音(比如 TTS 正在播报),那部分声音必须靠回声消除在拿得到参考信号的位置提前对消掉,指望 VAD 分辨”这是我自己放的声音”是不现实的。详见降噪和回声消除该放在哪。
常见问题
问:VAD 和端点检测(endpointing)是一回事吗?
关系很近但侧重不同。VAD 是逐帧的二元判断——这一帧是不是语音;端点检测是在 VAD 输出之上做的决策——积累到什么程度才宣布”一句结束”。很多实现把两者打包在一个模块里,所以常混着叫,但排查时区分开有好处:判错帧和判错端点,要动的旋钮不一样。
问:识别模型自己不能断句吗,为什么还要单独一个 VAD?
有些模型确实能输出断句和标点,但那是在拿到音频之后做的事,救不回前面三个职责里的任何一个:静音段照样被送进模型算了一遍,输入长度照样不受控,实时对话里”用户说完了没有”这个信号更是必须在识别之前就拿到——不然回应永远慢一拍。
问:我该选能量阈值型还是神经网络型?
先看你的噪声环境是不是稳态的。如果目标场景里有持续底噪(车载、工厂、开放办公区、带风扇的设备旁),能量阈值型基本可以直接排除。如果是安静的近场录音、且算力极度受限,能量阈值型仍然是划算的选择。
问:参数调好了,换个场景还要重调吗?
大概率要。噪声环境、说话人语速、业务能容忍的响应节奏,这三样一变,最优参数就变了。务实的做法是按场景准备几套配置,而不是找一组”通用最优值”。
相关阅读
本文讲的是语音活动检测的通用原理与参数概念,不绑定某一个具体实现。 文中刻意不给参数的具体数值——各实现默认值不同且随版本变化,请以官方文档为准。 提到的开源项目仅按仓库自述描述能力类别,我们未做性能实测,不给速度、准确率一类的数字。