Whisper 听写降延迟:按停顿切块更划算
Vocs 作者指出,whisper.cpp 会将短音频补至 30 秒编码窗口,按秒切块会重复消耗大量计算。其方案用语音活动检测寻找停顿,在说话期间后台转写,并采用首块至少约 4 秒、单块最多 12 秒的策略。
AI 深度解读
把语音每隔 1 秒切开送给 Whisper,听起来能边说边转,实际却可能更慢。开源 macOS 听写应用 Vocs 的作者发现,低延迟的关键是减少重复编码、缩短最后一段音频的处理时间,并防住参数组合引发的重复输出。
这篇经验记录值得看,不只是因为它给出了 whisper.cpp 的调参方法,还因为它揭示了一个常见误区:后台任务不一定能隐藏耗时,看似免费的提速选项也可能拆掉正确性保护。
为什么切成 1 秒,计算量反而更大
Vocs 使用 Tauri、Rust 和 whisper.cpp,目标是按键开始讲话,再次按键后立即粘贴文字。无论说了 5 秒还是 5 分钟,作者都希望停止到粘贴的等待时间保持稳定。
障碍在 Whisper 的编码器:它按 30 秒窗口工作,whisper.cpp 会把不足的音频补到 30 秒。因此,编码 1 秒音频的成本接近编码 25 秒;每 1 秒切一次,相比每 10 秒切一次,总计算量大约增加到 10 倍。
作者采用纯 Rust 的 WebRTC VAD 实现 earshot crate 检测停顿。VAD 是语音活动检测,这里只需要判断有没有人在说话,不必识别内容。具体策略是:讲话满 4 秒后,在首次停顿处结束第一块,每块最长 12 秒,并在用户继续讲话时后台转写。
优化目标不是让每块都尽快完成,而是停止讲话时只剩最后一个小片段。
后台转写为什么还会拖慢最后一下
whisper_full_params.audio_ctx 可以缩短编码器使用的音频上下文。默认值 1500 对应完整的 30 秒窗口,作者参考 whisper.cpp 自带的 stream 示例,按片段时长调整:
audio_ctx = clamp(ceil(duration_seconds / 30 * 1500) + 128, 256, 1500)
要看的是最后的边界:结果限制在 256 到 1500 之间,计算时额外加上 128 的余量。原文没有解释这个余量的选择依据。
作者实测,1.7 秒片段在 audio_ctx 为 256 时耗时 221 ms,为 1500 时耗时 1783 ms,得到相同文字,约快 8 倍。但这只能证明这次测试,不能直接推到所有语音和模型。
最初,作者只给最后一段缩短上下文,后台分块仍用完整上下文,理由是后台耗时可以被讲话时间覆盖。实际一次完整上下文解码用了 2.3 秒;用户恰好在途中停止时,最后一段只能排队,直到 2.6 秒后才粘贴。后来所有分块都改用缩短后的上下文。
这里真正该测的是尾部延迟,也就是用户按下停止之后还要等多久。后台队列里的旧任务,同样可能挡住最后一步。
两个提速设置,怎样组合成重复粘贴
whisper.cpp 默认会在解码失败后提高温度重试,最多额外执行 6 轮。作者观察到,正常 200 ms 的解码可能因此变成 1.4 秒,于是设置 temperature_inc = 0.0,关闭温度回退。
随后又设置 no_timestamps = true,因为产品不需要词语时间信息。但作者发现,时间戳 token 也参与解码停止判断;去掉它们后,解码器可能反复输出同一句话,直到达到 token 上限。温度回退原本有机会发现重复并重试,此时也被关掉了,最终出现一句话重复粘贴 24 次的问题。对作者测试的场景,no_timestamps = false 反而更快且正确。
关闭回退后,作者还遇到过「Scoffs」「punch」,以及一段 2.4 秒录音输出 217 个词、不断重复「the one who is...」的情况。因此他们增加了几道代码防线:
- 没检测到语音的片段不送模型,减少静音生成「thanks for watching」之类文本。
- 清理
*...*、[...]、(...)形式的音效标签。 - 短语连续重复至少 3 次、单词至少 5 次时折叠重复,并对该片段重试一次;仍循环就作为非语音丢弃。
- 分块间设置 no_context = true,避免解码状态跨块延续。作者认为,状态延续是同一句话重复 40 次的一种常见诱因。
模型返回了字符串,不代表这段字符串就适合直接粘贴。
我的判断
作者抓对了优化对象:交互式听写应该关注停止后的等待时间,也应该把参数组合放到真实语音中测试。单独测速度,很容易漏掉「处理很快,但输出重复了」这种失败。
不过,这仍是一份工程经验,原文没有交代完整的硬件、模型版本、语种、测试规模,也没有给出识别准确率对照。4 秒起切、12 秒封顶,以及重复检测阈值,都适合作为起点,不能当成通用最优值。关闭温度回退,再靠规则重试和丢弃来兜底,也是在延迟与恢复能力之间做取舍;清理括号内容、判定非语音,都可能误伤有效文本。
还有一项无法靠模型参数解决:提前录音的预滚动缓冲能保住首音节,却需要提前打开麦克风,令 macOS 的橙色隐私指示灯常亮。Vocs 默认按键后才开麦,耗时约 20 到 80 ms;另提供可选的「keep it warm」模式,用常亮指示灯换取开头不被截断。这是产品选择,原文没有提供用户接受度数据。
能拿来做什么
- 如果正在用 Tauri 或 whisper.cpp 做听写,先记录停止到粘贴的耗时,并检查最后片段是否在等待后台任务,再决定要不要继续缩小分块。
- 调整 audio_ctx、temperature_inc 和 no_timestamps 时,用同一批真实录音同时检查速度、漏字和重复输出,保留参数组合的结果。
- 把静音过滤、重复检测和有限重试放在粘贴之前;对会自动输入到编辑器的语音工具,输出校验应当是完整流程的一部分。
- 做按键录音交互时,明确默认开麦时机,并把常驻麦克风作为用户可选项,让首音节完整性与隐私指示之间的取舍可见。