Newshot
Account

Show HN:RealtimeVAD——以更多误报换取语音结束检测提前 110ms

Translation(model translation, not the source's own text)

Show HN:RealtimeVAD——用误报换取提前 110ms 的语音结束

仓库文件导航

题图
题图

RealtimeVAD

比 Silero 约早 100 ms 知道说话人已停止。

RealtimeSTT · RealtimeTTS · 在线演示 · 规格 · LiveKit 插件

语音助手只有在知道用户停止说话后才能做任何事。语音识别、LLM 调用和语音输出都在等待这一个判断,而普通的语音活动检测器需要几百毫秒才能做出判断。

RealtimeVAD 让它更早。它保留 Silero VAD 来判断什么是语音,并增加第二项更快的检查:每 10 ms 将音频电平与房间噪声和说话人自身电平进行比较。音频一安静下来,你就会收到事件,远早于 Silero 的概率下降。

  • 输入:16 kHz 单声道音频,任意大小的块(float32、int16 或 int16 字节)。
  • 输出:start、pause 和 end 事件,以及任意时刻当前运行片段的音频。
  • 提前量:在 3,279 个真实轮次上,语音结束比 Silero 的概率下降中位数早 100 ms,比 Silero 的 VADIterator 报告结束早 206 ms。在 82% 的轮次中,结束本身早于 Silero,中位数为 70 ms。
  • 代价:它也会在词与词之间的短暂间隙触发,频率约为 Silero 的十倍。
  • 安静输入:安静说话者的轻柔词尾不是静音;在嘈杂的安静线路上,提前量反而消失(详情)。
  • 运行于:一个 CPU 线程,远快于实时。只需要 numpy 和 onnxruntime;Silero 模型已捆绑,无需 torch。
  • 状态:alpha

Show HN:RealtimeVAD —— 用误报换取提前 110ms 的语音结束检测

  • 永远不要把它当作对话轮次的结束。只有 end 才会关闭一个分段。
  • 无法快速取消?忽略 pause 并等待 end。多出来的误报造成的代价会超过它们节省的收益。

已经在用 Silero?

修改导入语句:


        from silero_vad import load_silero_vad
from RealtimeVAD import VADIterator          # was: from silero_vad import VADIterator

vad_iterator = VADIterator(load_silero_vad(), sampling_rate=16000)
speech_dict = vad_iterator(chunk, return_seconds=True)   # {'start': ...} / {'end': ...} / None
      

构造函数和返回结果保持不变,但音频一安静下来 end 就会立刻到达。不传模型参数时,它会使用内置的 Silero 模型,无需 torch。

使用 LiveKit Agents?

livekit-plugins-realtimevad/ 是一个 VAD 插件:用 realtimevad.VAD.load() 替代 silero.VAD.load()。它的 README 展示了事件如何映射,以及在相同音频上相对 Silero 插件的测量结果。简而言之:与 Silero 插件的默认设置相比,语音结束约提前 380 ms,但其中大部分来自更短的静音设置(0.25 s 而非 0.55 s),而 Silero 同样可以设置成这样。在相同设置下,RealtimeVAD 的 end 提前 65 到 110 ms,LiveKit 的轮次检测器提前 95 到 180 ms 启动。在 LiveKit 会话内部,相比 Silero 的默认设置,轮次提交约提前 150 ms;相比 Silero 设置为 0.25 s,约提前 70 ms。

结果

在来自真实语音助手的 3,279 个轮次上测量,每个都带着其尾随静音,使用默认设置。Silero 的判定由完全相同的帧概率计算得出,因此两者看到的输入完全一致。"speech-off" 指的是 RealtimeVAD 在轮次结束前的最后一次 pause,对比 Silero 最终一段低于其阈值的语音中的第一帧。

RealtimeVAD 的 speech-off 早于……中位数p25 – p75p90更早的比例
Silero,概率低于 0.5(其默认阈值)100 ms80 – 150 ms214 ms98 %
Silero,概率低于 0.35( VADIterator 的 end 阈值)110 ms90 – 176 ms230 ms98 %
Silero VADIterator 的 end,默认 100 ms 最小静音206 ms186 – 272 ms336 ms99 %
后面还有语音的 speech-off 信号每秒语音完全没有的轮次
RealtimeVAD 的 pause1.517 %
Silero,概率低于 0.50.1486 %

在 0.0.4 之前,第一行写的是 110 ms。这 10 ms 用在避免切断轻声说话者上,见下一节。

end 事件,对比 Silero 自身在用相同阈值和相同的 0.24 s 静音计数时给出的 end(其中 2,323 个轮次两者在录音内关闭了相同的分段):

RealtimeVAD 的 end 早于仅用 Silero更早的比例中位数p25 – p75更晚的比例
0.0.582 %70 ms40 – 92 ms0
同上,但在一次咔嗒声后不重新开始计数51 %18 ms0 – 76 ms0

在 0.0.4 之前,单词之后安静段中的单个响亮 10 ms 块(一次咔嗒、一声咂嘴、一次呼吸)会永久停止静音计数,把 end 留给 Silero。这在 680 个抽样轮次中有三分之一发生。现在计数会在其后重新开始。

用你自己的录音复现(16 kHz 单声道 WAV,每个文件一个轮次,带尾随静音):


        python benchmarks/speech_off_lead.py <wav dir> out.json
      

轻声输入与电话音频

在 0.0.4 之前,"quiet" 有一个固定的底噪(quiet_rms,约 −54 dBFS)。对于约 −32 dBFS 的说话者来说这没问题。对于轻声的说话者(在源级录制的电话线路、远处的麦克风),底噪只比语音低几个 dB,他们轻声的词尾会被算作静音,0.24 s 之后分段就在他们仍在说话时结束了。自 0.0.5 起,底噪跟随说话者:它取 quiet_rms 或比说话者自身水平低 20 dB 两者中较低的那个(floor_margin_db)。

同样的 3,401 个 turn,衰减后送过一条模拟电话线路(300–3400 Hz、8 kHz、μ-law、线路噪声)。「说话中结束」统计的是这样的 turn:其结束点在同一检测器于录制电平下并不会判为结束,且其后还有语音。「仅 Silero」是同一个 VAD 关闭 quiet clock、在同一段音频上的结果。

语音电平说话中结束:0.0.40.0.5仅 Silero丢失 ≥ 100 ms 语音:0.0.40.0.5仅 Silero语音结束提前量 0.0.5
−32 dBFS(按录制电平)–––000100 ms
−38 dBFS361 (11 %)36581388108 ms
−42 dBFS609 (18 %)3797261010116 ms
−52 dBFS1,473 (43 %)2151558682623132 ms
−62 dBFS1,832 (54 %)2693031,778419415124 ms
−47 dBFS,位于 −62 dBFS 的房间噪声中746 (22 %)12531229593930 ms
电话线路,−38 dBFS285416391266234229112 ms
电话线路,−48 dBFS6672652753113012940 ms

这说明了什么,以及没有说明什么:

  • 轻声说话者不再被截断。说话中结束和丢失语音现在处在与仅 Silero 时相同的位置。在所有这些运行中,RealtimeVAD 的最后一个结束点从未晚于 Silero。
  • 有噪声的安静线路没有提前量。当噪声比说话者低不到约 20 dB 时(房间噪声那一行,以及带线路噪声的安静电话线路),要想不把轻声语音也算作安静,就没有任何东西能被判为安静。此时 RealtimeVAD 在 Silero 结束的地方结束:正确,但并不更快。
  • 极安静和窄带的语音是 Silero 的局限。在 −62 dBFS 以及电话线路上,Silero 自己也不确定。它评分从未达到 0.9 的语音,过去完全不会开启任何 segment(电话线路的 turn 中有 476 个,−62 dBFS 下有 1,264 个)。soft start 让这个数字减半(186 和 380);Silero 用自己 0.5 的阈值仍能听到更多(139 和 353)。找到更多 turn 也意味着电话线路各行中统计到的结束更多。在 VAD 之前放大这类输入,对 Silero 的帮助最大。
  • 边界结束点。在被计为安静的 0.24 s 内含原始录音语音的结束点,在 −52 dBFS 下比 0.0.4 中稀少 10 倍(当时 1,970 个 turn,现在 182 个),但在 click 之后重新开始计数又加回了一些(不含该处理时为 141)。其后还有语音的结束点和丢失语音并没有随之上升。
  • 电话路径是模拟的,「仍在说话」的参照是原始宽带录音。一个说话者,一个麦克风。

该设置什么:

如果……那么
你的输入安静且干净什么都不用做;提前量保留
你更想保留旧的固定 floorVAD(floor_margin_db=None)
soft start 会开启你不想要的 segment(背景人声)VAD(soft_start_s=None)
你只想要 Silero 自己的计数,不要提前结束VAD(quiet_rms=0)
你的输入很安静或是电话线路,且干净试试 VAD(normalize_to_dbfs=-30) ,见下文

        python benchmarks/quiet_input.py <wav dir> out.json
      

为 Silero 放大安静的输入(可选)

Silero 对安静的语音更没把握,在电话线路上尤其如此:它找到的 turn 更少,并且会在其中途掉出。VAD(normalize_to_dbfs=-30) 会把 Silero 看到的帧放大,最多 20 dB 且绝不进入削波,直到语音落在 −30 dBFS。电平、quiet clock 以及你拿回的音频都使用原样的输入。同样的 turn 如上:

语音电平完全没有 segment:关闭开启丢失 ≥ 100 ms 语音:关闭开启
−32 dBFS(按录制电平)0002
−52 dBFS138127265
−62 dBFS38035741940
−47 dBFS,位于房间噪声中2202159321
电话线路,−38 dBFS186142234125
电话线路,−48 dBFS542499301265
电话线路,−58 dBFS1,5691,203910411

文章标题:Show HN: RealtimeVAD —— 用误报换取提前 110ms 的语音结束

正文: 它默认关闭,因为在正常水平下它不是没有代价的:3,401 轮对话中有 2 轮损失超过 100 ms,语音结束比 Silero 更早的比例为 96 % 而非 98 %,并且在一次电话线路对话中,结束时间比单独使用 Silero 更晚。它需要 Silero 至少一次将语音评分到 0.5 以上才会行动,因此流开头的一些轻声词仍可能被漏掉。在嘈杂的安静输入上,结束点的偏移超出了基准测试中“说话时结束”计数所能判断的范围,因此这里不给出该计数;临界结束的情况没有增加。

短促的附和词

“mm-hmm”、“yeah”、“okay”:来自会议录音的 200 个真实样本(AMI 语料库,近场麦克风,时长 0.15–0.8 s)。在 0.0.4 版本之前,其中 50 个完全没有被报告为语音:Silero 对 35 个的评分在 0.5 到 0.9 之间,低于起始阈值,另有 15 个低于 0.5。使用软起始后,有 25 个被漏掉,其中 15 个是 Silero 自身漏掉的。起始点在词开始后中位数 160 ms 处出现。这样的声音是附和词、话轮还是打断,不是 VAD 的问题,这里不予回答。

限制

  • 这些数字来自单一说话人和单一采集链路:带噪声抑制和回声消除的浏览器采集,且多为安静房间。
  • 提前量需要音频在词后确实安静下来,噪声抑制对此有帮助。在 137 个较嘈杂的对话中(房间电平高于 −70 dBFS),提前量中位数为 102–124 ms。在嘈杂房间中不使用噪声抑制时,预期会更小;当噪声与说话人差距在约 20 dB 以内时,则没有提前量。
  • 更早不等于更正确:在录制的电平下,语音结束有时是否落在轻柔词尾之内,没有对照词时间戳进行检查。上面的安静输入表格是在正常电平下对同一音频进行检查的。
  • 100 ms 是针对语音结束的。end 在两种情况下都在语音结束后等待 0.24 s;它自身相对 Silero 的增益更小(中位数 70 ms,18 % 的对话中没有增益),并且取决于噪声。
  • 耳语大多检测不到(Silero 评分低于 0.5)。笑声、咳嗽和喷嚏则常常能检测到。
  • 输入中的直流偏移会使静音时钟看不到静音:此时不会提前结束,也不会损失什么。
  • 仅支持 16 kHz 单声道。

开发


        pip install -e ".[dev]"
pytest
      

tests/audio/ 中的边界情况测试运行在大约 100 MB 的自由许可音频上,这些音频不在 git 中(8 种语言的语音、噪声、音乐、咳嗽和笑声、耳语、真实附和词和犹豫)。没有这些音频时测试会被跳过。获取方式(约 1.3 GB 下载量,通过 sha256 校验):


        python tests/audio/fetch_audio.py
python tests/audio/fetch_backchannels.py
pytest tests/audio
      

使用 8 个工作进程时约需 10 秒,并打印每个测试所守护的内容,以及测量值及其限值。来源和许可证:LICENSES-audio.md、LICENSES-backchannels.md。

许可证与致谢

MIT。内置语音模型为 Silero VAD(MIT)。

The translation is machine-generated and individual terms may be off. Nothing is rewritten or cut; the original is authoritative.

Published by Hacker News
This site only summarises and aggregates; copyright stays with the original author. Headlines and summaries follow the source — where they differ, the original wins.
Read the original ↗