本篇速览

  • 端侧 ASR 至少有两条工程路线:一条是 ggml 自有权重 + 自研算子(代表 whisper.cpp),一条是 ONNX Runtime + 硬件厂商 EP(代表 sherpa-onnx,见 A 类)。两条路线解决同一件事,取舍不同。
  • whisper.cpp 是工程化程度最高的参照系:自带 whisper-bench 基准工具、whisper-server(README 描述为 OAI-like API)、whisper-stream 实时转写,以及 quantize 量化命令。
  • 量化是端侧 ASR 的必修课:体积与内存降下来,代价是识别质量的退化,必须实测而不是想当然。
  • 对照实验要控制变量:同一批音频、同一台设备、相近温度,分别记录延迟、内存、准确率三张表。
  • 边界:多语言与方言表现、长音频的内存增长、NPU 卸载的适用条件,三者都要写清。

一、一句话结论

把语音识别搬到点位上的时候,你会发现"用什么引擎"不是一个单纯的性能问题,而是两个工程哲学的选择:是走一个自包含的 ggml 推理库(权重格式和算子自己管),还是走一个通用 ONNX Runtime(把算子卸载交给硬件厂商的 EP)。whisper.cpp 是第一条路线的标杆,sherpa-onnx 是第二条路线的代表。本篇把 whisper.cpp 拆开,给出可复用的对照方法,让你在选型时有数字而不是印象。


二、背景:为什么是这两条路线

2.1 小智原链路用的是哪条

第一章的基线里,ASR 用的是 FunASR 的 SenseVoice,跑在另一台机器上。FunASR 后端是 PyTorch,体积和依赖都重,不适合直接塞进板子。改造时有两个自然的去处:

  • 去处一:换成 ggml 系的自包含模型(whisper.cpp 的 base.en / small 等),推理库自己带着权重格式和算子。
  • 去处二:换成 ONNX Runtime 系(sherpa-onnx),模型是 ONNX,算子卸载交给 EP(包括 Qualcomm NPU)。

两条路都能让 ASR 落板,但易用性、硬件适配面、内存账完全不同,这正是本篇要对照的。

2.2 whisper.cpp 是什么

whisper.cpp 是 OpenAI Whisper 模型的 C/C++ 移植,底层是 ggml 张量库,主打零依赖、多平台。README 顶部许可证徽章明确标注 MIT。GitHub 仓库元数据(核实日期 2026-10-08)显示约 54.2k star,最近 Release 为 v1.9.5(2026-10-06)。

README 明确写出的后端支持面很宽:Apple 侧的 Metal、Core ML、ANEForge;x86 的 AVX / VSX 指令集与 OpenBLAS;NVIDIA 的 CUDA(cuBLAS);Vulkan、AMD 的 HIP/ROCm、摩尔线程的 MUSA/muBLAS;以及三类 NPU 卸载——Intel 的 OpenVINO、AMD Ryzen AI 的 VitisAI、华为的 CANN(昇腾)。

注意其中三个是 NPU 卸载(OpenVINO / VitisAI / CANN),与本项目的高通硬件主线相关,但高通 QNN 不在 whisper.cpp 明确列出的后端里——这是和 sherpa-onnx 的一个关键差异,选型时要分清。

2.3 后端构建选项逐一看

README 列出的后端,本质上是一组编译开关。理解每个开关在什么硬件上才有意义,能避免"为用不上后端编译半天":

后端 / 开关适用的硬件在目标板上的意义
Metal / Core ML / ANEForgeApple Silicon与目标板无关,仅 macOS 开发机可用
AVX / VSXx86 CPU 向量指令目标板是 ARM,无关
OpenBLASCPU 通用 BLAS可作 ARM 上的 CPU 兜底
CUDA(cuBLAS)NVIDIA GPU目标板无独显,无关
Vulkan跨平台 GPU若板子有 GPU 且驱动支持 Vulkan 可考虑
HIP / ROCmAMD GPU无关
MUSA / muBLAS摩尔线程 GPU无关
OpenVINO / VitisAI / CANNIntel / AMD / 华为 NPU对应厂商平台,非高通

结论很直接:在咱们的高通板子上,whisper.cpp 能实际用上的主要是 CPU 路径(OpenBLAS 或纯 ggml),以及潜在的 Vulkan。NPU 卸载那一栏里没有 Qualcomm QNN,所以"想靠 NPU 加速 whisper.cpp"在当前版本是不成立的,选型时别被"支持 NPU"的笼统说法带偏——要看清支持的是哪家的 NPU。

怎么确认某个后端真的被启用?不能只看编译时开了开关。运行时可以看进程加载的动态库(例如开了 OpenVINO 却没装对应 runtime,会在初始化时报找不到库),以及看日志里是否打印了对应的后端初始化信息。更可靠的做法是:在同一段音频上对比"关掉某后端"和"开着某后端"的耗时,若两者一致,说明那个后端没真正参与,只是编译进去了。这一步和 A 类讲"确认 NPU 是否真启用"是同一个方法论。

2.4 一个事实归属提醒

star 数、版本号、许可证、后端清单都取自 README 与 GitHub 仓库元数据(核实日期 2026-10-08)。其中版本迭代快,发布前请再核一次。本文不把 README 没写的能力当作存在,例如不声称 whisper.cpp “支持 Qualcomm NPU”,只陈述它明确列出的后端。


2.5 从源码到可执行:构建流程长什么样

whisper.cpp 是 CMake 工程,典型流程是克隆后建构建目录、配置、编译。README 给出的核心命令就是 cmake -B build && cmake --build build -j。要点:

  • 构建目录 build 是产物集中地,交叉编译时要指定工具链文件;在 ARM 板子上直接编,注意给够内存——编译 ggml 的某些文件较吃内存,板子内存小时可用 -j 1 避免 OOM。
  • 后端开关(如 -DWHISPER_OPENVINO=1)要在配置阶段就传,事后改要重配重编。
  • 编译出的关键二进制是 whisper-cli(推理)、whisper-bench、whisper-server、whisper-stream、quantize,都在 build/bin/ 下。

这套流程本身不难,难在"选对开关"和"板子资源够不够编"——在内存紧张的板子上编译,常常比运行更考验资源。

2.6 两种权重格式背后的哲学

把视野拉远一点,两条路线的根本差异在"权重格式归谁管"。whisper.cpp 用自有的 .bin(ggml)格式,算子也由 ggml 自己实现——好处是高度自包含、零外部依赖、跨平台只靠一个库;代价是模型必须转成它的格式,且优化空间受 ggml 限制。sherpa-onnx 用 ONNX 这种开放中间表示,算子卸载交给各家 EP(包括厂商 NPU)——好处是能蹭到硬件厂商的加速,代价是要和 ORT 及各家 EP 的版本/算子集打交道。所以"选哪条路线"很大程度上是"你想要自包含的省心,还是想要硬件加速的弹性",这句话能回答大部分选型纠结。

三、量化:端侧 ASR 的必修课

3.1 为什么必须量化

端侧设备的存储和内存都紧。一个 base 量级的全精度 Whisper 权重体积不小,量化是把它压到能装、能跑的必要手段。whisper.cpp 的 Features 列表明确写着 “Integer quantization support”,量化工具是 build/bin/quantize。

3.2 量化命令长什么样

README 的 Quantization 小节给出完整命令:

cmake -B build && cmake --build build -j --config Release
./build/bin/quantize models/ggml-base.en.bin \
                    models/ggml-base.en-q5_0.bin q5_0
# 之后用量化后的模型正常跑
./build/bin/whisper-cli -m models/ggml-base.en-q5_0.bin ./samples/gb0.wav

README 明确写出的量化类型示例是 q5_0。本文只引用 README 中明确出现的类型,其他常见档位不在未经核实的情况下罗列。

3.3 量化的真实代价

量化省的是体积和内存带宽,代价是识别质量退化。但这个退化不是均匀的:数字、专有名词、弱信号下的识别更容易掉点。所以量化档位的选择不能用"选最小的就行",而要在目标场景的测试句集上实测 CER(字错误率),找到质量还能接受的最低档位。

一个可复用的实验设计:

取一档全精度 + 两档量化(如 q5_0 与更低一档)
在固定测试句集上各跑一遍
记录三条:模型体积、识别 CER、单条音频识别耗时
画一张"体积 vs 质量 vs 速度"的三方表

最后一列(耗时)要实测,不要推算——同一档量化在不同硬件上的加速比差异很大。

一个务实的量化档决策流程:先在全精度下跑一遍测试集拿 CER 基准,再上 q5_0 拿一档,若 CER 掉了但还能接受就停(体积已省一截),若掉了太多就回退到只做部分量化(见 3.4)或保留关键层。不要为了"体积最小"一路压到低档,最后质量崩了才发现——质量崩在端侧对话里表现为听写错误、专有名词识别错,用户体验直接掉。

3.4 部分量化与混合精度

进阶做法是不把所有层都量化到同一档。Whisper 的 encoder 和 decoder 对量化的敏感度不同:encoder 偏"感知特征",量化掉点往往体现在整体准确率;decoder 偏"自回归生成",量化可能让某些 token 预测抖动更明显。whisper.cpp 的量化可以按层或按张量粒度控制,实务上可以保留少数敏感层为较高精度、其余压到低档,在体积和质量之间找更细的平衡点。这一步没有普适配方,要针对你用的模型尺寸和测试集调。


3.5 模型尺寸与板子容量

Whisper 家族从 tiny 到 large 横跨数个数量级,端侧要选"装得下、跑得动"的那一档。经验上的取舍(属推算,以官方模型卡为准):tiny/base 量级适合嵌入式与实时,small/medium 质量更好但更重,large 基本不是端侧目标。选型时把"模型体积 + 运行时 + 同机其他负载"加总,和板子可用内存比,留足余量。不要只看模型体积一个数字——量化后体积降了,但运行时的临时态和并发态仍占一份,长音频下还会随上下文增长。

四、benchmark:怎么做才可信

4.1 自带工具

whisper.cpp 自带的基准工具让"带数字的文章"变得可能:

  • whisper-bench:README 的 Examples 表描述为 “Benchmark the performance of Whisper on your machine”,路径在 examples/bench。
  • scripts/bench.py:README 的 Benchmarks 小节说明可以按不同模型与音频文件批量跑,示例 python3 scripts/bench.py -f samples/jfk.wav -t 2,4,8 -p 1,2。

4.2 测量时的三个坑

沿用第一章"首 token 延迟"篇的纪律,语音识别的基准也有三个坑:

  • 缓存假象:连续识别同一段音频会越来越快,那是缓存和预热的效果,不是真实表现。测试集要用不同内容。
  • 首跑偏慢:第一次加载模型要初始化,第一轮数据不能当常态。
  • 温度与负载漂移:设备发热后降频,后半段数据普遍偏慢。要在目标温度下测,并记录温度。

4.3 看什么指标

whisper-bench 与 scripts/bench.py 输出的是吞吐(tokens/s 或音频时长/耗时)这类指标。但用户感知的是"这段录音多久能出完字",不是平均吞吐。所以除了吞吐,还要测"一段固定长度的真实录音,从喂入到完整文本返回的总耗时",并把两者都记下来——吞吐好看不代表体感好。

具体来说,假设一段 30 秒的录音,端到端从喂入到出完整文本花了 6 秒,而 whisper-bench 报的纯推理吞吐折算下来"每秒音频只需 0.15 秒计算"。体感差在哪?差在音频读取、VAD 前置、文本后处理、以及服务化下的排队。所以基准工具报的数字是下界,真实链路的数字一定更大,写稿时要把"基准下界"和"链路实测"分开,不要拿 bench 数字冒充端到端体验。

4.4 一个 whisper-bench 输出怎么读(示例,标注来源)

下面是一段示意输出(非本方案实测,仅说明字段含义):

whisper-bench 示例输出(示意)
model: ggml-base.en-q5_0.bin
threads: 4
audio: 30s
compute: 4.2s        # 纯推理耗时
load:   0.8s         # 模型加载
total:  5.0s         # 端到端(不含音频读取)
speedup over realtime: 6.0x

读法:关注 compute 与 total 的差——load 是一次性的,但每次进程重启都要付;compute 才是随音频长度线性增长的部分。把 compute 折算成"每音频秒多少毫秒",才能和流式场景下的实时性要求对照(例如要实时,需 compute < 音频时长)。这段数字为示意,真机填入实测即可。


五、服务化:把识别能力封装成接口

5.1 whisper-server 是什么

README 的 Examples 表写明 whisper-server 是 “HTTP transcription server with OAI-like API”(路径 examples/server),Docker 章节给出了运行命令和调用示例:

# README 的 Docker 示例(注意:这是示例里的端口映射,不是声明默认端口)
docker run -it --rm -p "8080:8080" -v $MODEL_PATH:/models $IMAGE \
  whisper-server --host 0.0.0.0 -m /models/ggml-base.bin

Docker 示例里把宿主 8080 映射到容器 8080,并给出 curl http://127.0.0.1:8080/inference ... 的调用示例,接口路径是 /inference。

这里有两个要小心的点:一是端口——README 只在 Docker 示例里出现 8080 映射,并未声明"默认端口就是 8080",写稿要写成"示例中映射宿主 8080";二是接口路径——是 /inference,和常见的 /v1/... 不同,这是"类 OpenAI 接口 ≠ OpenAI 兼容"的典型例子(见 5.2)。

5.2 类 OpenAI 不等于 OpenAI 兼容

把语音识别封装成 HTTP 服务后,上层链路就能像调用一个接口一样用它。但 whisper-server 标的是 “OAI-like”(类 OpenAI),不是 “OpenAI compatible”。这个差别很关键:上层如果用标准 OpenAI SDK 去对接,字段和路径对不上就会失败。

这正是第一章 B 类第 1 篇(OpenAI 兼容字段)的延伸:接口"长得像"不代表"字段对齐"。调用前要把请求与响应字段逐条对一遍,而不是凭"它写着 OAI-like"就直接接。具体到 whisper-server,请求体里的音频字段名、模型字段名、以及响应结构,都和 OpenAI 的 /v1/audio/transcriptions 不同,需要按它的文档逐一映射,或在你的编排层写一层适配。

5.3 流式与实时

whisper-stream 是 Examples 表里的实时麦克风转写工具,README 说明它"每半秒采样一次音频并持续转写",需要 SDL2 构建( -DWHISPER_SDL2=ON)。这是把识别做成"边说边出字"的现成参考。要注意:它是演示性质的实时转写,接入正式链路时还要处理"半秒块"和 VAD 切句的配合——即先由 VAD 判断一段话开始,再交给流式 ASR,而不是无脑每半秒送一次。

5.4 服务化的资源代价

把识别做成常驻服务后,要额外付"服务常驻"的代价:进程一直占着内存、模型一直在内存里。和"按需起进程"相比,常驻的首字更快(不用每次加载),但空闲时也占资源。在板子上,这个常驻占用要和 VAD、TTS、LLM 一起算进第六章的内存账,不能只看识别本身占多少。


5.5 输入音频的硬性要求

whisper 系列模型训练输入是 16000Hz 单声道。喂入前若采样率或通道不对,识别率会明显掉。whisper.cpp 内部有重采样能力,但实务上建议在喂入前就把音频规整到 16000Hz 单声道,避免依赖运行时的隐式处理;同时要注意字节序与 dtype(int16 vs float32)要和接口约定一致。这一条和 A 类讲的三环采样率对齐是同一类问题,只是发生在 ASR 这一环的入口。

5.6 whisper.cpp 在小智链路里的位置

如果最终选 whisper.cpp 作为 ASR,它在整条链路里替换的是 A 类里 sherpa-onnx 的 ASR 那一环,位置是 VAD 之后、LLM 之前:

麦克风 → VAD → [whisper.cpp ASR] → LLM(本地)→ TTS(本地)→ 扬声器

接入时要处理三件事:一是 VAD 切出的语音段要规整成 16000Hz 单声道再喂给 whisper.cpp(见 5.5);二是 whisper.cpp 默认是非流式的,要做"边说边出字"需要在 VAD 的句级切分上做伪流式(每切出一段就识别一段),而不是改 whisper.cpp 本身;三是它的输出是纯文本,要接回编排层的文本队列。换句话说,whisper.cpp 负责"一段进、一段出",实时性是靠 VAD 切句 + 短段识别拼出来的,不是模型原生的流式。这一点在和 sherpa-onnx 的流式 ASR 对照时要写清——两者"流式"的实现路径不同。

5.7 语言与解码参数

whisper.cpp 支持自动语言检测,但"自动检测"本身要多跑一遍前向(先判断语种再识别),在端侧会直接增加首字延迟。实务上如果场景语言固定(比如中文对话),应显式指定 language 而不是让它自动猜,省掉那一遍检测。另一个相关参数是是否输出时间戳——开启 timestamps 会让模型额外输出每词的起止时间,方便做字幕对齐,但也增加输出量和处理时间。这些参数不是"设了就好",而是在"功能"和"延迟"之间按场景权衡,要在真机上测出差异再定。

5.8 日志与计时:怎么拿到真实延迟

做对照实验时,光看"跑完多久"不够,要能拆出各段。whisper-server 通常有请求级日志,记录接收与返回时间;自己起进程跑 whisper-cli 时,可以在调用前后打时间戳。无论哪种,关键是把"模型加载 / 首字 / 完整返回"分开记,而不是只有一个总时长——总时长里混了加载,会高估识别本身的延迟。这一条和 A 类讲"逐环打点"是一脉相承的:延迟分析的价值不在总数,而在分段。另外,长音频识别时建议记录"音频时长 vs 识别耗时"的比值,它能直接回答"是否实时"这个产品级问题,比吞吐数字直观得多。

六、NPU 卸载:到底卸了什么

6.1 三个 NPU 后端

whisper.cpp 明确写出的 NPU 卸载有三类:Intel OpenVINO、AMD Ryzen AI(VitisAI)、华为 CANN(昇腾)。构建选项分别是 -DWHISPER_OPENVINO=1、-DWHISPER_VITISAI=1、-DGGML_CANN=1。

6.2 卸载的代价是数据搬运

NPU 卸载不是"免费加速"。encoder 被卸到 NPU 后,输入要先搬到 NPU 可见的内存、输出再搬回来,这一来一回的数据搬运本身有成本。在音频这种"单次计算量不算巨大"的任务上,搬运成本有时会吃掉加速收益。是否真的更快,必须实测,尤其是短音频场景——短音频的搬运占比更高。一个可操作的判据:在目标板上分别测"强制 CPU"与"启用 NPU"下,同一段 10 秒与 60 秒音频的端到端耗时,画出"音频长度—节省时长"曲线。曲线在短音频处若为负(NPU 反而更慢),说明搬运成本主导,卸载不划算;长音频处转正,才值得开。

6.3 边界:不是所有 NPU 都列在里面

whisper.cpp 的 NPU 后端清单里没有 Qualcomm QNN。如果目标是高通平台开发板,whisper.cpp 路线大概率要退回 CPU/其他后端跑,而 sherpa-onnx 路线明确支持 QNN。这一条是两条路线选型时的硬差异,必须写清。

6.4 短音频场景的搬运占比

用一个粗略的推算说明为什么短音频不划算:假设一次识别的纯计算只要 200ms,但数据搬到 NPU 再搬回花了 150ms,那么"卸载"只省了 50ms,却引入了额外的复杂度和依赖。音频任务普遍单次计算量不大,NPU 卸载的收益高度依赖模型规模与音频长度——大模型 + 长音频才更可能正向收益。这条必须用真机实测验证,不能假设"配了就快"。所以判断 NPU 卸载值不值,做法和确认后端启用一样:在长/短两类音频上分别测"开 NPU"与"关 NPU"的端到端耗时,看差值是否覆盖搬运成本。只有差值稳定为正,卸载才算成立;否则宁可走 CPU,少一层依赖、少一处出错可能。


6.5 一个对照结论怎么写(模板,填实测)

把 7.2 的表填完后,结论建议写成这种结构(括号内为待填的实测/推算示例):

  • 在 __ 板子上,whisper.cpp(模型 __,量化 __)的单条 30s 识别耗时为 __,CER 为 __;ONNX 路线(模型 __,EP __)耗时 __,CER __。
  • 当音频短于 __ 时,两者差异在 __ 以内,选构建更省心的;当音频长于 __ 且目标板有 QNN 时,ONNX 路线在耗时上更快 __。
  • NPU 生效情况:whisper.cpp 在目标板 __(未生效/走 CPU),ONNX 路线 __(生效/未生效)。

注意这条模板里的数字全部来自表,不凭印象写"谁更强",而是写"在什么条件下差多少"。读者拿到的是可复现的判断依据,而不是一句结论。

七、对照实验设计:两条路线怎么比

7.1 控制变量

要得出"哪条路线更适合我的板子",必须控制变量:

变量控制方式
音频集同一批固定测试音频,内容不同(避免缓存)
设备同一块板子,相近温度
模型能力尽量选能力相当的模型(同量级、同语言)
量化档两条路线各取能接受的量化档,记录档位

7.2 记录三张表

指标whisper.cpp(待真机填入)ONNX Runtime 路线(待真机填入)测量条件
模型体积待填待填量化档
单条识别耗时待填待填固定测试集
字错误率 CER待填待填固定测试集

上表为对照模板,实测列待真机填入,发布前不许留空、不许用引用或推算数据冒充实测。两条路线若能力不完全对等,要在表下注明差异,避免"用更强的模型压更弱的"那种不公平的对照。

7.3 结论怎么写才诚实

诚实的结论不是"路线 A 明显占优路线 B",而是给出适用条件:例如"在短指令场景、目标板上无 QNN 卸载时,路线 A 的 CPU 实现延迟更低;在长音频、目标板有 QNN 时,路线 B 的 NPU 卸载更稳"。条件写清楚,读者才能判断哪条适合自己。

7.4 一个选型决策树

把上面的判断收敛成树,选型时按顺序问:

目标板有 Qualcomm QNN 且要压榨算力? ──是──▶ 倾向 ONNX Runtime 路线(sherpa-onnx)
      │否
需要中英混说 / 多语种 / 与 FunASR 同源对照? ──是──▶ ONNX Runtime 路线更顺
      │否
只要一个自包含、零依赖、好编译的英文 ASR? ──是──▶ whisper.cpp 更省心
      │否
是否有 Intel/AMD/华为平台且要 NPU? ──是──▶ 看对应后端是否在 whisper.cpp 列表
      │否
两者都能跑 → 回到 7.2 的控制变量对照,用实测数字定

这棵树的关键是:先问硬件和场景,再问引擎。脱离硬件谈"哪个引擎好"没有意义,因为两者的加速面根本不同。


八、边界

  • 多语言与方言:不同路线对中文方言、中英混说的支持差异明显,对照时要专门准备这类测试句,不能只念标准普通话。
  • 长音频的内存增长:ASR 的内存不只看权重,还看上下文与中间状态;长录音下内存可能线性增长,要测最大输入长度下的占用。
  • NPU 卸载的适用条件:短音频、搬运成本高的场景,NPU 未必更快;卸载是否生效也要实测确认,不能假设"配置了就用了"。
  • 构建与运行的资源差:在板子上编译 whisper.cpp 比运行更挑内存,资源紧张的板子要考虑交叉编译或在 x86 预编译,这点在整机资源预算里容易被忽略。

九、常见问题(FAQ)

问:whisper.cpp 是 MIT 许可吗?
是的,README 顶部许可证徽章明确标注 MIT,链接到 opensource.org/licenses/MIT。

问:whisper.cpp 支持高通 NPU 吗?
README 明确列出的 NPU 后端是 Intel OpenVINO、AMD Ryzen AI(VitisAI)、华为 CANN(昇腾),未列出 Qualcomm QNN。高通平台上的 whisper.cpp 路线大概率走 CPU 或其他后端。

问:量化会损害识别质量吗?
会,但退化不均匀——数字、专有名词、弱信号更敏感。量化档位要在目标场景的测试句集上实测 CER 再定,不能只看体积。

问:whisper-server 的默认端口是 8080 吗?
README 只在 Docker 示例里出现 -p "8080:8080" 的映射和 curl .../inference 调用,并未声明默认端口;写稿应写成"示例中映射宿主 8080",接口路径是 /inference,不是常见的 /v1/...。

问:OAI-like 和 OpenAI 兼容一样吗?
不一样。"类 OpenAI"只说明形态接近,字段与路径不一定对齐。用标准 OpenAI SDK 对接前要把请求/响应字段逐条对一遍。

问:基准测试为什么不能用同一段音频反复跑?
因为缓存和预热会让后续结果失真。测试集要用不同内容,且第一轮(加载与预热)通常单独处理或标记,不计入统计。

问:部分量化(混合精度)值得做吗?
值得在精度敏感的场景试。encoder 与 decoder 对量化的敏感度不同,保留少数敏感层较高精度、其余压低档,能比"全量同一档"在同等体积下保更多质量。但要逐模型实测,没有通用配方。

问:把识别做成常驻服务,占多少额外资源?
服务常驻会让模型和进程一直占内存,首字更快但空闲也占资源。这个常驻占用要和 VAD/TTS/LLM 一起算进整机内存账,不能只看识别本身。


问:在板子上编译 whisper.cpp 会 OOM 吗?
有可能。ggml 部分源文件编译较吃内存,板子内存紧时用 cmake --build build -j 1 单线程编,或先在 x86 机器上交叉编译。编译比运行更挑资源,别以为能跑就能编。

问:whisper.cpp 和 sherpa-onnx 能共存吗?
能,它们是独立库,可以都在板子上。但没必要——同时引两个 ASR 引擎只会让依赖和内存更重。对照实验做完选定一条路线后,另一条应该从运行时移除。

问:量化后的模型还能用 whisper-cli 直接跑吗?
能,量化产物就是换了个文件名的模型,调用方式和全精度一样,只是 -m 指向 -q5_0.bin 那个文件。所以实验时全精度和量化可以共用同一套调用代码,只换路径。

问:whisper.cpp 能做真正的流式识别吗?
模型本身是非流式的,所谓"实时"是靠 whisper-stream 每半秒送一段 + VAD 切句模拟出来的。要接入正式链路,建议在 VAD 句级切分上做伪流式,而不是期待模型原生边说边出字。

问:whisper.cpp 识别中文怎么样?
multilingual 模型支持中文,但中文场景的方言、中英混说、专有名词仍是实测重点,不能只看英文 demo。对照实验的测试集要专门准备中文相关句。

问:whisper.cpp 自动语言检测会拖慢首字吗?
会。自动检测要先跑一遍前向判断语种,等于多一次前向。场景语言固定时显式指定 language 能省掉这一步,端侧首字延迟更稳。

问:whisper.cpp 输出能直接对接 小智 的文本队列吗?
能,它输出的是纯文本。接入时把识别文本接到编排层的文本队列即可,难点不在格式而在前面说的采样率规整和伪流式切句。

问:whisper.cpp 的 .bin 模型和 ONNX 模型能互换吗?
不能。两者是不同框架的格式,分别代表 ggml 自包含路线与 ONNX Runtime 路线。选了哪条路线就用那条路线的模型格式,互换意味着换引擎。

问:做对照实验,一次要跑多少条音频才有统计意义?
没有固定数,但原则是用"覆盖边界"的测试集而非"凑数量"。至少覆盖:标准普通话、带口音、中英混说、含专有名词、短句(<3s)、长句(>20s)、有背景噪声。每条类别跑若干条取中值与 P90,比随机跑一百条更有意义。

问:whisper.cpp 在 ARM 板子上 CPU 跑,能实时吗?
取决于模型尺寸与板子算力,没有统一答案。tiny/base 量级在较强 ARM 上接近实时是可能的,large 基本不现实。能否实时要用 5.8 讲的"音频时长 vs 识别耗时"比值在真机上测,不要凭 star 数或 x86 上的表现推算。

问:量化能同时减体积和提速吗?
通常能减体积和内存带宽,从而提速,但提速幅度取决于算子是否受带宽限制。受计算限制的部分提速有限。要和 3.3 的实验结合起来看,不能默认"量化必快"。

问:whisper.cpp 的模型去哪下载?
模型在官方仓库的 models 目录下,按尺寸(tiny/base/small/…)与语言(en/multilingual)组织。具体下载链接与命名以官方仓库当前版本为准,下载后记录实际文件名与体积写入 env.txt,不要凭记忆引用。

问:板子上有 GPU(如 Adreno),whisper.cpp 能用 Vulkan 加速吗?
理论上 Vulkan 后端可以,但前提是板子的 GPU 驱动支持 Vulkan 且 whisper.cpp 的 Vulkan 路径在该驱动下能初始化。这同样要在真机验证——开了 Vulkan 后对比 CPU 的端到端耗时,确认有正向收益才启用,否则多一层依赖。

十、总结与可带走物

本篇把端侧 ASR 的两条工程路线摆在一起:whisper.cpp 代表 ggml 自包含路线,sherpa-onnx 代表 ONNX Runtime + 硬件 EP 路线。前者的工程化工具(bench、server、stream、quantize)非常完整,后者的硬件适配面(含 Qualcomm QNN)更贴合本系列的高通平台主线。

可带走的产出:量化实验设计(体积/质量/速度三方表)、对照实验的变量控制清单与三张表模板、NPU 卸载的"搬运成本"视角、一张选型决策树、以及"确认后端/NPU 是否真启用"的方法论。

最后落到两条纪律:一是量化档位要实测 CER,不能选最小就行;二是对照要控制变量、条件写清楚,结论写成"在什么条件下哪条更合适",而不是"谁更强"。


十一、附录:本地 ASR 对照实验记录模板

给一张可直接抄的模板,做对照实验时逐项填:

项whisper.cppONNX 路线测量条件
模型名/尺寸待填待填—
量化档待填待填—
模型体积待填待填du -sh
单条 10s 音频识别耗时待填待填固定音频
单条 30s 音频识别耗时待填待填固定音频
CER(固定测试集)待填待填含方言/混说
空闲内存占用待填待填free -h
峰值内存占用待填待填长音频
NPU 是否生效待填待填算子落点日志
设备温度(负载下)待填待填thermal 节点

这张表把"延迟/质量/资源/加速"四类指标收在一起,对照结论直接基于它写,避免凭印象。所有"待填"在真机测完再填,发布前不许留空。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐