创建日期:2026-10-13 | 最近更新:2026-10-13 事实核对基于 sherpa-onnx 1.13.8(
sherpa-onnx-node原生 addon;node v24.19.0、Linux x64)。模型体积、RTF、内存均为本机实测,脚本与完整日志留档于source/sherpa-onnx-lab/。
sherpa-onnx 101(第 1 篇 · 选型):给「中英混说」的程序员挑模型
程序员说话的样子,和朗读课文完全不一样。真实的一句需求是这样的:
「你把那个 useEffect 的依赖数组补一下,然后跑一遍 Vitest。」
一句话里三种成分:中文语法、英文技术词、中间还有「的」这种被夹在英文里的助词。这类「中英混说」(code-switching)是选模型时最该先过的一关——大部分中文 ASR 会把 useEffect 听成「由斯艾菲克特」,大部分英文 ASR 根本进不了中文。
这篇就干一件事:用一个可复现的对照实验,把「选哪个模型」这件事从感觉变成数字。
0. 先给结论
| # | 结论 | 证据 |
|---|---|---|
| 1 | 挑模型先看是否支持中英混说,这一条筛掉的比什么都多。中文模型听不懂英文术语,英文模型听不懂中文 | §2 |
| 2 | 「越新越准」成立,「越新越快」不成立:2026 年的 X-ASR int8 比 2023 年的 mobile 版准得多,但 RTF 反而更差(0.11 对 0.086) | §3 |
| 3 | 老的 2023 双语模型不是废的——它体积大 2.2 倍、速度还快 1.3 倍,只有在混说场景才明显掉队 | §3 |
| 4 | 纯英文场景别上大模型:Moonshine v2 tiny 只要 43 MB,RTF 0.026(38 倍实时) | §4 |
| 5 | 模型体积和内存不是一回事:148 MB 的 encoder 加载后占 324 MB RSS,差不多是文件体积的 2 倍 | §5 |
| 6 | 「对编程领域友好」这件事,模型本身给不了一半,另一半要自己补——热词和同音词替换才是听懂黑话的关键(第 3 篇) | §6 |
1. 编程场景对语音模型的三条硬要求
先把需求写清楚,不然选型就是凭感觉:
| 要求 | 具体是什么 | 不满足会怎样 |
|---|---|---|
| 中英混说 | 一句话里中英文自由切换,且英文词是中英混训语料里的词 | 术语被音译成汉字,代码没法用 |
| 低延迟 / 可流式 | 边说边出字,用户说的时候屏幕上就有反馈 | 只能"录完再转",交互退化成一个按钮 |
| 体积可控 | 前端要能下载、浏览器内存要扛得住 | 页面加载 400 MB,用户直接走人 |
这三条里,第一条是筛子,后两条是预算。
2. 模型家族速查:先分清「解码结构」
sherpa-onnx 支持的模型很多,但按解码结构分只有几大类。理解结构比记名字有用——它决定了你能不能用某些特性(比如热词只支持 transducer)。
| 家族 | 解码结构 | 代表模型 | 流式 | 支持热词 | 特点 |
|---|---|---|---|---|---|
| Transducer(zipformer 等) | encoder + decoder + joiner 三段式 | zipformer、X-ASR、Nemotron | 是 | 是 | 流式的标准答案,延迟可调(看 chunk 大小) |
| Paraformer | 非自回归 CTC-like | FunASR 的 paraformer | 是(有流式版) | 否 | 中文很强、解码快,但定制手段少 |
| CTC | 单塔 + CTC 头 | zipformer-ctc、NeMo CTC、Wenet CTC | 是 | 否 | 结构最简单,可配 HLG 图解码 |
| Attention Encoder-Decoder | 编码器 + 自回归解码 | Whisper、Moonshine、Canary、FireRedASR | 否 | 否 | 多语种/鲁棒性强,但都只能整段跑 |
| LLM-based ASR | 语音编码器 + 语言模型 | Qwen3-ASR、FunASR Nano | 否 | 否 | 2026 年的新方向,重但上限高 |
如果你要的是「实时字幕」或「语音输入」,transducer 基本是唯一选择——它是唯一同时具备「原生流式」+「可热词定制」的结构。这条结论后面第 2、3 篇都会用到。
一个次要但实用的判断:中英混说的模型,词表通常标记为 cjkchar+bpe(汉字按字、英文按子词)。这个信息在配置热词时是必填项,第 3 篇会踩到。
3. 主实验:两代中英双语模型对照
拿两个都是「中英双语、流式、int8」的模型,跑同样四段音频:
| X-ASR 480ms int8 | zipformer bilingual mobile | |
|---|---|---|
| 发布 | 2026-06-05 | 2023-02-20 |
| 权重来源 | Gilgamesh-J/X-ASR | icefall(k2-fsa 官方) |
| chunk 大小 | 480 ms(decode_chunk_len_=48) | 320 ms(decode_chunk_len_=32) |
| 模型目录体积 | 163 MB(encoder 148 MB) | 360 MB |
| 单文件 encoder | 148 MB | — |
| 加载后常驻 RSS | 324 MB | 253 MB |
(两个模型的 release 资产名分别是 sherpa-onnx-x-asr-480ms-streaming-zipformer-transducer-zh-en-int8-2026-06-05 和 sherpa-onnx-streaming-zipformer-bilingual-zh-en-2023-02-20-mobile。)
3.1 四段音频的逐句对照
四段都是中英混说的真实口语(test_wavs/0..3.wav,取自模型包自带的测试集):
| 音频 | X-ASR 480ms int8(2026) | bilingual mobile(2023) | 谁对 |
|---|---|---|---|
| 0.wav(10.05s) | 昨天是 MONDAY TODAY IS 礼拜二 THE DAY AFTER TOMORROW 是星期三 | 昨天是 MONDAY TODAY IS LIBRAR THE DAY AFTER TOMORROW是星期三 | 新 |
| 1.wav(5.10s) | 这是第一种第二种叫呃与 ALWAYS ALWAYS 什么意思啊 | 这是第一种第二种叫呃与 ALWAYS ALWAYS什么意思啊 | 平 |
| 2.wav(4.69s) | 就是平凡的啊不认识接下来 FREQUENTLY 频繁的 | 这个是频繁的啊不认识记下来 FREQUENTLY频繁的 | 各有胜负 |
| 3.wav(8.83s) | 第一句是个什么时态加了 YES 是一般现代时对我们把它时态写上去 | 第一句是个什么时态加了 ES 是一般现在时对后面它时态写上 | 新 |
差别最刺眼的是 0.wav:「礼拜二」被 2023 版听成了英文单词 "LIBRAR"。这恰好是前面说的那个失效模式——中文词被强行塞进英文词表,输出一个不存在的英文单词。2026 版没犯这个错。
2.wav 上则是 2023 版赢了("这个/记下来"对,"平凡/接下来"错),说明新版不是全面碾压,只是整体更稳。
3.2 速度:新款反而更慢
| 音频 | X-ASR 480ms RTF | 2023 mobile RTF |
|---|---|---|
| 0.wav | 0.112 | 0.088 |
| 1.wav | 0.130 | 0.099 |
| 2.wav | 0.128 | 0.099 |
| 3.wav | 0.116 | 0.090 |
| 均值 | 0.121 | 0.094 |
2023 版快约 1.3 倍。 两者都是 2 线程、都是 int8、都在同一次 run.sh 里跑完,decode_chunk_len_ 分别是 48 / 32(开 debug: 1 时模型初始化会打出来)。合理解释是每步要算的帧更多、encoder 也更大(148 MB)——但这是我从数字和模型结构做的推断,不是官方说明,当参考不当结论。
3.3 这条对照给出的选型意见
- 只在中英混说场景用 → 选 2026 的 X-ASR,约 29% 的速度损失换明显更低的术语错误率,值。
- 纯中文或纯英文、且机器弱 → 2023 mobile 版性价比更高(快、内存也小一点)。
- 别只看发布时间。这两个模型的差距不在"新旧",而在"测什么数据"。
4. 纯英文侧:小模型能有多小
如果你的场景是纯英文(比如给英文播客做字幕),那 300 MB 的双语模型纯属浪费。Moonshine v2 tiny 量化版:
| 项 | 值 |
|---|---|
| 模型目录 | 43 MB(encoder_model.ort 12.7 MB + 合并后的 decoder) |
| 加载后 RSS | 162 MB |
| 音频 | 3.845 s(24 kHz,会被自动重采样到 16 kHz) |
| 识别结果 | Ask not what your country can do for you. Ask what you can do for your country. |
| 5 次耗时 | 0.109 / 0.106 / 0.104 / 0.103 / 0.100 s |
| RTF | 0.026(38 倍实时) |
一句话总结:43 MB 换来 38 倍实时的英文识别。 这是"放浏览器里也敢用"的量级。
这里有个配置字段的坑值得单独说:
// ✅ Moonshine v2 用这个字段名
modelConfig: {
moonshine: { encoder: '...encoder_model.ort', mergedDecoder: '...decoder_model_merged.ort' },
}
// ❌ 这样写会直接报错
modelConfig: {
moonshine: { encoder: '...', uncachedDecoder: '...', cachedDecoder: '...' },
}
报错信息其实很明确:
offline-moonshine-model-config.cc:Validate:133 Please provide --moonshine-preprocessor for v1
or --moonshine-merged-decoder for v2
SherpaOnnxCreateOfflineRecognizer:698 Errors in config
但 npm 包里的 types.js 只声明了 v1 的字段名(preprocessor / encoder / uncachedDecoder / cachedDecoder),照着 JSDoc 补全会踩坑。不信你看官方仓库 master 上的 Node 示例,用的就是 mergedDecoder。
5. 预算表:体积 ≠ 内存
把这一篇用到的模型摊平:
| 模型 | 磁盘目录 | 加载后 RSS | RTF | 适合 |
|---|---|---|---|---|
| Silero VAD | 0.6 MB | 74 MB | 0.0085 | 所有链路的前置 |
| KWS zipformer 3M(中英) | 39 MB | 93 MB | 0.029 | 本地待机唤醒 |
| Moonshine v2 tiny(英) | 43 MB | 162 MB | 0.026 | 英文识别 |
| X-ASR 480ms int8(中英) | 163 MB | 324 MB | 0.121 | 中英流式识别 |
| bilingual mobile(中英) | 360 MB | 253 MB | 0.094 | 中英流式识别(省算力) |
注意 RSS 常常接近文件体积的 1.5~2 倍(X-ASR:148 MB 的 encoder 撑起 324 MB 进程)。原因是 ONNX Runtime 会把权重加载进内存、还有运行时的工作区。做前端预算时按「文件体积 × 2」估比按文件体积估安全得多。
另外注意 360 MB 的模型 RSS 比 163 MB 的还小——因为前者目录里塞了 fp32 和 int8 两套权重,实际只加载了 int8 那套。按目录体积做预算会高估。真要精算,看单个 encoder 文件。
6. 模型给不了的另一半
到这里有个必须说清楚的事:上面的模型没有一个认识 useEffect 或 Vitest。 我用本地 TTS 合成了一句真实需求("把 useEffect 的依赖数组补上,然后跑一遍 vitest"),丢给两个双语模型:
X-ASR 480ms → 八 USE EFFECT 依赖数组补上然后跑一遍 VIRUS
2023 mobile → 但 第一赖树族补上然后跑一遍 Y
useEffect被听成两个英文单词USE EFFECT(还算能忍);vitest被听成VIRUS(病毒)——这是最典型的失效:发音相近的常见词胜出,专业词被挤掉。
这不是模型选得不对,而是通用语音模型的天花板:训练语料里 "virus" 的出现频率比 "vitest" 高几个数量级。要解决它必须在解码阶段给模型提供上下文——也就是热词(contextual biasing)和同音词替换。
这就是第 3 篇的全部内容。先剧透一张表,它是在同一个 2023 mobile 模型上跑出来的:
| 模式 | 结果 |
|---|---|
greedy_search | 昨天是 MONDAY TODAY IS LIBRAR THE DAY AFTER TOMORROW是星期三 |
modified_beam_search | 昨天是 MONDAY TODAY IS LIVE AFTER TOMORROW是星期三 |
modified_beam_search + 热词「礼拜二」 | 昨天是 MONDAY TODAY IS礼拜二 THE DAY AFTER TOMORROW是星期三 |
一份两行的词表,把一句句法和语义都错了的话整个拉回来了。
7. 选型决策树
按顺序问下来就行:
1. 只要能听懂中文?
├─ 是 → 中文 zipformer / paraformer(体积小、中文准)
└─ 否 ↓
2. 中英混说,还是纯英文?
├─ 纯英文 → Moonshine v2 tiny(43 MB / RTF 0.026)够用就不要更大的
└─ 中英混说 ↓
3. 要流式吗?
├─ 要 → transducer 类,且必须是中英双语版本
│ ├─ 术语错误率优先 → X-ASR 480ms int8(2026)
│ └─ 速度/内存优先 → bilingual mobile(2023)
└─ 不要 → 可以上 Whisper / SenseVoice / FireRedASR 这类更重的模型
4. 术语还是不对?
→ 加热词(第 3 篇);中文术语还可以加同音词替换
下一篇把这条链路真正搭起来:流式怎么喂、长音频怎么切、端点检测为什么会让你的最后一句凭空消失。
复现命令:
cd source/sherpa-onnx-lab && bash download-models.sh && bash run.sh | tee run.log第 1、4 节的数据在run.log的「02 非流式 ASR」与「04 流式 ASR」两段;第 6 节的 TTS 对照在「05b」段。