ウェイクワード / キーワード検出
SpeechWakeWord モジュールはオンデバイスのキーワードスポッターを実行します:フレーズのリストを登録し、音声チャンクをプッシュして検出結果を受け取ります。icefall のストリーミング Zipformer トランスデューサー(3.49M パラメータ、Apache-2.0)をベースに、INT8 パレット化で CoreML にコンパイルされています。
提供されるチェックポイントは gigaspeech KWS のファインチューンです。英語以外のキーワードには、別途 icefall のファインチューンと再エクスポートが必要です。
アーキテクチャ
| ステージ | 詳細 |
|---|---|
| fbank | kaldi 互換(25 ms / 10 ms、Povey 窓、80 メル bins、high_freq=-400、CMVN なし) |
| エンコーダー | 6 ステージの因果 Zipformer2(128 次元)、45 メルフレーム入力 → 8 フレーム出力(40 ms / フレーム)— 3.3 MB INT8 |
| デコーダー | ステートレスなトランスデューサー、BPE-500 語彙、コンテキストサイズ 2 — 525 KB FP16 |
| Joiner | 線形 + tanh 出力プロジェクション — 160 KB INT8 |
| デコード | ユーザーキーワードの Aho-Corasick ContextGraph 上での修正ビーム探索(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
またはキーワードファイル、1 行に 1 エントリ(コメントは #):
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 | トークンあたりのコンテキストブースト。正の値はフレーズのトリガーを容易にします。0 → 調整済みデフォルト(0.5)。 |
tokens | オプションの明示的な BPE ピースリスト。nil でない場合、検出器はモデルの tokens.txt で各ピースを検索し、貪欲 BPE エンコーダーをバイパスします。 |
tokens を使うべき場合icefall 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 |
パイプライン統合
モジュールは StreamingVADProvider を反映した WakeWordProvider プロトコルを公開しており、音声パイプラインで 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 — 推論パイプライン
- 上流:k2-fsa/icefall KWS レシピ / pkufool/keyword-spotting-models