Активационное слово / Распознавание ключевых слов
Модуль SpeechWakeWord запускает локальный детектор ключевых слов: вы регистрируете список фраз, подаёте звуковые фрагменты и получаете обнаружения. Основан на потоковом Zipformer-трансдьюсере из icefall (3,49M параметров, Apache-2.0), скомпилированном в CoreML с INT8-палетизацией.
Поставляемая контрольная точка — файн-тюнинг KWS на gigaspeech. Для ключевых слов не на английском требуется отдельный файн-тюнинг icefall и повторный экспорт.
Архитектура
| Этап | Детали |
|---|---|
| fbank | совместим с kaldi (25 мс / 10 мс, окно Povey, 80 mel-бинов, high_freq=-400, без CMVN) |
| Энкодер | 6-ступенчатый причинный Zipformer2 (128-dim), 45 mel-кадров на входе → 8 кадров на выходе (40 мс / кадр) — 3,3 МБ INT8 |
| Декодер | Безсостоянийный трансдьюсер, словарь BPE-500, размер контекста 2 — 525 КБ FP16 |
| Joiner | Линейная + tanh выходная проекция — 160 КБ INT8 |
| Декодирование | Модифицированный beam search (beam=4) по Aho-Corasick-ContextGraph ключевых слов пользователя |
Скомпилированный размер на диске: ~4 МБ всего (encoder.mlmodelc + decoder.mlmodelc + joiner.mlmodelc). Память во время работы: ~6 МБ, включая кэши энкодера.
Производительность
| Метрика | Значение | Примечания |
|---|---|---|
| RTF (CPU + Neural Engine) | 0,04 | 26× реального времени на серии M |
| Recall (12 ключевых слов) | 88 % | LibriSpeech test-clean, 158 положительных высказываний |
| Ложные срабатывания / высказывание | 0,27 | 60 отрицательных высказываний |
| CoreML INT8 vs 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". Также используется как источник для жадной BPE-кодировки, когда tokens равно nil. |
acThreshold | Средняя акустическая вероятность, требуемая на совпавшем участке. 0 → использовать подобранное значение по умолчанию (0,15). |
boost | Бонус контекста на токен. Положительные значения облегчают срабатывание фразы. 0 → подобранное значение по умолчанию (0,5). |
tokens | Необязательный явный список BPE-фрагментов. Если не nil, детектор ищет каждый фрагмент в tokens.txt модели и минует жадный BPE-кодировщик. |
tokensСловарь KWS в icefall — 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 |
Интеграция в пайплайн
Модуль предоставляет протокол WakeWordProvider, повторяющий форму StreamingVADProvider, что позволяет голосовому пайплайну управлять активацией через 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 — пайплайн инференса
- Upstream: KWS-рецепт k2-fsa/icefall / pkufool/keyword-spotting-models