编码之旅
用键盘述说着工作和生活中的点点滴滴

离线语音唤醒调研

Published on
/24 分钟读/---

简介

语音唤醒在学术上被称为keyword spotting(简称KWS),给它做了一个定义:在连续语流中实时检测出说话人特定片段。

这里要注意,检测的“实时性”是一个关键点,语音唤醒的目的就是将设备从休眠状态激活至运行状态,所以唤醒词说出之后,能立刻被检测出来,用户的体验才会更好。

那么,该怎样评价语音唤醒的效果呢?通行的指标有四个方面,即唤醒率、误唤醒、响应时间和功耗水平:

➤唤醒率,指用户交互的成功率,专业术语为召回率,即recall。

➤误唤醒,用户未进行交互而设备被唤醒的概率,一般按天计算,如最多一天一次。

➤响应时间,指从用户说完唤醒词后,到设备给出反馈的时间差。

➤功耗水平,即唤醒系统的耗电情况。很多智能设备是通过电池供电,需要满足长时续航,对功耗水平就比较在意。

技术路线

经过长时间的发展,语音唤醒的技术路线大致可归纳为三代,特点如下:

第一代:基于模板匹配的KWS

训练和测试的步骤比较简单,训练就是依据注册语音或者说模板语音进行特征提取,构建模板。测试时,通过特征提取生成特征序列,计算测试的特征序列和模板序列的距离,基于此判断是否唤醒。

基于模板匹配的KWS

第二代:基于HMM-GMM的KWS

将唤醒任务转换为两类的识别任务,识别结果为keyword和non-keyword。

基于HMM-GMM的KWS

第三代:基于神经网络的方案

神经网络方案又可细分为几类,第一类是基于HMM的KWS,同第二代唤醒方案不同之处在于,声学模型建模从GMM转换为神经网络模型。 第二类融入神经网络的模板匹配,采用神经网络作为特征提取器。第三类是基于端到端的方案,输入语音,输出为各唤醒的概率,一个模型解决。

基于神经网络的KWS

唤醒的难点

语音唤醒的难点,主要是低功耗要求和高效果需求之间的矛盾。

一方面,目前很多智能设备采用的都是低端芯片,同时采用电池供电,这就要求唤醒所消耗的能源要尽可能的少。

另一方面,用户对体验效果的追求越来越高。目前语音唤醒主要应用于C端,用户群体广泛,且要进行大量远场交互,对唤醒能力提出了很高要求。

要解决两者之间的矛盾,对于低功耗需求,我们采用模型深度压缩策略,减少模型大小并保证效果下降幅度可控;而对于高效果需求,一般是通过模型闭环优化来实现。先提供一个效果可用的启动模型,随着用户的使用,进行闭环迭代更新,整个过程完成自动化,无需人工参与。

典型应用

语音唤醒的应用领域十分广泛,主要是C端产品,比如机器人、音箱、汽车等。比较有代表性的应用模式有如下几种:

➤传统语音交互:先唤醒设备,等设备反馈后(提示音或亮灯),用户认为设备被唤醒了,再发出语音控制命令,缺点在于交互时间长。

➤One-shot:直接将唤醒词和工作命令一同说出,如“叮咚叮咚,我想听周杰伦的歌”,客户端会在唤醒后直接启动识别以及语义理解等服务,缩短交互时间。

➤Zero-shot:将常用用户指定设置为唤醒词,达到用户无感知唤醒,例如直接对车机说“导航到科大讯飞”,这里将一些高频前缀的说法设置成唤醒词。

开源方案

方案说明备注官网
voskVosk 在 Kaldi 的基础上进行了封装和改进。它提供了更简单的 API,降低了开发门槛,让开发者能更方便地将语音识别功能集成到自己的应用程序中。通过设置grammar来配置固定的识别词实现唤醒功能或固定词检测教程
PocketSphinx是 CMU Sphinx 项目的一部分,是一个离线的语音识别和唤醒库,支持多种语言。它可以在资源有限的设备上运行,并且具有一定的自定义能力。通过自带的关键词检测方法addKeyphraseSearch实现Android教程
Sherpa-onnx基于微软开发的 ONNX 运行时的开源语音处理库,除了语音识别、语音合成等功能外也提供了自定义关键词识别。提供了两个模型分别支持英文和中文的功能。可以指定任何关键字,无需重新训练模型教程
Snowboy轻量级、高度可定制的热词检测引擎,由 KITT.AI 团队开发,主要用于在资源受限的设备上实现语音唤醒功能。Snowboy 提供了一个在线工具,能帮助你训练自定义的唤醒词模型。你只需访问 Snowboy 官网,按照指引录制包含唤醒词的语音样本,系统就会自动训练出对应的模型文件(.pmdl 或 .umdl)。已不在维护(被百度收购)商用收费需要联系snowboy@kitt.aisnowboy模型训练
Porcupine是一款高性能、低开销的端到端唤醒词引擎。超过免费额度收费
wekws采用端到端的深度学习模型,高效、低延迟的实时语音唤醒服务。不同的唤醒词需要自行训练导出模型。官网

Vosk介绍

Vosk 继承了 Kaldi 的语音识别能力,并在此基础上进行了优化和改进,使其更适合在各种设备上实时运行。Vosk 对 Kaldi 的部分功能进行了封装,提供了更简单易用的 API,降低了开发门槛,让开发者可以更轻松地将语音识别功能集成到自己的应用程序中。

接口说明

  1. SetMaxAlternatives

    SetMaxAlternatives(int maxAlternatives)方法用于设置语音识别结果中返回的最大替代句子数量。这个设置允许你获取到不仅仅是最可能的识别结果,还可以得到其他可能的替代结果。例如,如果你设置maxAlternatives为3,那么识别器将尝试返回最多三个识别结果,按可能性排序。

  2. setWords

    启用 / 禁用单词级别的详细信息。当设置为true时,识别结果将包含每个单词的时间戳和置信度。

    • 时间戳分析:了解每个单词在音频中的出现位置(用于语音对齐或字幕生成)。
    • 置信度过滤:丢弃低置信度的单词,提高整体准确率。
  3. setSpeakerModel

    方法允许你设置一个说话人识别模型。这使得Vosk不仅能够识别说话的内容,还能识别是谁在说话。这对于需要区分不同说话人或执行说话人验证的应用非常有用。Speaker Model是一个专门的模型,需要与语音识别模型一起使用。

  4. setGrammar

    允许你定义一个语法列表,识别器将只识别列表中的词或短语。这对于创建有限词汇或命令控制的应用非常有用,因为它可以大大提高特定词汇的识别准确率并减少错误识别。

PocketSphinx介绍

接口说明

processRaw(short[] SDATA, long NSAMP, boolean no_search, boolean full_utt)

  1. byte[] buffer
  • 含义:包含原始音频数据的字节数组。
  • 要求
    • 音频格式必须与解码器配置一致(默认 16kHz、16 位、单声道、小端序)。
    • 通常来自麦克风输入或音频文件读取。
  1. int length
  • 含义:要处理的字节数(从 offset 开始)。
  • 注意
    • 需确保 offset + length ≤ buffer.length
    • 建议长度为 10ms 音频的整数倍(16kHz 下为 320 字节)。
  1. boolean no_search
  • 含义:是否仅提取声学特征,不进行语言模型搜索。
  • 适用场景
    • true:用于预提取特征(如保存特征用于后续离线处理)。
    • false:正常识别流程,提取特征并进行语音搜索。
  1. boolean full_utt
  • 含义:是否将当前数据视为完整的语音 utterance。
  • 行为
    • true:处理完当前数据后立即结束 utterance,触发识别结果回调。
    • false:继续累积数据,等待后续调用(适用于流式识别)。

setKeywordThreshold(float threshold)

关键词阈值,控制着关键词搜索(Keyphrase Search)的敏感度。决定关键词被触发的难度。值越小,系统越敏感,更容易触发;值越大,系统越严格,需要更清晰的发音才能触发。

该值是声学模型得分的对数概率阈值(log-probability),单位为 1e-10。

理论范围:1e-100 到 1e-1(即 10^(-100) 到 10^(-1))。

实际有效范围:通常在 1e-50 到 1e-5 之间,具体取决于:

  • 关键词长度:较长的关键词需要更小的阈值(如 1e-40)。
  • 环境噪声:嘈杂环境需要更大的阈值(如 1e-10)。
  • 发音清晰度:模糊发音需要更小的阈值。

addGrammarSearchaddNgramSearch 和 addAllphoneSearch 是三种不同的搜索模式,适用于不同的语音识别场景。以下是它们的核心区别和适用场景:

1. addGrammarSearch(语法搜索)

特点

  • 基于有限状态语法(FSG):使用预定义的语法规则限制可能的词汇组合,适合严格结构化的命令。
  • 轻量级:识别速度快,资源消耗少,适合嵌入式设备或低功耗场景。
  • 高准确率:由于限制了搜索空间,对语法内的命令识别准确率高。

适用场景

  • 固定命令集:如智能家居控制("打开灯光"、"关闭电视")。
  • 数字拨号:如电话号码、航班号。
  • 表单填写:如日期、姓名输入。

示例代码

// 添加语法搜索(需先创建gram文件)
decoder.addGrammarSearch("commands", "/path/to/grammar.gram");
decoder.setSearch("commands");

语法文件示例(JSGF)

#JSGF V1.0;

grammar commands;

public <command> = (turn on | turn off) (the light | the fan | the TV);

2. addNgramSearch(N 元语法搜索)

特点

  • 基于统计语言模型:使用大规模文本训练的 N-gram 模型,预测单词序列的概率。
  • 灵活性高:支持更自然、开放的语言表达,但需要更大的模型文件。
  • 资源消耗大:模型文件通常较大(数十 MB 到 GB 级别),计算复杂度高。

适用场景

  • 自由对话:如语音助手、听写应用。
  • 长文本识别:如会议记录、语音笔记。
  • 领域特定语言:如医疗术语、法律条文。

示例代码

// 添加N-gram搜索
decoder.addNgramSearch("dictation", "/path/to/lm.bin");
decoder.setSearch("dictation");

3. addAllphoneSearch(全音素搜索)

特点

  • 基于音素级别识别:直接识别语音中的基本音素(phonemes),而非完整单词。
  • 无词典依赖:适用于未知词汇或需要细粒度语音分析的场景。
  • 低层级输出:输出音素序列,需要额外处理才能转换为语义。

适用场景

  • 语音合成(TTS):生成自然语音。
  • 语音学研究:分析发音特征。
  • 语音密码验证:基于发音特征而非单词内容。

示例代码

// 添加全音素搜索
decoder.addAllphoneSearch("phones", "/path/to/phone.lm");
decoder.setSearch("phones");

输出示例

识别结果(音素序列):[h ə l oʊ] → 对应单词 "hello"
  1. 对比表格
特性addGrammarSearchaddNgramSearchaddAllphoneSearch
搜索单位单词(基于语法规则)单词(基于统计模型)音素(语音基本单位)
模型大小小(几十 KB)大(几十 MB 到 GB)中等(取决于音素数量)
准确率高(语法内)中等到高(取决于语料)低(需后续处理)
响应速度中等
适用场景固定命令、数字输入自由对话、长文本语音合成、语音学研究
典型文件格式JSGF、FSGARPA、BINPhone LM
  1. 性能优化建议
  • 优先使用语法搜索:对于已知命令集,使用 addGrammarSearch 可显著降低资源消耗。
  • 限制 N-gram 模型大小:使用 cmuclmtk 工具修剪模型(如 sphinx_lm_convert -i large.lm -o small.lm -prune 0.000001)。
  • 避免不必要的音素搜索:仅在需要细粒度语音分析时使用 addAllphoneSearch

根据具体应用场景选择合适的搜索模式,可在准确率和性能之间取得最佳平衡。

Sherpa-Onnx介绍

KeywordSpotterConfig参数说明

  1. maxActivePaths
  • 功能:这个参数用于控制解码器在解码过程中所保留的最大活跃路径数量。在束搜索解码时,解码器会探索多种可能的路径,而 maxActivePaths 限制了同时保留的路径数目。
  • 作用
    • 性能优化:通过限制活跃路径数量,能减少内存使用和计算量,提升解码速度。
    • 避免过拟合:防止解码器在搜索过程中陷入过多不必要的路径,从而避免过拟合。
  • 取值建议:若取值过小,可能会遗漏一些潜在的正确路径,降低关键词检测的准确率;若取值过大,则会增加计算资源的消耗。
  1. keywordsScore
  • 功能:该参数为每个关键词预先设定了一个得分。在关键词检测时,模型会为每个检测到的关键词给出一个得分,而 keywordsScore 可作为一个基础得分或者权重,对最终的关键词得分产生影响。可以把它理解为对每个关键词重要性或者先验概率的一种量化表示。
  • 作用
    • 调整关键词优先级:针对不同的关键词,可设置不同的 keywordsScore,以此调整它们在检测结果中的优先级。
    • 优化检测策略:在某些场景下,能够通过调整 keywordsScore 来优化关键词检测的策略。
  • 取值建议:取值范围通常为实数,可根据关键词的重要性和实际需求进行调整。例如,对于重要的关键词,可以设置较高的 keywordsScore
  1. keywordsThreshold
  • 功能:此参数定义了关键词检测的得分阈值。只有当关键词的得分超过 keywordsThreshold 时,才会被判定为检测到该关键词。
  • 作用
    • 控制误检率:通过提高阈值,可以减少误检的情况,但可能会增加漏检的概率;降低阈值则相反。
    • 平衡检测性能:需要在误检率和漏检率之间找到一个平衡点,以达到最佳的检测性能。
  • 取值建议:取值范围通常为实数,具体取值要根据模型的输出特性和实际应用场景来确定。可以通过实验不同的阈值,观察检测结果的误检率和漏检率,进而选择合适的阈值。
  1. numTrailingBlanks
  • 功能:该参数规定了在检测到关键词后,需要连续出现多少个空白帧(即无关键词的帧),才认为关键词检测结束。
  • 作用
    • 消除尾音干扰:避免因音频中的尾音或者噪声被误判为关键词的一部分。
    • 准确界定关键词边界:有助于更精确地确定关键词的起始和结束位置。
  • 取值建议:取值为正整数,具体数值需要根据音频的特性和实际应用场景来调整。如果音频中尾音较长或者噪声较多,可以适当增大 numTrailingBlanks 的值。

Snowboy介绍

SetSensitivity

  • 含义:该方法用于设置唤醒词检测的灵敏度。灵敏度是一个衡量唤醒词检测难易程度的指标,其取值范围通常在 0 到 1 之间(不同版本可能会有细微差异),数值越大表示检测越灵敏,即更容易触发唤醒;数值越小则表示检测越不灵敏,需要更清晰、更准确的唤醒词发音才能触发。
  • 作用:通过调整灵敏度,可以在不同的环境和使用场景下优化唤醒词检测的效果。在安静的环境中,可以适当降低灵敏度以减少误触发;而在嘈杂的环境中,可以提高灵敏度以确保能够及时检测到唤醒词。

SetAudioGain

  • 含义SetAudioGain 方法用于设置音频增益。音频增益是对输入音频信号的放大倍数,通过调整增益可以增强或减弱输入音频的音量。其取值通常是一个大于 0 的浮点数,值大于 1 表示放大音频信号,值小于 1 表示缩小音频信号。
  • 作用:在某些情况下,输入音频的音量可能过低或过高,导致唤醒词检测不准确。通过设置音频增益,可以调整输入音频的音量到一个合适的水平,从而提高唤醒词检测的性能。

ApplyFrontend

  • 含义ApplyFrontend 方法用于启用或禁用前端音频处理功能。前端音频处理是一系列对输入音频进行预处理的操作,例如降噪、回声消除等,目的是提高音频的质量,从而提升唤醒词检测的准确性。该方法接受一个布尔值作为参数,True 表示启用前端音频处理,False 表示禁用。
  • 作用:在嘈杂的环境中,启用前端音频处理可以有效地去除背景噪声和回声,使唤醒词更加清晰可辨,从而提高检测的准确性。但在某些情况下,前端音频处理可能会引入一些额外的延迟或失真,因此需要根据实际情况选择是否启用。

结论

通过综合比较vosk和PocketSphinx在唤醒效果、性能和开源能力方便比较适合。其他方案或者是收费、或者已经不在维护,或者效果不是很理想。