sherpa-onnx语音命令定制:用户自定义词汇表实现
引言
在语音交互应用中,固定词汇表往往无法满足特定场景需求。Sherpa-ONNX作为一款高效的ONNX格式语音模型部署工具,提供了灵活的关键词定制功能,允许用户根据实际业务需求扩展词汇表。本文将系统介绍如何通过自定义词汇表实现专属语音命令识别,从环境准备到高级优化,全程基于本地部署,无需依赖云端服务。
技术背景
核心概念解析
| 术语 | 解释 | 作用 |
|---|---|---|
| 关键词 spotting(KWS) | 实时检测音频流中特定关键词的技术 | 唤醒设备、触发指令 |
| ONNX(Open Neural Network Exchange) | 开放格式的机器学习模型表示 | 跨平台模型部署 |
| 词汇表(Vocabulary) | 模型可识别的词语集合 | 限定识别范围,提升准确率 |
| 拼音分词(Pinyin Tokenization) | 将汉字转换为拼音音节序列的过程 | 解决语音到文本的映射 |
Sherpa-ONNX架构优势
Sherpa-ONNX的关键词识别模块采用流式处理架构,支持两种词汇扩展方式:
- 静态扩展:通过关键词文件批量导入
- 动态扩展:运行时实时添加词汇
其技术优势体现在:
- 低延迟:最小检测延迟可达200ms
- 轻量级:模型体积最小仅3.3M
- 跨平台:支持x86/ARM架构,Linux/macOS/Windows系统
- 多语言:原生支持中英文混合识别
环境准备
开发环境配置
# 克隆仓库
git clone https://gitcode.com/GitHub_Trending/sh/sherpa-onnx
cd sherpa-onnx
# 创建虚拟环境
python -m venv venv
source venv/bin/activate # Linux/macOS
# venv\Scripts\activate # Windows
# 安装依赖
pip install -r requirements.txt
预训练模型下载
| 模型名称 | 大小 | 适用场景 | 下载命令 |
|---|---|---|---|
| 流式Zipformer模型 | 3.3M | 轻量级设备 | ./scripts/download_kws_model.sh |
| 通用关键词模型 | 12M | 高精度场景 | ./scripts/download_kws_large_model.sh |
提示:模型默认下载至
sherpa-onnx-kws-zipformer-wenetspeech-3.3M-2024-01-01目录
自定义词汇表实现
1. 基础实现:通过文件定义关键词
关键词文件格式
创建custom_keywords.txt,遵循以下格式:
# 格式:拼音分词 @关键词 [阈值]
h e l l o @hello 0.5
n i h a o @你好 0.6
y ǎn y uán @演员 0.45
- 拼音分词:每个音节用空格分隔
- @符号:分隔拼音与关键词
- 阈值(可选):0-1之间,越高识别越严格
加载自定义关键词文件
import sherpa_onnx
def create_kws_with_custom_file():
return sherpa_onnx.KeywordSpotter(
tokens="./sherpa-onnx-kws-zipformer-wenetspeech-3.3M-2024-01-01/tokens.txt",
encoder="./sherpa-onnx-kws-zipformer-wenetspeech-3.3M-2024-01-01/encoder-epoch-12-avg-2-chunk-16-left-64.onnx",
decoder="./sherpa-onnx-kws-zipformer-wenetspeech-3.3M-2024-01-01/decoder-epoch-12-avg-2-chunk-16-left-64.onnx",
joiner="./sherpa-onnx-kws-zipformer-wenetspeech-3.3M-2024-01-01/joiner-epoch-12-avg-2-chunk-16-left-64.onnx",
num_threads=2,
keywords_file="./custom_keywords.txt", # 自定义关键词文件
provider="cpu",
)
2. 高级实现:动态添加关键词
在不修改文件的情况下,可通过API在运行时动态添加关键词:
def dynamic_keyword_demo():
kws = create_kws_with_custom_file()
# 场景1:添加单个关键词
stream1 = kws.create_stream("z ì d òng huà @自动化")
process_audio(stream1, "audio1.wav")
# 场景2:添加多个关键词(用/分隔)
stream2 = kws.create_stream("j i qì rén @机器人/k ōng tiáo @空调")
process_audio(stream2, "audio2.wav")
# 场景3:临时覆盖所有关键词
stream3 = kws.create_stream("*|y ùn xíng @运行") # *|表示清除现有关键词
process_audio(stream3, "audio3.wav")
3. 词汇表扩展:自定义Token处理
当需要添加模型未包含的新词时,需扩展tokens.txt:
# tokens.txt 示例片段
<eps> 0
<s> 1
</s> 2
a 3
ai 4
an 5
# ... 现有内容 ...
# 添加自定义拼音
xuan 1001
zhi 1002
hua 1003
注意:扩展token后需重新导出模型,具体方法参见项目
scripts/export_bpe_vocab.py脚本
完整代码示例
实时麦克风关键词识别
import argparse
import time
import numpy as np
import sherpa_onnx
import sounddevice as sd
def main():
parser = argparse.ArgumentParser()
parser.add_argument("--keywords-file", type=str, default="custom_keywords.txt")
parser.add_argument("--sample-rate", type=int, default=16000)
parser.add_argument("--num-threads", type=int, default=2)
args = parser.parse_args()
# 创建关键词识别器
kws = sherpa_onnx.KeywordSpotter(
tokens="./sherpa-onnx-kws-zipformer-wenetspeech-3.3M-2024-01-01/tokens.txt",
encoder="./sherpa-onnx-kws-zipformer-wenetspeech-3.3M-2024-01-01/encoder-epoch-12-avg-2-chunk-16-left-64.onnx",
decoder="./sherpa-onnx-kws-zipformer-wenetspeech-3.3M-2024-01-01/decoder-epoch-12-avg-2-chunk-16-left-64.onnx",
joiner="./sherpa-onnx-kws-zipformer-wenetspeech-3.3M-2024-01-01/joiner-epoch-12-avg-2-chunk-16-left-64.onnx",
num_threads=args.num_threads,
keywords_file=args.keywords_file,
provider="cpu",
)
# 麦克风音频回调函数
def audio_callback(indata, frames, time, status):
if status:
print(f"音频状态: {status}", file=sys.stderr)
# 转换为float32格式
audio_data = indata.flatten().astype(np.float32) / 32768.0
# 喂入音频流
stream.accept_waveform(args.sample_rate, audio_data)
# 处理识别
while kws.is_ready(stream):
kws.decode_stream(stream)
result = kws.get_result(stream)
if result:
print(f"检测到关键词: {result}")
kws.reset_stream(stream) # 重置流以继续检测
# 创建音频流
stream = kws.create_stream()
# 启动麦克风监听
with sd.InputStream(
samplerate=args.sample_rate,
channels=1,
dtype="int16",
callback=audio_callback
):
print("开始监听关键词... (按Ctrl+C停止)")
while True:
time.sleep(0.1)
if __name__ == "__main__":
main()
批量文件处理工具
def batch_process_audio_files(kws, audio_dir):
"""批量处理目录中的音频文件"""
results = {}
for audio_path in Path(audio_dir).glob("*.wav"):
samples, sample_rate = read_wave(str(audio_path))
stream = kws.create_stream()
# 添加尾部填充(确保关键词在结尾也能被检测)
tail_padding = np.zeros(int(0.66 * sample_rate), dtype=np.float32)
stream.accept_waveform(sample_rate, samples)
stream.accept_waveform(sample_rate, tail_padding)
stream.input_finished()
# 处理识别
detected = []
while kws.is_ready(stream):
kws.decode_stream(stream)
result = kws.get_result(stream)
if result:
detected.append(result)
kws.reset_stream(stream)
results[str(audio_path)] = detected
return results
性能优化策略
识别准确率调优
| 参数 | 作用 | 推荐值 |
|---|---|---|
| 关键词阈值 | 控制检测灵敏度 | 0.4-0.7 |
| 音频增益 | 提升弱音信号 | 1.5-3.0 |
| 窗口大小 | 平衡响应速度与准确率 | 200-500ms |
| 平滑系数 | 减少误检 | 0.3-0.5 |
代码优化示例
# 优化1:设置动态阈值(根据环境噪音调整)
def adaptive_threshold(noise_level):
if noise_level < -40: # 安静环境
return 0.45
elif noise_level < -20: # 中等噪音
return 0.55
else: # 嘈杂环境
return 0.65
# 优化2:多流并行处理
def parallel_process(kws, audio_files):
with concurrent.futures.ThreadPoolExecutor(max_workers=4) as executor:
futures = {executor.submit(process_single_file, kws, f): f for f in audio_files}
for future in concurrent.futures.as_completed(futures):
# 处理结果
常见问题解决
关键词冲突问题
当多个关键词拼音相似时,可通过权重设置解决:
# 关键词文件中设置权重(越高优先级越高)
n i h a o @你好 0.6;2 # 权重2
n i h a o a @你好啊 0.5;1 # 权重1
低置信度问题
若关键词频繁漏检,可尝试:
- 降低阈值(0.5→0.4)
- 优化拼音分词(如"j i q i @机器"改为"j i qì @机器")
- 录制多版本样本进行模型微调:
./scripts/finetune_kws.sh --data_dir ./custom_samples
资源占用优化
在嵌入式设备上可采用以下优化:
# 减少线程数
kws = sherpa_onnx.KeywordSpotter(num_threads=1)
# 降低采样率(可能影响准确率)
stream = kws.create_stream(sample_rate=8000)
# 关闭动态更新
kws.set_update_threshold(False)
应用场景案例
智能家居控制
# 智能家居关键词配置
keywords = """
k ā i dēng @开灯
gu ā n dēng @关灯
k ā i k ōng tiáo @开空调
gu ā n k ōng tiáo @关空调
t i á o g ā o wēn dù @调高温度
t i á o dī wēn dù @调低温度
"""
with open("smart_home_keywords.txt", "w") as f:
f.write(keywords)
工业语音指令
# 工业控制动态关键词管理
class IndustrialCommandManager:
def __init__(self, kws):
self.kws = kws
self.command_sets = {
"maintenance": "tíng jī @停机/xiū lǐ @修理",
"production": "kāi gōng @开工/jìn dù @进度",
"quality": "jiǎn chá @检查/pǐn zhì @品质"
}
def switch_mode(self, mode):
"""切换工作模式,加载对应指令集"""
if mode in self.command_sets:
return self.kws.create_stream(self.command_sets[mode])
raise ValueError(f"未知模式: {mode}")
总结与展望
核心功能回顾
Sherpa-ONNX的自定义词汇表实现主要通过三种方式:
- 文件配置:适合静态关键词集
- 动态注入:适合临时场景需求
- Token扩展:适合新增词汇场景
其技术优势在于:
- 无需重新训练模型即可扩展词汇
- 支持实时更新与多场景切换
- 轻量级设计适合边缘设备部署
进阶方向
- 上下文感知识别:结合对话语境动态调整关键词权重
- 多语言混合识别:扩展支持中英文以外的其他语言
- 个性化适应:通过少量样本学习用户发音特点
- 低功耗优化:针对电池供电设备的功耗优化
学习资源
- 官方文档:项目根目录下
docs/文件夹 - 示例代码:
python-api-examples/目录下的关键词识别示例 - 模型库:
./scripts/download_*脚本可获取更多预训练模型
通过本文介绍的方法,开发者可以快速实现专属语音命令系统,满足特定业务场景需求。无论是智能家居、工业控制还是移动应用,Sherpa-ONNX提供的灵活词汇扩展机制都能帮助开发者构建高效、准确的语音交互体验。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



