唤醒词 / 关键词识别
SpeechWakeWord 模块在设备上运行关键词识别器:你注册一组短语,推入音频分段并接收检测结果。基于 icefall 的流式 Zipformer 转换器(3.49M 参数,Apache-2.0),通过 INT8 调色板量化编译为 CoreML。
仅支持英语
随附的检查点为 gigaspeech KWS 微调。非英语关键词需要单独的 icefall 微调并重新导出。
架构
| 阶段 | 详情 |
|---|---|
| fbank | kaldi 兼容(25 ms / 10 ms、Povey 窗、80 mel bins、high_freq=-400、无 CMVN) |
| 编码器 | 6 阶段因果 Zipformer2(128 维),45 个 mel 帧输入 → 8 帧输出(每帧 40 ms)——3.3 MB INT8 |
| 解码器 | 无状态转换器,BPE-500 词表,上下文大小 2——525 KB FP16 |
| Joiner | 线性 + tanh 输出投影——160 KB INT8 |
| 解码 | 在用户关键词的 Aho-Corasick ContextGraph 上执行改进 beam search(beam=4) |
磁盘上的编译大小:共计 ~4 MB(encoder.mlmodelc + decoder.mlmodelc + joiner.mlmodelc)。运行时内存:包含编码器状态缓存约 ~6 MB。
性能
| 指标 | 值 | 说明 |
|---|---|---|
| RTF(CPU + Neural Engine) | 0.04 | M 系列芯片上 26× 实时 |
| Recall(12 个关键词) | 88% | LibriSpeech test-clean,158 个正样本 |
| 误报 / 话语 | 0.27 | 60 个负样本 |
| CoreML INT8 对比 PyTorch FP32 | 99% | 发射一致率 |
调优默认值:acThreshold=0.15、contextScore=0.5、numTrailingBlanks=1。支持按关键词覆盖设置。
CLI 使用
纯短语形式(贪心 BPE——对常见词效果良好):
audio wake recording.wav --keywords "hey soniqo"
audio wake recording.wav --keywords "hey soniqo:0.15:0.5" "cancel"
预先分词形式(sherpa-onnx 风格——推荐在你确切知道模型训练所用分解时使用):
# Format: "phrase|piece1 piece2 ...:threshold:boost"
audio wake recording.wav \
--keywords "LIGHT UP|▁ L IGHT ▁UP:0.25:2.0"
# Multiple keywords + JSON output
audio wake recording.wav \
--keywords "LIGHT UP|▁ L IGHT ▁UP:0.25:2.0" \
"LOVELY CHILD|▁LOVE LY ▁CHI L D:0.25:2.0" \
--json
或者一个关键词文件,每行一个条目(# 为注释):
audio wake recording.wav --keywords-file keywords.txt
Swift API
import SpeechWakeWord
// Load the model with your keyword list.
let detector = try await WakeWordDetector.fromPretrained(
keywords: [
KeywordSpec(phrase: "hey soniqo", acThreshold: 0.15, boost: 0.5),
KeywordSpec(phrase: "cancel")
]
)
// Streaming: push chunks, consume detections as they fire.
let session = try detector.createSession()
for chunk in micAudioChunks { // Float32 @ 16 kHz
for detection in try session.pushAudio(chunk) {
print("[\(detection.time(frameShiftSeconds: 0.04))s] \(detection.phrase)")
}
}
// Batch: single shot over a full buffer.
let detections = try detector.detect(audio: samples, sampleRate: 16000)
KeywordSpec
| 字段 | 含义 |
|---|---|
phrase | 显示短语,例如 "hey soniqo"。当 tokens 为 nil 时,也作为贪心 BPE 编码的来源。 |
acThreshold | 匹配跨度上所需的平均声学概率。0 → 使用调优默认值(0.15)。 |
boost | 按 token 的上下文增益。正值更容易触发短语。0 → 调优默认值(0.5)。 |
tokens | 可选显式 BPE 片段列表。若不为 nil,检测器在模型的 tokens.txt 中逐片查找,并跳过贪心 BPE 编码器。 |
何时使用预先分词的
tokensicefall KWS 词表是大写 BPE。对一条短语的贪心分词可能选出与模型训练发射不同的 BPE 分解——"LIGHT UP" 贪心编码为 ▁LI GHT ▁UP,而训练分解是 ▁ L IGHT ▁UP。如果在 TTS 合成或干净朗读语音上检测漏掉了明显的匹配,请尝试 sherpa-onnx 风格的预先分词形式。
模型下载
| 模型 | 参数 | 大小 | HuggingFace |
|---|---|---|---|
| KWS-Zipformer-3M | 3.49M | ~4 MB | aufklarer/KWS-Zipformer-3M-CoreML-INT8 |
Pipeline 集成
模块提供与 StreamingVADProvider 形状一致的 WakeWordProvider 协议,使语音 pipeline 可以通过 VAD、唤醒词或两者共同开启激活。WakeWordStreamingAdapter 将已加载的检测器 + 单个会话封装为可复用的 provider 对象。
let adapter = try WakeWordStreamingAdapter(detector: detector)
// pipeline.configure(wakeWord: adapter)
源代码
- Sources/SpeechWakeWord — Swift 模块
- docs/models/kws-zipformer.md — 架构备忘
- docs/inference/wake-word.md — 推理 pipeline
- 上游:k2-fsa/icefall KWS 配方 / pkufool/keyword-spotting-models