ONNX/TFLiteモデルでオフライン音声制御
小さな固定キーワードモデルのセット——スマホ・ブラウザ・デスクトップには標準ONNX、マイコンにはINT8 TFLite——が、ウェイクワードと少数の音声コマンドを完全オフラインで待ち受けます。この記事では、この種のモデルがどんなときに適切なのか、同じモデルファイルが各プラットフォームをどう渡り歩くのかを説明し、最後に自分の言葉で再現できるESP32-S3デモの全手順をたどります。
どんなときにこの種のモデルが必要か
キーワードスポッティング(KWS)モデルが適するのは、語彙が固定かつ少数——ウェイクワード1つと高々数十のコマンド——で、製品が常時待受けしながら音声をどこにも送らない場合です。利用者が何でも話せる必要があれば完全なASRモデルが必要ですが、ASRは計算量も消費電力も一桁多く要求するため、ESP32クラスのマイコンやバッテリー製品では手が届かず、常時待受けは非現実的です。
| 固定キーワードKWS (ONNX/TFLite) | オンデバイスASR | クラウド音声アシスタント | |
|---|---|---|---|
| 語彙 | 固定:ウェイクワード1 + コマンド2〜10 | オープン | オープン |
| サイズ | 約100KBクラス | 数百MB | ローカルはゼロ、音声は端末外へ |
| ハードウェア | MCU(ESP32-S3)からブラウザまで | Linux系SBC / スマホ | マイクとネットワークのあるもの |
| ネットワーク | 不要 | 不要 | 必須 |
| 計算と電力 | MCU上でミリ秒クラス——常時待受けでも余裕、バッテリー製品でも耐えられる | 一桁多くCPU/RAM/電力が必要——常時待受けは非現実的 | ローカルは軽いが、無線とネットワークは常時消電 |
| 応答 | 端末内で即時 | 重め、端末依存 | 往復レイテンシ |
| ランニングコスト | なし——モデルファイルは自分のもの | 計算/電力 | 従量制またはサブスク |
| プライバシー | 音声は端末の外に出ない | ローカルに留まる | 音声をアップロード |
具体的に、チームが固定キーワードモデルを選ぶのはこういう場面です:
- スマートホーム:「ライトをつけて」「エアコンつけて」——ウェイクワード1つと数個のデバイスコマンド
- 共用端末と打刻:「打刻お願い」「勤務開始」——入力が難しい場面での音声チェックイン
- ロボットとカメラ:「写真を撮って」「スタート」「ストップ」——手が塞がっているときの音声操作
- プレゼンとサイネージ:「次のページ」「戻る」——リモコンもタッチも不要
- おもちゃとガジェット:カスタムウェイクワードがそのまま製品の名前に
- PTT無線:ボタンに手を伸ばさず、声で送信を起動
いずれも語彙は固定であり、KWSのトレードオフを喜んで受け入れます:オープンな対話を諦める代わりに、小型・即時・プライベート・ランニングコストゼロを手に入れる。こうした固定語彙のKWSモデルが必要ですか?voicute.comで学習できます——日本語・英語・ドイツ語・フランス語・中国語対応。以下の手順で全工程をたどります。
ひとつのモデル形式で、すべてのプラットフォームへ
オープンソースのオープンソース推論エンジンとデモモデルを見る →パイプラインは、同じ学習から2つの形式を生成します:ONNXはONNX Runtimeを持つすべての環境へ、INT8 TFLiteはESP32-S3などのTFLite Microデバイスへ。推論パイプラインは3ファイルだけです:
音声 (16 kHz) → melspectrogram.onnx → embedding_model.onnx → your_word.onnx → 確率
melspectrogram.onnxとembedding_model.onnxは全ウェイクワードで共有——リポジトリに同梱。your_word.onnxは単語ごとの分類器。ウェイクワードの変更=小さなファイルの差し替え(とmodel_info.jsonの1行)。- マルチクラスモデルは2〜10個のキーワードを1つの分類器ファイルに収め、N+1路のソフトマックスを出力。
同じリポジトリのオープンソースランタイムがこれらのファイルをAndroid(android/)、Web(web/、ブラウザ内ONNX)、Windows/Linux/macOSのPython(linux/)、ESP32-S3/P4(esp32/、TFLite)で読み込みます。Wyomingサイドカーで同じモデルをHome Assistantに接続できます。AccessKeyなし、クラウド推論なし、デバイスごとのランタイム料金なし——ダウンロードしたモデルファイルはあなたのものです。
実例:ESP32-S3上のウェイクワード + 音声コマンド
チェーン全体をエンドツーエンドで示すため、同じモデル群をこのリストの最小端で動かします。デモはパイプラインが生み出す2種類のモデルをちょうど組み合わせた構成です:単一キーワードのウェイクモデルが「Hallo Lumo」を担い、マルチキーワードモデルが後続の4つのコマンドを担当します。ボードはESP32-S3 HMI DevKit(デュアルコア240 MHz、8 MB PSRAM、ES7210マイクコーデック)、ESP-IDF 6.0.1。「Hallo Lumo」(ドイツ語のカスタムウェイクワード)と言うとLEDが点灯してウェイクを知らせ、6秒の窓内で「Licht rot / blau / grün / weiß」と言うとライトがその色に変わり、その後デバイスは待受けに戻ります。
Invoke() ≈ 76 ms ≈ 1フレーム121 ms、160 msのホップに対してリアルタイム。静かな部屋と騒がしい部屋で20回以上のウェイク、4コマンドすべて認識、混同ゼロ。ウェイクモデルの誤検知はハードネガティブマイニングにより約319回/時から2.6回/時に低下(24時間のホールドアウト音声で実測)。推論時間はモデルにより異なるため、必ず自身のモデルで計測してください。なぜ自前で学習するのか。Espressif純正のMultiNetはコマンドを中国語と英語しかサポートしません。ここでの2つのモデルは、Mel特徴から学習する独自パイプラインで訓練されており、同じ流れはどの言語でも機能します。ドイツ語なのは、このデモがドイツ語を話すように作られただけです。
- デモモデル(ドイツ語のウェイク + コマンド):onnx-wakeword/models/de
- ESP32ファームウェアとサンプル:onnx-wakeword/esp32
自分のキーワードモデル——単一のウェイクワードでも、マルチキーワードのコマンドセットでも、5言語対応——を学習したいなら、それがvoicute.comの仕事です。以下、その全手順を順にたどります。
仕組み:2つの小さなモデルを時間分割
1つのデバイスが、コンパクトな2つのINT8 TFLiteモデルを同じコアで、互いに競合しないようスケジューリングして実行します:
- ウェイクモデル、常時稼働。ESP-SR AFEがオーディオフロントエンド(AEC、NS、VAD)を担い、ウェイクモデルが最新のMel窓を消費します。検出するとデバイスはモードを切り替えます。
- コマンドモデル、ウェイク後のみ。続く6秒間、2つ目のマルチキーワード分類器が発話を設定済みコマンド(ここでは4色)に対応させます。窓の終了後——またはコマンド実行後——デバイスはウェイクのみの待受けに戻ります。
このゲート構造こそが誤コマンドを抑えます:コマンドモデルは単独では決してリスンしません。誤検知の負担はすべてウェイクワード側が引き受けており、それはまさにパイプラインで最適化できる部分です(ステップ4)。
ステップ1 · デモモデルを自分で動かす(無料)
2つのデモモデルはGitHubにあり、登録不要です。最速の確認にハードウェアは不要——デスクトップのPythonランタイムが同じモデルファイルを読み込みます:
git clone https://github.com/voicute/onnx-wakeword.git
cd onnx-wakeword
pip install onnxruntime numpy sounddevice
python linux/infer.py --model-dir ./models/de/
組み込みのフルパスは実測済みのESP-IDFサンプルです:
cd onnx-wakeword/esp32/examples/esp32s3_hmi_devkit
idf.py set-target esp32s3
python patch_led_strip.py
idf.py build
idf.py -p COM6 flash monitor
Webランタイムは同じデモモデルをブラウザ内だけで実行できます——ハードウェアを触る前にキーワードの響きを確かめるのに便利です。
ステップ2 · 自分のウェイクワードを学習する(無料)
voicute.comでフレーズを入力し、言語を選択——日本語、英語、ドイツ語、フランス語、中国語に対応。学習すると最初のモデル(R0)が得られます:デスクトップ/モバイル/ブラウザ用の標準ONNX分類器と、ESP32-S3用の対応するINT8 TFLiteです。このステップは無料です。
ステップ3 · 自分の声でテストする(無料)
コンソールでBasicモデルを、自分のマイク・実際の距離・実際の背景騒音で試します。このステップがその後のすべてを決めるので、支払いの前にテストを:普段の話し方で、普段の使用距離から呼んでください。リコールが物足りなければ、そのフレーズの実声レコーディングを数本追加して再学習——録音は任意の学習入力で、これも無料です。
ステップ4 · 実測に応じて道を選ぶ
テストのあと、あなたは次の4つのどれかにいます:
- ときどき呼びかけに反応しない → 実声レコーディングを5本追加して再学習(無料)。リコール問題の大半はカバレッジの問題で、この段階で解決します。
- ウェイクは確実だが、テレビや会話で誤反応する → 誤検知最適化を実行(1回につき有料)——仕組みは次のセクションで。
- ウェイクワードだけでなく音声制御が欲しい → 2〜10個の固定コマンドを1ファイルに収めたマルチキーワードモデルを学習するか、ウェイクワードをやめてコマンドワードを直接トリガーにします。上のデモはまさにこの構成です。
- そのまま使える → モデルをダウンロード(一回払い)し、上のどのランタイムにも配置できます。
誤検知の制御:R0とR1、そしてハードネガティブマイニング
すべてのモデルはR0から始まります——準備した音声データと、ノイズ・残響・音量変化といった固定の妨害セットで学習した最初のモデルです。しかし現実の妨害はるかに多く、テレビ、音楽、背後の会話、広告……。しかも妨害は単語依存です:同じ広告音声が「Hallo Lumo」のモデルでは誤検知を起こしても、別の単語のモデルは完全に無反応、ということも起きます。学習直後のR0の誤検知が多いのはすべてのキーワードモデルに共通する性質で、学習が下手な証拠ではありません。
R1がこれを治します。コンソールの2つのボタンに対応する2ステップです:
- 負例スキャン。R0モデルに約100時間の対象言語ネガティブ音声を聞かせ、反応したクリップを抽出——これが定義上、このモデル固有のハードネガティブです(最大10,000件保持)。数分。
- 全量再学習。抽出クリップを負例として元の学習セットに加え、モデルを丸ごと再学習します。通常40〜80分。
負例がR0の実弱点に合わせて選ばれるため、1ラウンドで誤検知は平均して毎時数百回から一桁へ(約95%減)。このデモのウェイクワードでは、24時間のホールドアウト音声で319回/時 → 2.6回/時を実測しました。単一キーワードでもマルチキーワードでもこの流れをサポートします。デモの4色コマンドモデルも同じスキャン+再学習のループを通せます。R2パイプラインは将来の新たな妨害源のために用意されていますが、ほとんどのキーワードはR1の1ラウンドで日常使用に十分です。最適化は1回につき有料、現在の価格はvoicute.comをご覧ください。
ステップ5 · 実機で検証する
コンソールのテストがモデルを証明し、デバイスが製品を証明します。出荷前の3つのチェック:
- リアルタイム予算。Mel +
Invoke()を実測し、音声ホップ(このデモでは160 ms)より短く保つ。収まらなければモデルを縮小するか、最新窓だけを処理します。 - 1時間あたりの誤検知回数。実環境——テレビ、会話、音楽——を1時間録音し、その音声は決して学習に入れず、ファームウェアに流してトリガーを数えます。この1つの数字はどんな短いデモ映像より説得力があります。
- 同じしきい値での検出漏れ。誤検知とリコールの数字は揃って初めて意味を持ちます。両方記録します。
FAQ
- 固定キーワードモデルが不向きなのはどんなとき?
- 語彙が固定していないとき。書き起こし、自由な質問などオープンな発話には完全なASRが必要——KWSモデルは設計上、学習した単語しか認識しません。
- 完全にオフラインで動きますか?
- はい。ウェイクワードもコマンドもローカル動作。クラウド推論なし、実行時アカウントキーなし。
- 対応プラットフォームと言語は?
- Android、Web、Python(Windows/Linux/macOS)、ESP32-S3/P4、加えてHome Assistant向けWyoming。モデルは日・英・独・仏・中に対応。
- 無料と有料の境界は?
- 学習とコンソールテストは無料。モデルファイルのダウンロードと誤検知最適化は有料——現在の価格はvoicute.comで。
- 無関係な会話でコマンドが誤動作することは?
- ウェイク後6秒の窓内のみで、ゲートが大半を防ぎます。窓内の発話は現状まだ誤分類の可能性があり、キーワードのハードネガティブマイニングで改善できます。