hey_fable β€” openWakeWord model

A custom openWakeWord wake-word detection model for the phrase "hey fable".

Trained on synthetic speech from two complementary TTS engines:

  • Piper (en_US-libritts_r-medium, 850 speakers) β€” bulk timbre and prosody coverage
  • Kokoro-82M (27 expressive American + British voices) β€” natural, expressive read speech that Piper cannot cover

On top of the modal-voice clips we add a whisperized subset (STFT phase-randomisation of the modal clips) so the model fires on whispered "hey fable" even in noisy environments. Every clip is augmented with room impulse responses (MIT IR Survey), ESC-50 background mixing at SNRs from βˆ’10 to +15 dB, pitch shift, band-stop filtering, seven-band EQ, tanh distortion, and coloured noise, on top of Google's frozen speech-embedding backbone that ships with openwakeword.

Metrics

End-to-end streaming, 80 ms frames, 1200 held-out modal positives + 300 held-out whispered positives + 1200 held-out adversarial negatives, plus the 10.7-hour general-speech FP corpus that ships with the base openwakeword package.

Threshold Recall
(modal)
Recall
(whisper)
Recall
(Kokoro voices)
Adversarial
FPR
FP / hour
(10.7 h corpus)
0.5 99.6 % 98.3 % 99.5 % 17.2 % 1.40
0.7 99.6 % 97.3 % 99.5 % 16.8 % 1.03
0.9 99.5 % 97.3 % 99.5 % 16.3 % 0.84 ← recommended
  • Recall (modal) β€” synthetic modal-voice held-out positives from Piper + Kokoro.
  • Recall (whisper) β€” whisperized versions of held-out positives (STFT phase-randomisation).
  • Recall (Kokoro voices) β€” subset of the modal set from 27 expressive Kokoro voices.
  • Adversarial FPR β€” fraction of 1200 held-out adversarial clips ("hey table", "hey stable", "hey cable", "hey Google", "hey Siri", "a fable", "the stable", "vegetable", "comfortable", …) that fire. Intentionally hard: those phrases really do sound like "hey fable".
  • FP / hour β€” false fires per hour on the 10.7-hour openWakeWord validation corpus (mixed real-world speech, music, noise).

Quick start

import openwakeword
from openwakeword.model import Model

# Download the shared melspectrogram + embedding backbone (~4 MB, one-off)
openwakeword.utils.download_models()

model = Model(
    wakeword_models=["hey_fable.onnx"],
    inference_framework="onnx",
)

# Feed 16 kHz mono 16-bit PCM audio in 80 ms frames (1280 samples)
prediction = model.predict(frame)
if prediction["hey_fable"] > 0.9:  # recommended threshold
    print("wake!")

Files

File Purpose
hey_fable.onnx The classifier head. Input [1, 16, 96] embeddings, single sigmoid output.
negative_phrases.txt Weighted adversarial phrases used to generate hard negatives. Format <count>\t<phrase>.
hey_fable_config.yaml Training configuration.
metrics.json Full evaluation metrics.

Training data

  • Positives β€” 7500 training / 1700 held-out. 4000 Piper (850 speakers Γ— 5 length-scales Γ— 4 noise-scales Γ— 3 noise-scale-ws), 2000 Kokoro (27 voices Γ— 5 speaking speeds Γ— 10 prosody prompts), 1500 whisperized (STFT phase-randomised versions of the modal clips).
  • Hard negatives β€” 5500 training / 1200 held-out synthetic clips of phonetically similar phrases. The phrase file is weighted (<count>\t<phrase> lines): "hey table", "hey stable", "hey cable" each get 20Γ— oversampling; the /-eΙͺbΙ™l/ rhyme family ("hey label", "hey Mabel", "hey sable", "hey Abel", "hey fabled", "hey fabric", "hey Fabio", …) gets 10Γ—; syllable-substitution neighbours ("gay stable", "say fable", "weigh stable", "prey fable", …) get 5Γ—; determiner + noun ("a fable", "the stable", "away table", …) get 3Γ—; polysyllabic -able words ("comfortable", "notable", "capable", …) get 2Γ—. Kokoro also synthesises these to give the negatives expressive prosody.
  • Background negatives β€” 2000 hours of pre-computed openWakeWord features from ACAV100M (speech + noise + music) via davidscripka/openwakeword_features.
  • False-positive validation β€” the 10.7-hour held-out corpus from the same dataset.

Data augmentation

Every positive and negative clip runs through the openWakeWord augmentation pipeline 3 rounds (22 500 augmented positives + 16 500 augmented negatives), with augmentation probabilities pushed above defaults for whisper-in-noise robustness:

  • Background mixing at SNR βˆ’10 to +15 dB β€” p = 0.9.
  • Room impulse responses from the MIT Environmental IR survey (270 rooms) β€” p = 0.75.
  • Coloured noise at SNR 10-30 dB β€” p = 0.5.
  • Pitch shift Β±3 semitones β€” p = 0.35.
  • Seven-band parametric EQ Β±6 dB β€” p = 0.35.
  • Band-stop filter β€” p = 0.35.
  • Tanh distortion β€” p = 0.30.

Model architecture

  • Input: 16 Γ— 96 melspectrogram embeddings from the shared openWakeWord Google speech-embedding backbone (frozen, ships with openwakeword).
  • Head: fully-connected DNN, layer width 32, LayerNorm + ReLU, single sigmoid output.
  • ~15 000 parameters β€” faster than real-time on a Raspberry Pi.

Training used openwakeword.train.Model.auto_train with a cosine LR schedule + warmup, three sequences (75 000 + 7 500 + 7 500 steps), adaptive negative-example weighting up to 8000Γ— (raised on demand to hit target FP rate), stochastic-weight-averaging over the top-decile checkpoints, and a rebalanced batch (ACAV100M:1024 / adversarial:600 / positive:300).

Recommended threshold

0.9 β€” the highest-value operating point on this model:

  • 99.5 % recall on modal held-out positives.
  • 97.3 % recall on whispered held-out positives.
  • 0.84 FP/hour on the 10.7-hour openWakeWord validation corpus.

Bumping to 0.5 buys a tiny amount of recall (99.6 % vs 99.5 %) at the cost of 1.7Γ— more false positives per hour.

Limitations

  • English only. Trained purely on synthetic speech; real-user voices are not represented explicitly.
  • Adversarial phrases (hey table, hey cable) still fire ~16 % of the time even at the highest threshold β€” this is a phonetic-similarity ceiling, not a training failure. Real users don't randomly emit those phrases.
  • Small classifier head is not designed to distinguish speakers.
  • Whisper detection works because the model was trained on whisperized clips; extreme background noise (SNR ≀ βˆ’10 dB) will still miss most whispers.

License

Apache-2.0 for code. The model weights inherit the base openWakeWord backbone's CC-BY-NC-SA-4.0 because they were derived using Google's speech-embedding model under those terms.

Credit

Downloads last month

-

Downloads are not tracked for this model. How to track
Inference Providers NEW
This model isn't deployed by any Inference Provider. πŸ™‹ Ask for provider support