Home Assistant Voice PE に日本語ウェイクワードを自作して載せるまで
Home Assistant Voice PE(以下 Voice PE)は、ESPHome ベースでローカル音声アシスタントを動かせる便利なデバイスです。ただし、公式で配布されている wake word モデルは英語中心で、日本語の wake word は自分で用意する必要があります。
この記事では、microWakeWord を使って日本語ウェイクワード「ねえ、マイク」を学習し、ESPHome 経由で Voice PE に組み込むまでの流れをまとめます。
本記事は「とにかく動かしてみる」より、同じ手順を再現したい人向けの内容です。学習にはそれなりの時間とディスク容量を使います。
できたもの
- 日本語 wake word モデル(量子化済み TFLite): 約62KB
- パラメータ数: 約26,000
- マニフェスト:
nee_maiku.json - Voice PE 向けファームウェアへのビルド・OTA書き込みまで成功
自分の環境では、テストセット上で次のような結果でした。
| Cutoff | 誤拒否率 | 誤検知/時間 |
|---|---|---|
| 0.92 | 6.2% | 0.56 |
| 0.86 | 6.2% | 0.75 |
| 0.74 | 5.3% | 1.5 |
最終的な probability_cutoff は 0.85 にしています。ただし、合成音声1話者+自分の声33件という小規模なデータなので、実運用での精度は実機で調整が必要です。
全体像
Piperで日本語音声を生成
↓
Augmentation(背景音・RIR・ノイズ)
↓
スペクトログラム特徴量を生成
↓
microWakeWordで学習
↓
量子化 → TFLite + JSONマニフェスト
↓
ESPHomeでVoice PEに組み込み
↓
OTA書き込み → 実機で調整
前提・環境
- Linux(Ubuntu系)
- Python 3.10.14
- CPUのみ(GPUなし)
- Home Assistant + ESPHome Device Builder アドオン
- Voice PE のファームウェア: 26.9.0
- ESPHome: 2026.9.0 以上
Python 3.10 を使うのは、TensorFlow まわりの互換性を考慮してです。
1. 作業ディレクトリとPython環境
mkdir -p ~/mww-ja-work
cd ~/mww-ja-work
~/.pyenv/versions/3.10.14/bin/python3.10 -m venv venv
source venv/bin/activate
pip install -U pip wheel setuptools
microWakeWord 本体をインストールします。
pip install -e /path/to/micro-wake-word
CPU版の PyTorch を先に入れておくと、CUDA版の巨大な依存を避けられます。
pip install torch --index-url https://download.pytorch.org/whl/cpu
Piper と日本語音声合成用の依存を入れます。
pip install piper-tts onnxruntime pyopenjtalk
piper-sample-generator は pip 版だと piper_train が同梱されず失敗することがあるため、GitHub から clone します。
git clone https://github.com/rhasspy/piper-sample-generator.git
2. 日本語Piper音声モデルを用意
Hugging Face から日本語の Piper 音声モデルをダウンロードします。
mkdir -p voices
cd voices
wget -O ja_JP-hi_fi_captain-medium.onnx \
'https://huggingface.co/rhasspy/piper-voices/resolve/main/ja/ja_JP/hi_fi_captain/medium/ja_JP-hi_fi_captain-medium.onnx?download=true'
wget -O ja_JP-hi_fi_captain-medium.onnx.json \
'https://huggingface.co/rhasspy/piper-voices/resolve/main/ja/ja_JP/hi_fi_captain/medium/ja_JP-hi_fi_captain-medium.onnx.json?download=true'
日本語モデルが1話者しかないため、話速・ノイズ・長さを変えてバリエーションを増やします。
3. wake wordサンプルを生成
「ねえ、マイク」を2000件生成します。
cd ~/mww-ja-work/piper-sample-generator
PYTHONPATH=. ../venv/bin/python -m piper_sample_generator "ねえ、マイク" \
--model ../voices/ja_JP-hi_fi_captain-medium.onnx \
--max-samples 2000 \
--length-scales 0.8 0.9 1.0 1.1 1.2 \
--noise-scales 0.5 0.667 0.8 \
--noise-scale-ws 0.6 0.8 1.0 \
--output-dir ../generated_samples
生成後、generated_samples/ に WAV が並びます。
4. 背景音・負例を用意
Augmentation 用に RIR と背景音、学習用に負例データセットを取得します。
- MIT environmental impulse responses
- FMA(音楽)
dinner_partydinner_party_evalno_speechspeech
負例は Hugging Face の microWakeWord 用データセットを使います。
cd negative_datasets
link_root="https://huggingface.co/datasets/kahrendt/microwakeword/resolve/main/"
for fname in dinner_party.zip dinner_party_eval.zip no_speech.zip speech.zip; do
wget -O "$fname" "${link_root}${fname}"
unzip -q -o "$fname" -d .
done
AudioSet は配布形式が変わっていて取得に失敗したため、今回はスキップしました。FMA と負例セットで代替しています。
5. Augmentationとスペクトログラム生成
Clips → Augmentation → SpectrogramGeneration の順で特徴量を作ります。
from microwakeword.audio.augmentation import Augmentation
from microwakeword.audio.clips import Clips
from microwakeword.audio.spectrograms import SpectrogramGeneration
clips = Clips(
input_directory='generated_samples',
file_pattern='*.wav',
max_clip_duration_s=None,
remove_silence=False,
random_split_seed=10,
split_count=0.1,
)
augmenter = Augmentation(
augmentation_duration_s=3.2,
augmentation_probabilities={
"SevenBandParametricEQ": 0.1,
"TanhDistortion": 0.1,
"PitchShift": 0.1,
"BandStopFilter": 0.1,
"AddColorNoise": 0.1,
"AddBackgroundNoise": 0.75,
"Gain": 1.0,
"RIR": 0.5,
},
impulse_paths=['work/mit_rirs'],
background_paths=['work/fma_16k'],
background_min_snr_db=-5,
background_max_snr_db=10,
min_jitter_s=0.195,
max_jitter_s=0.205,
)
spectrograms = SpectrogramGeneration(
clips=clips,
augmenter=augmenter,
slide_frames=10,
step_ms=10,
)
training / validation / testing の3分割で RaggedMmap に保存します。学習用は repetition 2、validation は 1、testing は 1 にしています。
6. 学習
training_parameters.yaml を作成し、10,000ステップ学習します。
主な設定は以下です。
window_step_ms: 10
clip_duration_ms: 1500
training_steps:
- 10000
batch_size: 128
learning_rates:
- 0.001
positive_class_weight:
- 1
negative_class_weight:
- 20
maximization_metric: average_viable_recall
target_minimization: 0.9
学習コマンドは以下です。
cd ~/mww-ja-work
nohup ../venv/bin/python -m microwakeword.model_train_eval \
--training_config='training_parameters.yaml' \
--train 1 \
--restore_checkpoint 1 \
--test_tf_nonstreaming 0 \
--test_tflite_nonstreaming 0 \
--test_tflite_nonstreaming_quantized 0 \
--test_tflite_streaming 0 \
--test_tflite_streaming_quantized 1 \
--use_weights "best_weights" \
mixednet \
--pointwise_filters "64,64,64,64" \
--repeat_in_block "1, 1, 1, 1" \
--mixconv_kernel_sizes '[5], [7,11], [9,15], [23]' \
--residual_connection "0,0,0,0" \
--first_conv_filters 32 \
--first_conv_kernel_size 5 \
--stride 3 > train.log 2>&1 &
12コアCPUで約32分で完了しました。
7. 自分の声でファインチューニング
合成音声だけだと実際の声に弱いので、自分の声を追加します。
「ねえ、マイク」を33回録音し、16kHzモノラルWAVに変換して my_voice_samples/ に置きます。
clips = Clips(
input_directory='my_voice_samples',
file_pattern='*.wav',
max_clip_duration_s=None,
remove_silence=True,
random_split_seed=10,
split_count=0.15,
)
自分の声は件数が少ないので、repetition を大きくして水増しします。
- training: 40
- validation: 10
- testing: 5
ファインチューニング設定では、自分の声の sampling_weight を 3.0 にし、学習率を 0.0002 に下げます。
training_steps:
- 12000
learning_rates:
- 0.0002
既存チェックポイントから2,000ステップ追加学習します。
nohup ../venv/bin/python -m microwakeword.model_train_eval \
--training_config='training_parameters_finetune.yaml' \
--train 1 \
--restore_checkpoint 1 \
--test_tf_nonstreaming 0 \
--test_tflite_nonstreaming 0 \
--test_tflite_nonstreaming_quantized 0 \
--test_tflite_streaming 0 \
--test_tflite_streaming_quantized 1 \
--use_weights "best_weights" \
mixednet \
--pointwise_filters "64,64,64,64" \
--repeat_in_block "1, 1, 1, 1" \
--mixconv_kernel_sizes '[5], [7,11], [9,15], [23]' \
--residual_connection "0,0,0,0" \
--first_conv_filters 32 \
--first_conv_kernel_size 5 \
--stride 3 > finetune.log 2>&1 &
8. ESPHome用のモデルとマニフェストを作る
量子化済みの TFLite を取り出します。
cp trained_models/wakeword_finetuned/tflite_stream_state_internal_quant/stream_state_internal_quant.tflite \
final_model/nee_maiku.tflite
マニフェスト nee_maiku.json を作ります。
{
"type": "micro",
"wake_word": "ねえ、マイク",
"author": "custom",
"model": "nee_maiku.tflite",
"trained_languages": ["ja"],
"version": 2,
"micro": {
"probability_cutoff": 0.85,
"feature_step_size": 10,
"sliding_window_size": 5,
"tensor_arena_size": 30000,
"minimum_esphome_version": "2024.7.0"
}
}
9. Voice PEへインストール
モデル2ファイルを Home Assistant の /config/esphome/models/ に置きます。
/config/esphome/models/nee_maiku.tflite
/config/esphome/models/nee_maiku.json
ESPHome の YAML に micro_wake_word を追記します。既存の packages はそのまま使います。
substitutions:
name: home-assistant-voice-xxxxxx
friendly_name: Home Assistant Voice
packages:
Nabu Casa.Home Assistant Voice PE: "github://esphome/home-assistant-voice-pe/home-assistant-voice.yaml"
esphome:
name: ${name}
friendly_name: ${friendly_name}
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
micro_wake_word:
models:
- model: /config/esphome/models/nee_maiku.json
id: nee_maiku_model
api などの既存設定は消さずに残してください。
ESPHomeのバージョンに注意
公式パッケージが min_version: 2026.9.0 を要求していました。ESPHome Device Builder が 2026.8.1 だと次のエラーになります。
Current ESPHome Version is too old to use this package: 2026.8.1 < 2026.9.0.
アドオンを更新してから VALIDATE / INSTALL してください。
パスの注意
ESPHome Device Builder アドオンでは、ホスト側の /config/esphome/models/ をそのまま指定して問題ありませんでした。
model: /config/esphome/models/nee_maiku.json
/config/models/... に変換する必要はありませんでした。
再現性のために
github://esphome/home-assistant-voice-pe/home-assistant-voice.yaml は dev ブランチを参照します。記事執筆時点の内容を再現したい場合は、タグやコミットに固定するのがおすすめです。
10. 実機で調整
書き込み後、「ねえ、マイク」と話しかけて反応を確認します。
- 反応しない →
probability_cutoffを下げる(例: 0.85 → 0.75) - 誤検知が多い →
probability_cutoffを上げる(例: 0.85 → 0.9)
YAML 側で上書きできます。
micro_wake_word:
models:
- model: /config/esphome/models/nee_maiku.json
id: nee_maiku_model
probability_cutoff: 0.8
ハマりどころまとめ
piper-sample-generatorは pip 版ではなく GitHub clone を使う- ESPHome Device Builder のバージョンが古いとパッケージを読めない
- モデルのパスは
/config/esphome/models/...で通った tensor_arena_sizeが小さいとモデルロードに失敗することがある- 合成音声1話者だけだと実環境での精度は未知数。自分の声を混ぜるのが重要
まとめ
日本語の wake word でも、Piper の日本語音声モデルと microWakeWord を組み合わせれば、Voice PE 用のモデルを作るところまで到達できました。現時点では、実機で「ねえ、マイク」を問題なく検出できています。
ただし、今回のモデルは次の制約があります。
- TTS は1話者のみ
- 自分の声は33件
- 負例は英語中心
- テストセットも小規模
実運用では、話者を増やす、日本語の負例を追加する、実環境で録音する、といった改善が必要です。
まずは「動くところまで」を作りたい人にとって、この手順が参考になれば幸いです。