silero-vad:超轻量级开源 VAD 实践指南

TTS 音频入库前常规检查:"这条合成里有没有一大段静音"。传统做法是短时能量 + 阈值------低音量语音直接漏、带噪音频满屏假阳性。silero-vad 神经模型替换掉能量法之后,鲁棒性拉起来了,模型本身还只有 2 MB · 在桌面级单线程 CPU 上跑到 350× 以上实时(本文两条样本的实测口径 · 含推理与后处理 · 不含模型加载)。

一、silero-vad 是什么

  • 仓库 https://github.com/snakers4/silero-vad · MIT license · Silero Team
  • 模型体积 TorchScript JIT 2.3 MB · ONNX 2.3 MB · half 精度 ONNX 1.3 MB
  • 架构 4 层 SileroVadBlock(Conv1d + ReLU)+ LSTMCell decoder + Sigmoid 输出 P(speech) · 每 chunk 输出一个概率 · 依赖历史 state
  • 采样率 16 kHz 和 8 kHz 双分支 · chunk 大小 16k 用 512 样本(32 ms)· 8k 用 256 样本
  • 接口 一次跑整段音频 · 也可逐 chunk 流式喂 state · 不依赖未来 chunk · 实时使用等一个 32 ms chunk 的输入到齐即可
  • 多语种 训练语料覆盖 6000+ 语言变体(官方 README 口径) · 跨语种退化小 · 是它对比 WebRTC VAD 的最大优势

不做什么:不做说话人区分 · 不做识别文字内容 · 不做端点检测(EPD · 判断一句话是否说完、能否触发对话轮次结束)· 判断口径是"这段时间里有没有 human voice 发声"。

二、三行 API 上手

复制代码
pip install silero-vad

Python 侧最短闭环(调用示意 · 未实跑):

复制代码
from silero_vad import load_silero_vad, read_audio, get_speech_timestamps

model = load_silero_vad()                                                # TorchScript JIT 加载
wav = read_audio("audio.wav", sampling_rate=16000)                       # 16 kHz mono
segs = get_speech_timestamps(wav, model, sampling_rate=16000, return_seconds=True)
print(segs)
# [{'start': 1.0, 'end': 4.5}, {'start': 5.3, 'end': 6.2}, ...]

切 ONNX Runtime 只改一个 flag(其他 API 完全一致):

复制代码
model = load_silero_vad(onnx=True)                                       # 内部走 onnxruntime · CPU 推理

接入常见 pipelinefaster-whisper 内置 vad_filter=True 参数直接调它 · WhisperX 也用它做 pre-VAD · pyannote 常把它当前置轻量筛子。

三、TorchScript vs ONNX · CPU 单线程性能

单线程 CPU(torch.set_num_threads(1) + OMP_NUM_THREADS=1)· warmup 3 次 · 计时 5 次取均值 · 只计 get_speech_timestamps 与后处理 · 不计模型加载和音频读取。硬件口径:桌面级单线程 CPU · 换硬件需重跑。

读图 :横轴两条 case(23.50 s 长语音 · 8.61 s 短语音)· 纵轴单次跑完全长音频耗时。左蓝 TorchScript JIT · 右橙 ONNX Runtime · 数字上方标 ms + RTF

  • ONNX 在这两条样本上比 JIT 快约 20%
  • 两条 case 都跑到 350× 以上实时(在桌面级单线程 CPU 口径下 · 换硬件需要重跑)
  • 单条音频推理下 ONNX 提速主要来自算子融合和更精简的运行时开销 · 端侧交付 / 部署栈偏 ONNX Runtime 时更方便 · QPS 和内存收益按服务形态另压测确认

两个 runtime 在本次实测里出的 speech 段完全相同 · 差异只在耗时。TorchScript 走 pytorch 是好写好调 · ONNX 走 onnxruntime 是端侧部署的常见选择。

四、无语音检测 · silero-vad 之上的常用后处理

silero-vad 直接输出的是 speech 段。业务上关心的往往是反过来的问题:"TTS 合成音里有没有一大段无语音"------超过 2 s 的空白就当 badcase 打回。围绕这个目标 · 生产实践里常见的一套配置和后处理链路(本文两条 case 就是跑这段函数的实测结果):

推荐配置 + 后处理(生产实践中收敛下来的一组):

复制代码
import torch, io
import librosa
from silero_vad import load_silero_vad, get_speech_timestamps

SR                        = 16000
VAD_THRESHOLD             = 0.3
MIN_SPEECH_MS             = 100
MIN_SILENCE_INTERNAL_MS   = 300
MERGE_GAP_S               = 0.1
MIN_NO_SPEECH_DUR         = 2.0

model = load_silero_vad()

def detect_no_speech(wav_bytes: bytes):
    y, sr = librosa.load(io.BytesIO(wav_bytes), sr=SR, mono=True)
    audio_end = len(y) / sr

    speech = get_speech_timestamps(
        torch.from_numpy(y), model,
        sampling_rate=sr,
        threshold=VAD_THRESHOLD,
        min_speech_duration_ms=MIN_SPEECH_MS,
        min_silence_duration_ms=MIN_SILENCE_INTERNAL_MS,
        return_seconds=True,
    )
    speech_segs = [(float(s["start"]), float(s["end"])) for s in speech]

    if not speech_segs:
        no_speech = [(0.0, audio_end)]
    else:
        no_speech, prev = [], 0.0
        for s, e in speech_segs:
            if s > prev: no_speech.append((prev, s))
            prev = max(prev, e)
        if prev < audio_end: no_speech.append((prev, audio_end))

    no_speech.sort()
    merged = []
    for s, e in no_speech:
        if merged and s - merged[-1][1] <= MERGE_GAP_S:
            merged[-1] = (merged[-1][0], max(merged[-1][1], e))
        else:
            merged.append((s, e))

    return [(s, e) for s, e in merged if (e - s) >= MIN_NO_SPEECH_DUR]

实测输出(这段函数跑本文两条 case · 完整波形图在 §五):

  • case1(含长静音的语音片段 · 23.50 s)→ [(10.70, 12.90), (14.40, 23.50)] · 与人工 label 两段无语音完全对齐
  • case2(低电平全程语音 · 8.61 s)→ [(0.00, 3.40)] · 假阳性 · 详见 §5.2

几处配置的取向

  • threshold=0.3 · 比默认 0.5 低 · 对 TTS 合成里常见的轻声、气声、尾音更宽容 · 代价是背景音乐 / 底噪也可能被识成 speech
  • min_silence_duration_ms=300 · silero-vad 内部参数 · 意思是一句话内部的呼吸停顿 ≤ 300 ms 不切段 · 避免把连贯句子切成多段短 speech 后反推出一堆假的短静音
  • MERGE_GAP_S=0.1 · 相邻无语音段之间只隔 100 ms 的一小段 speech · 直接合并 · 通常是 silero-vad 误检的短脉冲
  • MIN_NO_SPEECH_DUR=2.0 · 业务阈值:合成音里出现 ≥ 2 s 的空白才算异常 · 更短的都当作正常语气停顿

调参顺序 (出问题时按这个次序动):漏检语音 → 先看响度归一化 / 再降 threshold碎片短静音多 → 加 min_silence_duration_msMERGE_GAP_S长空白误报多 → 改 MIN_NO_SPEECH_DUR 或加豁免规则。

并发 :流式调用不要跨请求共享 model state(一个实例的 LSTM 状态会被别的线程污染)· 整段批处理可以用 queue.Queue 做 model pool 隔离多个实例。POOL_SIZE 的吞吐提升需按部署环境压测确认 · 不要预设一个固定倍数。

五、两个 case 复盘

两条真实 TTS 合成音频跑一遍上面的后处理链路 · 一条精确命中 · 一条误报。

5.1 case1 · 精确命中

音频 23.50 s · label 里两段真实静音 10.70-12.9014.40-23.50 · 后处理输出的两段无语音跟 label 完全对齐。

图分四层:主图(波形 + silero-vad 绿色 speech + 最终无语音橙色)· label 条(人工标注 speech)· silero-vad 条 · 后处理输出条。三条 track 上下对齐即命中一致。

silero-vad 把前半段连贯说话拆成 5 小段 · min_silence_internal=300ms + merge_gap=100ms 把段间气口吞回来 · 短静音过滤后只剩两段长静音------正好压在 label 空档里。

5.2 case2 · 误报

音频 8.61 s · label 全程有语音 · 后处理却输出 (0.00, 3.40) 3.40 s 无语音。

同样四层结构 · 底部橙条压在 label 蓝条上 = 假阳性。

看波形前 3.40 s 明显有能量(幅度 ±0.05)· 但整段电平偏低 · silero-vad 用 threshold=0.3 也没把它判成 speech · 后处理链路无法挽回。

tradeoff :silero-vad 触发 speech 主要看 chunk 级 P(speech) >= threshold;已进入 speech 后 · 结束还受 neg_threshold(默认 threshold - 0.15)和 min_silence_duration_ms 控制。case2 前 3.40 s 的问题是始终没触发进入 speech ------ 对电平整体压低的语音(TTS 尾部渐弱 · 远场录音 · 咬字轻的感叹词)不敏感。生产上常见做法:

  1. 先做峰值 / RMS 响度归一化再判 ------本例前段电平低 · 把检测副本(不覆盖原始音频)归一化到 -3 dBFS 后再跑 silero-vad · 在这类样本上通常有效 · 具体是否命中需按批 case 验证
  2. 降 threshold 到 0.15/0.20 能救一部分 · 但会把底噪 / 前奏也判成 speech · 换成另一种假阳性
  3. 报出来的 badcase 走二次人工抽检 · 允许一定假阳性率换召回
  4. 对已知场景(TTS 尾部渐弱)单独维护豁免规则(例如尾部 3 s 内的无语音段不报)

六、写在最后

silero-vad 装机简单、CPU 单线程实时倍数高、多语种鲁棒 · 是 TTS 前处理 / ASR 前置 VAD 的一个轻量 baseline。但它不是万能的 ------case2 揭示的低电平语音漏检是它稳定的弱点之一。生产落地要把上面这套后处理链路当成配方而不是黑盒 · 阈值和过滤时长按场景调 · 出问题时能定位到是神经模型漏检 还是后处理链路合并/过滤逻辑不当 。ASR 前置场景可复用同一套 pipeline · 但 threshold / min_silence / 误报成本口径都要重新校。

相关推荐
哭哭啼2 小时前
JAVA服务问题诊断
java·开发语言·jvm
2501_944676162 小时前
揭秘!WORDTIP公司靠不靠谱?小白必看
大数据·人工智能·python
Minner-Scrapy3 小时前
Scrapy 2.17 源码解析:Scheduler 调度器与磁盘/内存双队列
java·爬虫·python·scrapy·网络爬虫·twisted
CODER03043 小时前
Anaconda和Mamba创建环境管理包常用命令合集
python·深度学习·conda·虚拟环境·mamba·常用命令大全
闲猫3 小时前
LangGraph / Capabilities / Fault tolerance
python·agent·langgraph
CRMEB定制开发3 小时前
2026年最值得推荐的10大开源商城系统盘点
开发语言·人工智能·开源·商城系统·小程序商城
IvanCodes3 小时前
Python 基础语法(一):变量、数据类型与运算符
python
buhuizhiyuci4 小时前
【QT-百日筑基篇】修真世界摸爬滚打多年,终于黄天不负有心人,突破炼器中期——常用控件QWight的属性
开发语言·c++·qt·gui·图形化
程序员三藏4 小时前
浅谈性能测试
自动化测试·软件测试·python·测试工具·职场和发展·测试用例·性能测试