A working end-to-end kit for training a custom wake word ("hey Robby", "okay Mango", …) with openWakeWord on today's Google Colab — plus, more importantly, the measurement gate that tells you whether the result is safe to deploy.
Why this exists: openWakeWord's official training notebook no longer runs on
current Colab (Python 3.12, torch 2.11, reorganized datasets). This kit
encodes ~10 verified fixes — see the "Live-run addendum" in FINDINGS.md for
the full list — and was proven end to end on 2026-08-01: the resulting model
measured 0.00 false accepts/hour over 75 minutes of real living-room audio
at 91% recall on the target device (a Reachy Mini robot).
It was built for a robot named Clem; everything except one config cell is name-agnostic.
| File | What it does |
|---|---|
hey_clem_colab.ipynb |
The trainer. Upload to Colab, edit ONE cell, Run all (~1.5–2 h on a free-tier T4). |
oww_train_shim.py |
Compat shim for openwakeword.train on a modern stack (also written by the notebook itself). |
check_model.py |
The deployment gate. False-accepts/hour + recall vs threshold, from real audio. Do not skip. |
phrase_check.py |
Compare candidate phrases (phonemes, clip length) before spending an hour training the wrong one. |
slice_positives.py |
Splits "me saying the phrase 20 times" into the per-utterance clips the gate needs. |
hey_clem.yaml |
The training config, as reference — the notebook embeds its own copy. |
FINDINGS.md |
Findings, phrase research, gate criteria, rollback thinking, and every wall we hit with its fix. |
1. Pick a good phrase. Short phrases false-accept more. Aim for 3+
syllables / ~8 phonemes ("hello Robby", not "Rob"). If you have espeak-ng
locally, phrase_check.py will show you phonemes and (with a Piper
checkout) measured clip lengths. Training several phrasings into one model
works — we trained "hey / okay / hello Clem" together, and note that
n_samples in the config is a TOTAL split across phrases (~25k per phrase
is a good density).
2. Edit one cell. In the notebook, the %%writefile hey_clem.yaml cell
holds the whole config. Change:
model_name— yourstarget_phrase— your phrase(s), lowercasecustom_negative_phrases— near-misses for YOUR name (rhymes, one-sound-off words; ours had "hey clam", "hello glen"…). This is where short wake words win or lose.
Leave the rest alone unless you've read the yaml's comments.
3. Run it. Colab → File → Upload notebook → Runtime → T4 GPU → Run all.
⚠ Budget for Colab Pro (US$9.99/month). The full job is ~2 GPU-hours, and free-tier credit is unpredictable — in our runs it expired mid-job and stranded a nearly-finished model, forcing a rerun. Pro finished the same job comfortably; you can cancel after training. If you do try the free tier, be prepared for the session to be reclaimed partway through. Keep the tab open. Cell 1 fails fast if you didn't get a GPU; cell 2 ends with a preflight so environment problems die in the fast cell, not 30 minutes into training. At the end your browser downloads a zip with
<name>.onnxand<name>.onnx.data— you need BOTH (the weights live in the sidecar). To fold them into a single file:
python -c "import onnx; m = onnx.load('hey_clem.onnx'); onnx.save_model(m, 'hey_clem_single.onnx', save_as_external_data=False)"A ModuleNotFoundError: onnx_tf at the very end of the training cell is
expected and harmless — it's a tflite conversion attempt that fires
after the ONNX is already written.
4. MEASURE BEFORE YOU DEPLOY. This is the part most people skip and the
reason this kit exists. A wake word listens all day; an unmeasured one is a
false-accept machine. Record at least one hour of ordinary audio from
the room it will live in (any recorder, any sample rate — phone is fine;
nobody says the wake phrase), plus ~20 real recordings of you saying it
(slice_positives.py splits a single say-it-repeatedly session). Then:
python check_model.py --model your_model.onnx --negatives room_audio/ --positives your_clips/It reports false-accepts/hour and recall per threshold and recommends the
threshold — or refuses, which is also an answer. A reasonable bar:
≤0.5 false accepts/hour at ≥80% recall. If it can't hit both, don't
lower the bar — train a longer phrase, or look at openWakeWord's
custom_verifier_model (gates detections on your specific voice).
⚠ Score on Linux or on your deployment device — in our runs, macOS scored every model near-zero through the same code path that read 0.89+ on Linux (unresolved platform quirk). The gate's numbers are only as good as the machine they ran on; run them where the model will actually live.
5. Deploy at the recommended threshold, and keep your old wake word one config line away for rollback.
This kit produces and validates the wake-word model. It does not give your app ears: something in your stack must route mic audio through openWakeWord and act on detections. Where that already exists:
-
Anything that embeds openWakeWord (Python stacks, Wyoming satellites, Home Assistant's wake-word ecosystem) can load the
.onnxdirectly. Note HA's add-on defaults to tflite models; this kit deliberately produces ONNX. -
Reachy Mini: the stock conversation app has no wake-word support today. Standby-mode support (opt-in
STANDBY_ON_SLEEP=1, wake model viaWAKE_WORD_MODEL— pretrained name or a custom.onnxfrom this kit) has been proposed upstream: pollen-robotics/reachy_mini_conversation_app PR #514.PR #514 status: open, awaiting maintainer review (last checked 2026-08-02). This README will be updated once its fate is decided — merged (use the stock app) or not (use the PR branch as the reference integration).
Loading the model: openwakeword.model.Model(wakeword_models=["path/to/your.onnx"], inference_framework="onnx"),
fed 80 ms chunks (1280 samples) of 16 kHz mono int16. On Python 3.12,
install openwakeword with --no-deps plus onnxruntime scipy scikit-learn requests tqdm — a plain pip install openwakeword silently downgrades to
a fossil version (no py3.12 tflite-runtime exists).
Built with Claude (2026-08-01) for a Reachy Mini named Clem — who woke to his own name on the first try. License: Unlicense — public domain; no attribution required, no rights reserved.