Palabra de activación / Detección de palabras clave
El módulo SpeechWakeWord ejecuta un detector de palabras clave en el dispositivo: registra una lista de frases, empuja fragmentos de audio y recibe detecciones. Basado en el transductor Zipformer en streaming de icefall (3,49M parámetros, Apache-2.0), compilado a CoreML con paletización INT8.
El checkpoint enviado es el fine-tuning KWS sobre gigaspeech. Las palabras clave no inglesas requieren un fine-tuning de icefall independiente y una re-exportación.
Arquitectura
| Etapa | Detalles |
|---|---|
| fbank | compatible con kaldi (25 ms / 10 ms, ventana Povey, 80 bins mel, high_freq=-400, sin CMVN) |
| Encoder | Zipformer2 causal de 6 etapas (128-dim), entrada 45 frames mel → 8 frames de salida (40 ms / frame) — 3,3 MB INT8 |
| Decoder | Transductor sin estado, vocabulario BPE-500, context size 2 — 525 KB FP16 |
| Joiner | Proyección lineal + tanh — 160 KB INT8 |
| Decodificación | Beam search modificado (beam=4) sobre un ContextGraph Aho-Corasick con las palabras clave del usuario |
Tamaño compilado en disco: ~4 MB en total (encoder.mlmodelc + decoder.mlmodelc + joiner.mlmodelc). Memoria en ejecución: ~6 MB incluyendo las cachés del encoder.
Rendimiento
| Métrica | Valor | Notas |
|---|---|---|
| RTF (CPU + Neural Engine) | 0,04 | 26× tiempo real en serie M |
| Recall (12 palabras clave) | 88 % | LibriSpeech test-clean, 158 enunciados positivos |
| Falsos positivos / enunciado | 0,27 | 60 enunciados negativos |
| CoreML INT8 vs PyTorch FP32 | 99 % | Acuerdo de emisiones |
Valores por defecto ajustados: acThreshold=0,15, contextScore=0,5, numTrailingBlanks=1. Se admiten sobreescrituras por palabra clave.
Uso del CLI
Forma de frase plana (BPE greedy — funciona bien para palabras comunes):
audio wake recording.wav --keywords "hey soniqo"
audio wake recording.wav --keywords "hey soniqo:0.15:0.5" "cancel"
Forma pre-tokenizada (estilo sherpa-onnx — recomendado cuando conoces la descomposición exacta con la que se entrenó el modelo):
# 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
O un archivo de palabras clave, una entrada por línea (# para comentarios):
audio wake recording.wav --keywords-file keywords.txt
API Swift
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
| Campo | Significado |
|---|---|
phrase | Frase de visualización, p. ej. "hey soniqo". También se usa como origen para la codificación BPE greedy cuando tokens es nil. |
acThreshold | Probabilidad acústica media requerida sobre el tramo coincidente. 0 → usa el valor por defecto ajustado (0,15). |
boost | Refuerzo de contexto por token. Valores positivos facilitan el disparo de la frase. 0 → valor por defecto ajustado (0,5). |
tokens | Lista explícita opcional de piezas BPE. Cuando no es nil, el detector busca cada pieza en el tokens.txt del modelo y omite el codificador BPE greedy. |
tokens pre-tokenizadosEl vocabulario KWS de icefall es BPE en mayúsculas. La tokenización greedy de una frase puede elegir una descomposición BPE distinta de la que se entrenó al modelo para emitir — "LIGHT UP" se codifica greedy como ▁LI GHT ▁UP, pero la descomposición de entrenamiento es ▁ L IGHT ▁UP. Cuando la detección sobre audio sintetizado por TTS o lectura limpia pierde coincidencias obvias, prueba la forma pre-tokenizada estilo sherpa-onnx.
Descargas del modelo
| Modelo | Parámetros | Tamaño | HuggingFace |
|---|---|---|---|
| KWS-Zipformer-3M | 3.49M | ~4 MB | aufklarer/KWS-Zipformer-3M-CoreML-INT8 |
Integración en pipeline
El módulo expone un protocolo WakeWordProvider que refleja StreamingVADProvider, de modo que una pipeline de voz puede controlar la activación mediante VAD, palabra de activación o ambas. WakeWordStreamingAdapter envuelve un detector cargado + una única sesión en un objeto provider reutilizable.
let adapter = try WakeWordStreamingAdapter(detector: detector)
// pipeline.configure(wakeWord: adapter)
Código fuente
- Sources/SpeechWakeWord — módulo Swift
- docs/models/kws-zipformer.md — notas de arquitectura
- docs/inference/wake-word.md — pipeline de inferencia
- Upstream: receta KWS k2-fsa/icefall / pkufool/keyword-spotting-models