MNN Whisper 实时 ASR 工程实现

适用工程:mnn-whisper

当前版本重点:Whisper + MNN C++ 推理、decoder KV cache、实时预览 single-pass 优化、视频字幕叠加演示。


代码链接

1. 项目概述

本工程将 HuggingFace Whisper 模型导出为 MNN 可执行模型,并在 C++ 端完成音频提取、Whisper log-mel 前处理、encoder 推理、decoder 解码、tokenizer 解码和视频实时字幕叠加。

当前优化目标:

  1. 支持 Whisper decoder KV cache,减少自回归解码阶段的重复计算。
  2. 支持实时预览 single-pass 模式,避免一个外层窗口内再次切块导致重复 ASR。
  3. 输出实时性能日志,包括 cost_msrtfmodelag_sec
  4. 保留旧版 chunked 模式,方便在长窗口或离线批处理场景下回退。

2. 目录与关键文件

text 复制代码
mnn-whisper/
├── include/
│   ├── asr.hpp              # ASR 类接口
│   ├── asrconfig.hpp        # 配置读取与模型路径接口
│   ├── tokenizer.hpp        # Tokenizer 接口
├── src/
│   ├── asr.cpp              # Whisper 前处理、encoder/decoder 推理、解码逻辑
│   ├── tokenizer.cpp        # Tokenizer 实现
│   └── asr_demo.cpp         # 视频/音频实时演示与字幕叠加
├── asrexport.py             # Whisper -> ONNX/MNN 导出脚本
├── export_model.sh          # 模型导出命令封装
├── run.sh                   # 运行示例
└── model_whisper/
    ├── config.json
    ├── asr_config.json
    ├── tokenizer.txt
    ├── encoder.mnn
    ├── decoder.mnn
    ├── decoder_prefill.mnn  # KV cache prefill decoder
    └── decoder_cache.mnn    # KV cache incremental decoder

3. 当前能力

3.1 Whisper MNN 推理流程

text 复制代码
PCM16 音频
    ↓
Whisper log-mel frontend
    ↓
encoder.mnn
    ↓
decoder / decoder_prefill / decoder_cache
    ↓
token ids
    ↓
tokenizer decode
    ↓
text

3.2 decoder KV cache

当前支持三类 decoder:

模型 作用
decoder.mnn 旧版 full-history decoder,作为 fallback
decoder_prefill.mnn 首次输入完整 prompt,输出 logits + present KV
decoder_cache.mnn 后续每步只输入上一个 token + position + past KV,输出 logits + 更新后的 KV

运行时如果 KV cache 模型不存在或执行失败,会自动回退到 decoder.mnn

3.3 实时 single-pass 模式

旧逻辑:

text 复制代码
外层 window=3 秒
    ↓
内部再按 asr_chunk=1.4 / asr_overlap=0.35 切成多段
    ↓
每段都完整跑一次 ASR

这会导致 3 秒窗口实际触发多次完整 Whisper 推理,性能开销明显增加。

新逻辑:

text 复制代码
外层 window=3 秒
    ↓
single-pass 一次完整 ASR
    ↓
输出字幕

日志中会显示:

text 复制代码
mode=single

4. 模型导出

4.1 默认导出 KV cache 版本

bash 复制代码
bash export_model.sh

等价于:

bash 复制代码
python asrexport.py \
  --path ./whisper-tiny \
  --language auto \
  --task transcribe \
  --no_timestamps

导出完成后,model_whisper/ 下应包含:

text 复制代码
encoder.mnn
decoder.mnn
decoder_prefill.mnn
decoder_cache.mnn
config.json
asr_config.json
tokenizer.txt

4.2 禁用 KV cache 导出

如果需要回退旧版 decoder 导出:

bash 复制代码
python asrexport.py \
  --path ./whisper-tiny \
  --language auto \
  --task transcribe \
  --no_timestamps \
  --disable_kv_cache

5. 编译工程

建议每次替换核心源码后 clean build:

bash 复制代码
cd /home/panguofeng/project/mnn-whisper

rm -rf build
mkdir build
cd build

cmake ..
make -j$(nproc)

编译产物:

text 复制代码
build/asr_demo

6. 推荐实时运行命令

6.1 实时预览推荐配置

bash 复制代码
./build/asr_demo model_whisper/config.json ../mnn-asr/36401905752-1-192.mp4 \
/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc \
--show \
--window=3 \
--stride=0.6 \
--ahead=0.8 \
--hold=0.35 \
--speed=1.0 \
--recent-chunks=3 \
--chunks-per-line=2 \
--subtitle-width=24 \
--rt-single-pass \
--log-level=quiet

6.2 CPU 压力较大时

降低刷新频率:

bash 复制代码
--stride=0.8

或:

bash 复制代码
--stride=1.0

6.3 字幕断句较多时

适当加长窗口:

bash 复制代码
--window=4 --stride=0.8 --ahead=1.0

只要日志中的 rtf < 1.0,整体就可以跟上实时播放。


7. 参数说明

参数 推荐值 说明
--window 3 ASR 外层实时滑窗长度,单位秒
--stride 0.6 ASR 刷新间隔,越小刷新越频繁,CPU 压力越大
--ahead 0.8 离线视频模式下预读未来音频,用于抵消识别耗时
--hold 0.35 语句结束后字幕保留时间
--speed 1.0 播放速度,1.0 为原速
--recent-chunks 3 预览中保留最近几段识别结果
--chunks-per-line 2 每行拼接几个 chunk
--subtitle-width 24 字幕换行显示宽度
--rt-single-pass 开启 实时窗口只跑一次 ASR,推荐
--rt-chunked 关闭 回退旧版窗口内二次切块逻辑
--log-level quiet 日志级别,实时预览推荐 quiet

8. 性能日志解释

示例:

text 复制代码
[RT-ASR] req_end=87.7 window=3 cost_ms=2003.31 rtf=0.667769 mode=single lag_sec=-1.13687e-13

字段含义:

字段 含义
req_end 当前请求窗口的音频结束时间,单位秒
window 当前 ASR 窗口长度,单位秒
cost_ms 本次 ASR 耗时,单位毫秒
rtf Real-Time Factor,cost_ms / (window * 1000)
mode single 表示 single-pass;chunked 表示窗口内二次切块
lag_sec 相对播放时间的延迟估计

判断标准:

text 复制代码
rtf < 1.0  表示可以实时
rtf = 1.0  表示刚好实时
rtf > 1.0  表示识别速度跟不上播放

当前 single-pass 实测日志:

text 复制代码
[RT-ASR] req_end=70.9 window=3 cost_ms=2369.93 rtf=0.789976 mode=single lag_sec=...
[RT-ASR] req_end=73.3 window=3 cost_ms=2807.13 rtf=0.935711 mode=single lag_sec=...
[RT-ASR] req_end=76.3 window=3 cost_ms=1496.48 rtf=0.498827 mode=single lag_sec=...

说明当前 3 秒窗口已经能实时跑。


9. KV cache 与 single-pass 的关系

9.1 KV cache 优化什么

decoder KV cache 优化的是 Whisper decoder 自回归生成阶段。

旧方式:

text 复制代码
step 0: 输入完整 tokens[0]
step 1: 输入完整 tokens[0:1]
step 2: 输入完整 tokens[0:2]
...
step N: 输入完整 tokens[0:N]

KV cache 方式:

text 复制代码
prefill: 输入完整 prompt,生成初始 KV
step 1: 只输入上一个 token + past KV
step 2: 只输入上一个 token + past KV
...

它可以减少 decoder 重复计算。

9.2 single-pass 优化什么

single-pass 优化的是实时 demo 外层调度逻辑,避免一个 3 秒窗口被内部再次切成多段,导致多次完整 ASR。

旧实时逻辑的问题:

text 复制代码
window=3
asr_chunk=1.4
asr_overlap=0.35

会导致一次 3 秒窗口里跑多次完整 Whisper。

single-pass 后:

text 复制代码
window=3
只跑一次 ASR

所以 single-pass 对端到端实时性能提升更明显。


10. 常见问题

10.1 日志没有 rtfmode=single

说明运行的仍是旧版二进制,或源码没有覆盖成功。

正确日志应包含:

text 复制代码
rtf=...
mode=single

处理方式:

bash 复制代码
cd /home/panguofeng/project/mnn-whisper
rm -rf build
mkdir build
cd build
cmake ..
make -j$(nproc)

然后重新运行:

bash 复制代码
./build/asr_demo ...

10.2 出现 mode=chunked

说明启用了旧版窗口内二次切块模式。

检查命令中是否包含:

bash 复制代码
--rt-chunked

实时预览推荐使用:

bash 复制代码
--rt-single-pass

10.3 3 秒窗口仍然超过 3 秒

先确认:

  1. 是否为 mode=single
  2. 是否 rtf > 1.0
  3. 是否 CPU 负载过高
  4. 是否 stride 太小

可尝试:

bash 复制代码
--stride=0.8

或:

bash 复制代码
--stride=1.0

10.4 KV cache 模型加载失败

检查 model_whisper/ 下是否存在:

text 复制代码
decoder_prefill.mnn
decoder_cache.mnn

如果不存在,重新导出:

bash 复制代码
bash export_model.sh

如果 MNN 转换后 tensor name 被改写,可能需要根据实际 MNN 输入输出名调整 Module::load() 的 input/output names。


11. 下一步优化方向

当前优化状态:

text 复制代码
已完成:decoder KV cache
已完成:实时 single-pass,RTF 已低于 1
待优化:dynamic log-mel + dynamic encoder
待优化:streaming encoder / encoder cache

11.1 dynamic log-mel + dynamic encoder

当前 Whisper frontend 仍然容易固定到 30 秒特征长度:

text 复制代码
[1, 80, 3000]

这意味着即使输入只有 3 秒,也可能按 30 秒路径处理。

下一步可以改成:

text 复制代码
3 秒输入 → 约 300 帧 mel
4 秒输入 → 约 400 帧 mel

同时导出 dynamic time-axis encoder,进一步降低 encoder 计算量。

11.2 streaming encoder

更进一步可以实现真正流式 ASR:

text 复制代码
音频 ring buffer
    ↓
增量 log-mel
    ↓
encoder cache / streaming encoder
    ↓
decoder KV cache
    ↓
稳定增量字幕

这会比当前滑窗 single-pass 更接近真正的低延迟流式识别。


12. 推荐默认配置

当前推荐实时预览配置:

bash 复制代码
./build/asr_demo model_whisper/config.json ../mnn-asr/36401905752-1-192.mp4 \
/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc \
--show \
--window=3 \
--stride=0.6 \
--ahead=0.8 \
--hold=0.35 \
--speed=1.0 \
--recent-chunks=3 \
--chunks-per-line=2 \
--subtitle-width=24 \
--rt-single-pass \
--log-level=quiet

判断是否达标:

text 复制代码
mode=single
rtf < 1.0
cost_ms < window * 1000

满足以上条件即可认为实时预览可用。

相关推荐
AC赳赳老秦1 小时前
OpenClaw实战案例:用Agent实现每日工作日报自动生成+发送
人工智能·python·职场和发展·eclipse·github·deepseek·openclaw
敢敢のwings2 小时前
NVIDIA Thor学习之 |在Jetson AGX Thor上部署OpenClaw并基于Ollama的边缘AI协作实战(二)
人工智能·学习
OFIRM碳基硅基2 小时前
OFIRM-AGI-ASI官方标志图01版,发布
人工智能·agi·ofirm颠覆性
haina20192 小时前
海纳AI正式发布“面试Agent”——实现千岗千面与人机共管的智面新纪元
人工智能·面试·职场和发展
大信说财务2 小时前
2026年数电票管理生态:技术路线、市场格局与选型策略
人工智能·自然语言处理·电子发票·智能化·发票管理·财务工具
Deepoch2 小时前
Deepoc 开发板赋能工业巡检机器人自主感知与决策
人工智能·科技·机器人·巡检机器人·具身模型·deepoc
code 小楊2 小时前
DeepSeek V4 全面解析:测评、对比、案例及实操指南
人工智能·开源
ZPC82102 小时前
ROS2 速度远快于 UDP的完整方案(同机节点)
人工智能·算法·计算机视觉·机器人
AI袋鼠帝2 小时前
Claude Design完整系统提示词泄露!
人工智能