大家遇到语音识别中的关键词识别/唤醒词识别是怎么处理的呢?
前言
最近接到一个开发需求:给我们的安卓 APP 加一个离线语音唤醒功能。要求很明确:纯离线可用、不需要授权、性能好、误触发率不能太高。
本以为这是个很简单的需求,毕竟现在 AI 这么发达,一个小小的唤醒词能有多难?结果没想到,我整整踩了两个星期的坑,试遍了市面上几乎所有的方案,最后才终于找到了一个完美的解决方案。
今天把这个过程分享出来,希望能帮到同样有这个需求的同学。
一、我试过的那些坑
1. 大厂云服务方案
第一个想到的就是阿里云、腾讯云、百度这些大厂的语音服务。
优点:识别准确率高,生态完善。
缺点:
- ❌ 必须联网:我们的核心需求就是离线使用,很多场景下用户没有网络
- ❌ 收费贵:按调用次数收费,量大了成本很高
- ❌ 隐私问题:用户的语音数据要上传到云端,有隐私风险
- ❌ 不灵活:自定义关键词需要额外付费,而且限制很多
直接 pass。
2. 大厂离线 SDK 方案
然后试了百度、讯飞的离线语音唤醒 SDK。
优点:识别准确率高,集成相对简单。
缺点:
- ❌ 需要授权:商业使用需要购买授权,年费动辄几万到几十万
- ❌ 体积大:一个 SDK 就要几十 MB,大大增加了 APP 的安装包大小
- ❌ 不透明:黑盒运行,出了问题根本不知道怎么调试
- ❌ 限制多:对使用场景和设备有很多限制
也 pass。
3. 通用开源语音识别方案
然后试了 Whisper、Kaldi 这些通用的语音识别模型。
优点:功能强大,完全开源,支持自定义关键词。
缺点:
- ❌ 模型太大:最小的 Whisper tiny 也有 70 多 MB,安卓上跑起来非常卡
- ❌ 功耗太高:CPU 占用率很高,手机发热严重,续航直接减半
- ❌ 延迟大:识别一次需要几百毫秒,体验很差
- ❌ 误触发率高:本来是做连续语音识别的,用来做唤醒词完全不合适
也 pass。
4. 专门的开源唤醒词方案
然后试了几个专门做唤醒词的开源项目:
- Porcupine:效果最好,但商业使用收费,而且很贵
- Snowboy:已经停止维护了,中文效果很差
- SpeechBrain:太复杂,学习成本高,而且模型也不小
就在我快要放弃的时候,我发现了OpenWakeWord。
二、OpenWakeWord:一个被低估的神器
OpenWakeWord 是一个专门为端侧设备设计的轻量级唤醒词引擎。
它的优点简直就是为我们的需求量身定做的:
- ✅ 模型极小:生成的模型不到 1MB
- ✅ 性能极高:安卓上 CPU 占用率不到 5%,几乎不耗电
- ✅ 纯离线:所有推理都在本地完成,不需要联网,完全保护用户隐私
- ✅ 跨平台:支持安卓、前端、Python、Windows、Linux、macOS
- ✅ MIT 许可证:完全免费,商业使用也没有任何限制
我当时简直欣喜若狂,以为终于找到了解决方案。结果没想到,更大的坑还在后面。
三、最大的痛点:误触发率
OpenWakeWord 什么都好,就是有一个致命的问题:中文误触发率高得离谱。
我用默认的方法训练了一个 "小燕子" 的模型,结果一天能误触发二三十次。有人在旁边聊天会触发,电视声音会触发,甚至有时候手机外放的声音都会触发。
我查了很多资料,试了各种方法:
- 下载了上万条杂音、环境音、日常对话作为负样本
- 调整置信度阈值,从 0.5 调到 0.9
- 增加平滑窗口和最小触发帧数
结果都没用。很多时候,模型会把完全不相关的声音判定为关键词,而且输出 100% 的置信度。
后来我才发现,问题出在 OpenWakeWord 的模型本身:它是在英文数据集上训练的,根本分不清中文的声母和韵母。而且它的训练方式会导致模型严重过自信,输出的置信度完全不可信。
四、我的解决方案:动态相对置信度算法
既然调阈值没用,那我就换个思路:不看绝对置信度,看相对置信度。
我发现,对于真正的关键词,模型的输出会呈现一个明显的峰值:关键词得分会突然从 0 升到 0.9 以上,然后再降下来。而误触发通常是平稳的高值,没有明显的峰值。
基于这个观察,我设计了一个动态相对置信度算法:
- 计算关键词得分的指数移动平均值(EMA),作为背景基线
- 当某一帧的得分超过背景基线的 3 倍时,才视为候选
- 再结合同一帧的关键词得分和非关键词得分的差值,做最终判断
python
运行
# 核心代码片段
background_ema = 0.0
alpha = 0.02 # 平滑系数
def detect(frame_output):
global background_ema
non_keyword_score, keyword_score = frame_output
# 更新背景基线
background_ema = alpha * keyword_score + (1 - alpha) * background_ema
# 动态相对置信度判断
if (keyword_score > 0.6 and
keyword_score - non_keyword_score > 0.5 and
keyword_score / background_ema > 3.0):
return True
return False
这个简单的改动,直接把误触发率降低了 90% !
从原来的每天二三十次,降到了每天 1-2 次。而且召回率几乎没有下降,真正的关键词几乎都能准确识别。
五、开源我的运行时代码
为了方便大家使用,我把优化后的运行时代码开源到了 GitHub 上:
这个运行时的特点:
- 纯 ONNX Runtime 实现,没有任何其他依赖
- 内置了我优化的动态相对置信度算法
- 与原版 OpenWakeWord 生成的模型 100% 兼容
- 目前已经在安卓、前端浏览器、Python三个环境下完整测试通过
- MIT 许可证,完全免费商用
使用非常简单,只需要几行代码:
python
运行
from onnx_wakeword import OnnxWakeWord
ww = OnnxWakeWord("xiaoyanzi.onnx")
while True:
audio_frame = get_audio_frame()
if ww.detect(audio_frame):
print("唤醒成功!")
安卓和前端的示例代码也都在 GitHub 仓库里,复制粘贴就能跑。
注:目前主要支持安卓、前端和 Python 环境,嵌入式设备(ESP32、树莓派等)的支持正在开发中,后续会逐步推出。
六、更进一步:一键生成模型
虽然有了运行时,但自己训练模型还是有点折腾:
- 需要配置复杂的 PyTorch 训练环境
- 需要收集和整理正样本、负样本数据集
- 需要反复调整各种训练参数和后处理参数
- 对电脑性能要求很高,没有好的显卡训练一次要几个小时
如果你不想折腾这些,可以用这个在线工具:
你只需要输入你的关键词,点击生成,15 分钟后就能下载一个训练好的 ONNX 模型。然后用我上面开源的运行时代码,直接就能在安卓、前端或 Python 环境中运行。
- ✅ 不需要任何机器学习知识
- ✅ 不需要自己收集任何录音
- ✅ 生成的模型完全离线可用
- ✅ 第一次生成完全免费
我自己实际测试了几十个关键词,效果都非常好。误触发率和我自己手动训练了几天的模型差不多,而且生成的模型和我的开源运行时完美兼容。
七、总结
经过两个星期的踩坑,我终于找到了一个完美的安卓离线语音唤醒解决方案:
- 用 OpenWakeWord 作为基础模型
- 用动态相对置信度算法解决误触发问题
- 用一键生成网站解决训练难的问题
- 用开源运行时解决部署难的问题
这个方案完全满足了我们的需求:纯离线、免费、轻量、低功耗、低误触发率。而且已经在我们的安卓 APP 上稳定运行了一个多月。
如果你也有离线语音唤醒的需求,不妨试试这个方案。有任何问题都可以在 GitHub 上提 issue,我会尽量回复。
最后,如果你觉得这个项目对你有帮助,欢迎给我的 GitHub 点个 star,这对我是最大的鼓励。
👉 **GitHub:github.com/voicute/onn…**👉 一键生成模型:www.voicute.com/
#语音识别 #安卓开发 #前端 #AI #开源 #关键词唤醒 #离线语音