【语音识别部署】--- sherpa-onnx:让 ASR 模型跑得更快、跑在任何地方
上一篇 FunASR 实战 里,我们用 FunASR 跑通了完整的语音识别 pipeline。但有个问题:FunASR 依赖 PyTorch 运行时,模型加载慢、显存占用大、部署到边缘设备更是想都别想。
sherpa-onnx 解决的就是这个问题。它是 k2-fsa 项目组维护的推理加速库,基于 ONNX Runtime,支持把各种 ASR 模型(包括 FunASR 的 Paraformer、Whisper、Zipformer 等)导出成 ONNX 格式后高效推理。说直白点:FunASR 负责训练和实验,sherpa-onnx 负责部署和上线。
一、sherpa-onnx 的定位
sherpa-onnx 不是一个训练框架,它只做推理。它的核心卖点:
| 特性 | 说明 |
|---|---|
| 跨平台 | Linux / macOS / Windows / Android / iOS / 树莓派 / WebAssembly |
| 多语言绑定 | Python / C++ / Go / Rust / Java / C# / Kotlin / Swift / Dart |
| 不依赖 PyTorch | 只需要 ONNX Runtime,包体小、启动快 |
| 支持多种模型 | Paraformer / Whisper / Zipformer / NeMo / SenseVoice 等 |
| 流式 + 非流式 | 实时识别和离线识别都支持 |
| VAD 集成 | 内置 Silero VAD,可以和任意 ASR 模型组合 |
二、安装
bash
pip install sherpa-onnx
就这么简单。不需要装 CUDA 版的 PyTorch,不需要配 ModelScope。sherpa-onnx 自带 ONNX Runtime,CPU 推理开箱即用。
如果你需要 GPU 加速:
bash
# CUDA 11.x
pip install sherpa-onnx-cuda11
# CUDA 12.x
pip install sherpa-onnx-cuda12
验证安装:
bash
python -c "import sherpa_onnx; print(sherpa_onnx.__version__)"
三、模型下载
sherpa-onnx 的预训练模型托管在 GitHub Releases 上。以中文 Paraformer 为例:
bash
# 非流式 Paraformer(中文)
wget https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/sherpa-onnx-paraformer-zh-2023-09-14.tar.bz2
tar xvf sherpa-onnx-paraformer-zh-2023-09-14.tar.bz2
rm sherpa-onnx-paraformer-zh-2023-09-14.tar.bz2
# 流式 Paraformer(中英双语)
wget https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/sherpa-onnx-streaming-paraformer-bilingual-zh-en.tar.bz2
tar xvf sherpa-onnx-streaming-paraformer-bilingual-zh-en.tar.bz2
# Whisper(英文)
wget https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/sherpa-onnx-whisper-base.en.tar.bz2
tar xvf sherpa-onnx-whisper-base.en.tar.bz2
# Silero VAD 模型
wget https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/silero_vad.onnx
模型目录里通常包含:model.onnx(或 encoder/decoder 分离的文件)、tokens.txt。这两个文件是必需的。
四、非流式识别:Paraformer
非流式(offline)识别适合处理已录制好的音频文件,一次性送入整段音频,输出完整文本。
python
import wave
import numpy as np
import sherpa_onnx
def read_wave(path):
"""读取 16-bit 单声道 WAV 文件,返回 float32 采样点和采样率"""
with wave.open(path) as f:
assert f.getnchannels() == 1, "只支持单声道"
assert f.getsampwidth() == 2, "只支持 16-bit"
raw = f.readframes(f.getnframes())
samples = np.frombuffer(raw, dtype=np.int16).astype(np.float32) / 32768.0
return samples, f.getframerate()
# 初始化识别器
recognizer = sherpa_onnx.OfflineRecognizer.from_paraformer(
paraformer="./sherpa-onnx-paraformer-zh-2023-09-14/model.onnx",
tokens="./sherpa-onnx-paraformer-zh-2023-09-14/tokens.txt",
num_threads=4,
provider="cpu", # GPU 推理改成 "cuda"
)
# 读取音频
samples, sample_rate = read_wave("./test.wav")
# 创建流 → 送入音频 → 解码
stream = recognizer.create_stream()
stream.accept_waveform(sample_rate, samples)
recognizer.decode_stream(stream)
result = recognizer.get_result(stream)
print(f"识别结果:{result.text}")
批量解码
sherpa-onnx 支持同时解码多段音频,这在批量处理场景下很有用:
python
# 创建多个流
s1 = recognizer.create_stream()
s1.accept_waveform(sample_rate, samples)
s2 = recognizer.create_stream()
s2.accept_waveform(sample_rate, samples2)
# 一次性解码
recognizer.decode_streams([s1, s2])
print(recognizer.get_result(s1).text)
print(recognizer.get_result(s2).text)
五、流式识别:实时转写
流式(streaming/online)识别适合实时场景------麦克风录音、直播字幕、会议实时转写。音频一小块一小块地喂进去,识别结果实时输出。
python
import wave
import numpy as np
import sherpa_onnx
# 初始化流式识别器
recognizer = sherpa_onnx.OnlineRecognizer.from_paraformer(
tokens="./sherpa-onnx-streaming-paraformer-bilingual-zh-en/tokens.txt",
encoder="./sherpa-onnx-streaming-paraformer-bilingual-zh-en/encoder.int8.onnx",
decoder="./sherpa-onnx-streaming-paraformer-bilingual-zh-en/decoder.int8.onnx",
num_threads=2,
)
def read_wave(path):
with wave.open(path) as f:
raw = f.readframes(f.getnframes())
samples = np.frombuffer(raw, dtype=np.int16).astype(np.float32) / 32768.0
return samples, f.getframerate()
samples, sample_rate = read_wave("./test.wav")
stream = recognizer.create_stream()
# 模拟实时:每次送 100ms 的音频
chunk_size = int(0.1 * sample_rate)
offset = 0
while offset < len(samples):
chunk = samples[offset : offset + chunk_size]
stream.accept_waveform(sample_rate, chunk)
offset += chunk_size
while recognizer.is_ready(stream):
recognizer.decode_stream(stream)
if recognizer.is_endpoint(stream):
result = recognizer.get_result(stream)
print(f"一句话结束:{result.text}")
recognizer.reset(stream)
# 刷出剩余音频
tail_padding = np.zeros(int(0.66 * sample_rate), dtype=np.float32)
stream.accept_waveform(sample_rate, tail_padding)
stream.input_finished()
while recognizer.is_ready(stream):
recognizer.decode_stream(stream)
print("最终结果:", recognizer.get_result(stream).text)
🔴 重点 :is_endpoint() 是端点检测,当模型判断一句话说完了(有足够长的静音),返回 True。这时候你可以输出当前结果并 reset 流,开始识别下一句话。
六、VAD + 非流式 ASR:处理长音频的正确姿势
处理长音频时,直接整段送入非流式模型可能会出问题(内存占用、模型最大输入长度限制)。正确做法是用 VAD 先切分,再逐段识别。
sherpa-onnx 内置了 Silero VAD,用起来很方便:
python
import wave
import numpy as np
import sherpa_onnx
def read_wave(path):
with wave.open(path) as f:
raw = f.readframes(f.getnframes())
samples = np.frombuffer(raw, dtype=np.int16).astype(np.float32) / 32768.0
return samples, f.getframerate()
# 初始化 VAD
vad_config = sherpa_onnx.VadModelConfig()
vad_config.silero_vad.model = "./silero_vad.onnx"
vad_config.silero_vad.threshold = 0.5
vad_config.silero_vad.min_silence_duration = 0.5 # 静音超过 0.5s 认为一句话结束
vad_config.silero_vad.min_speech_duration = 0.25
vad_config.sample_rate = 16000
vad = sherpa_onnx.VoiceActivityDetector(vad_config, buffer_size_in_seconds=30)
# 初始化 ASR
recognizer = sherpa_onnx.OfflineRecognizer.from_paraformer(
paraformer="./sherpa-onnx-paraformer-zh-2023-09-14/model.onnx",
tokens="./sherpa-onnx-paraformer-zh-2023-09-14/tokens.txt",
num_threads=4,
)
# 读取音频
samples, sample_rate = read_wave("./long_meeting.wav")
# 送入 VAD
vad.accept_waveform(samples)
# 逐段识别
results = []
while not vad.is_empty():
segment = vad.front()
vad.pop()
stream = recognizer.create_stream()
stream.accept_waveform(sample_rate, segment.samples)
recognizer.decode_stream(stream)
text = recognizer.get_result(stream).text
if text.strip():
start_time = segment.start / sample_rate
results.append((start_time, text))
print(f"[{start_time:.1f}s] {text}")
这个模式在生产环境里非常实用------VAD 负责"什么时候有人说话",ASR 负责"说了什么",各司其职。
七、Whisper 模型支持
如果你之前用 OpenAI 的 Whisper,sherpa-onnx 也能跑 Whisper 的 ONNX 版本:
python
recognizer = sherpa_onnx.OfflineRecognizer.from_whisper(
encoder="./sherpa-onnx-whisper-base.en/base.en-encoder.int8.onnx",
decoder="./sherpa-onnx-whisper-base.en/base.en-decoder.int8.onnx",
tokens="./sherpa-onnx-whisper-base.en/base.en-tokens.txt",
language="en",
task="transcribe",
num_threads=2,
provider="cpu",
)
samples, sample_rate = read_wave("./test.wav")
stream = recognizer.create_stream()
stream.accept_waveform(sample_rate, samples)
recognizer.decode_stream(stream)
print(recognizer.get_result(stream).text)
int8 量化的 Whisper 模型在 CPU 上的速度比 PyTorch 原版快 3~5 倍,精度损失很小。
八、FunASR 模型导出到 sherpa-onnx
如果你用 FunASR 训练了自己的模型,想部署到 sherpa-onnx,流程是:
- 导出 ONNX:FunASR 支持直接导出 ONNX 格式
- 准备 tokens.txt:从模型配置中提取词表
- 用 sherpa-onnx 加载
python
# 用 FunASR 导出 ONNX
from funasr import AutoModel
model = AutoModel(model="FunAudioLLM/Fun-ASR-Nano-2512",
trust_remote_code=True,
device="cpu")
# 导出
model.export(type="onnx", quantize=True)
导出后的文件可以直接用 sherpa-onnx 的 OfflineRecognizer.from_paraformer() 加载。
九、性能对比:FunASR vs sherpa-onnx
我们在同一台机器(Intel i7-12700H, 无 GPU)上对 10 分钟中文音频做了简单测试:
| 指标 | FunASR (PyTorch) | sherpa-onnx (ONNX) |
|---|---|---|
| 模型加载时间 | ~8s | ~0.5s |
| 推理耗时(10min 音频) | ~45s | ~18s |
| 内存占用 | ~2.5GB | ~800MB |
| 包体大小 | ~2GB(含 PyTorch) | ~200MB |
sherpa-onnx 的优势在 CPU 场景下尤其明显。GPU 场景下差距会缩小,但模型加载速度和内存占用仍然有明显优势。
⚠️ 注意:以上数据仅供量级参考,不同硬件、不同音频内容会有差异。
十、常见问题排查
Q1:FileNotFoundError: model.onnx
模型文件路径不对。确认你解压后的目录结构,paraformer 和 tokens 参数都要指向正确的文件。
Q2:识别结果是空的
检查音频格式。sherpa-onnx 要求 16kHz、16-bit、单声道 WAV。如果你的音频是 MP3 或其他格式,先用 ffmpeg 转换:
bash
ffmpeg -i input.mp3 -ar 16000 -ac 1 -sample_fmt s16 output.wav
Q3:流式识别延迟高
调小 chunk_size。默认 100ms 是个不错的平衡点,对延迟敏感的场景可以试 60ms。
Q4:VAD 切得太碎 / 漏切
调整 min_silence_duration 和 threshold。阈值越高越严格(更难检测到语音),静音时长越长越不容易切分。
十一、总结
sherpa-onnx 是 ASR 模型部署的事实标准方案。它的核心价值:
- 去掉 PyTorch 依赖:模型加载从 8 秒降到 0.5 秒,内存占用砍 2/3。
- 跨平台:同一套模型文件,Python/C++/Android/iOS 都能跑。
- VAD + ASR 组合:内置 Silero VAD,长音频处理不再是问题。
- FunASR 无缝衔接:FunASR 训练的模型可以直接导出 ONNX 给 sherpa-onnx 用。
选型建议:
- 实验/调参阶段 → 用 FunASR,方便迭代
- 部署/上线阶段 → 用 sherpa-onnx,性能好、依赖少
- 实时场景 → sherpa-onnx 的流式识别 + VAD
验证清单
- ⭐ sherpa-onnx 安装成功:
python -c "import sherpa_onnx; print(sherpa_onnx.__version__)" - 模型下载完成:Paraformer / Whisper / VAD 模型文件就位
- 非流式识别跑通:Paraformer 识别测试音频
- 流式识别跑通:实时逐块送入音频并输出结果
- VAD + ASR 组合跑通:长音频被正确切分并逐段识别
- 音频格式确认:16kHz / 16-bit / 单声道 WAV
参考资源
- sherpa-onnx GitHub:https://github.com/k2-fsa/sherpa-onnx
- 模型下载:https://github.com/k2-fsa/sherpa-onnx/releases/tag/asr-models
- ONNX Runtime 文档:https://onnxruntime.ai/
- FunASR ONNX 导出:参考 FunASR 实战 第八节