Exploring into TensorFlow speech_commands, and experimental micro_speech for microcontrollers.
手持ちのマイコンで、TensorFlow Lite の micro_speech(音声認識)デモを試してみたくて、いろいろやっています。しかし、まだ experimental (実験段階)ということもあってか、なかなかうまくいきません。STM32F103 (bluepill) 用のオブジェクトを生成しようとしたら、リンク時にマイコンのメモリが足りなくなったり、–preprocess=micro を指定したくて Docker 上で bazel ビルドしようとしたら、コンパイラが internal error で落ちたり。。。後者に関しては、Docker に与えるメモリを増やしたりしてみて、再挑戦中です。後で書きますが、TensorFlow をソースからビルドしなくてはならないので、一日作業です。(とほほ)
今までに分かったこと
まず最初に、TensorFlow のサンプルプロジェクトとして speech_commands というのがあります。TensorFlow を GitHub から clone すると、tensorflow/tensorflow/examples/speech_commands というところにあるヤツですね。
このサンプルプロジェクトの説明は、ここにあります。こちらは特に experimental ということではないので、説明通りに実行すれば、ちゃんと(パソコン上で)音声データで学習し、(パソコン上で)問題無く推論もできます。問題は、TensorFlow Lite 用のプロジェクトである micro_speech です。こちらは experimental 扱いになっていて、いろいろ試してはみているものの、すんなりとは動いてくれません。コードを読みながら、その背景を少し推測してみました。
micro_speech のややこしさは、まず最初に、TensorFlow Lite for microcontrollers が experimental であり、ドキュメントが十分に用意されていないことと、モデルの学習時や、マイコン用のプロジェクト生成時にバックグラウンドで用いる、speech_commands のオプションが多岐にわたることです。特に重要なオプションは、以下の 2つです。
- –model_architecture
- –preprocess
これが分からないと、micro_speech は理解できません。いずれも十分なドキュメントはないのですが、コードを読みながら推測します。
model_architecture オプション
これは、音声認識で用いる TensorFlow モデル(グラフ)として、どのようなものを利用するか、ということで、おそらく研究者がいろいろなモデルによる性能差を見たくて、複数の異なるモデルを用意しているようです。さらにもう一つ、マイコン上(つまり TensorFlow Lite)で音声認識(つまり micro_speech)を実現するために、どこまでモデルをコンパクトにできるか、つまり演算量とメモリ使用量を減らしつつ、どこまで性能を維持できるか、という実験のために、追加のモデルが用意されているようです。
本オプションで指定できるモデルには、以下のようなものがあります。(2019/7/3 現在)
conv
これがデフォルトです。speech_commands の models.py というファイルを読むと説明があります。
このモデルは、Convolutional Neural Networks for Small-footprint Keyword Spotting(Tara N. Sainath, Carolina Parada)という論文に基づいていて、論文中の cnn-trad-fpool3 というアーキテクチャをベースにしているそうです。いずれ機会があったら、Netron で綺麗なチャートを表示してみたいですが、詳しくは models.py をお読みください。ピュアな TensorFlow で記述されています。
このモデルは性能は良いそうですが、重みパラメタが多く演算量も大きいとのことです。つまり、マイコン(TensorFlow Lite)には向いていないようです。
low_latency_conv
前述の論部で、cnn-one-fstride4 アーキテクチャに相当するそうです。重みパラメタや計算量は conv モデルより小さいようですが、推論の品質は若干下がるとのことです。
single_fc
隠れ層を一つだけ持つ、全接続のニューラルネットに基づく実装のようです。
low_latency_svdf
こちらは、Compressing Deep Neural Networks using a Rank-Constrained Topology という論文に基づく SVDF (Funk SVD?) モデルに基づく実装で、conv モデルよりも認識率は劣るものの、重みパラメタが少なく、また演算量が圧倒的に少ないのが特徴だそうです。(すいません。全然分からないので直訳です。)
tiny_conv
こちらは、micro_speech で利用されているモデルです。conv モデルに比べて性能はかなり劣るそうですが、モデルをコンパクトにしたことで、20Kバイトの RAM、32Kバイトの ROM があれば実装できるということです。
なお、このモデルのポイントは、消費電力の小さいマイコンでこちらを動かすことで、まずは何かしらの音声が入力されていることを見つけることを意図している、ということのようです。一度その気配を察知したら、より高性能のマイコンを起こして、過去に遡った音声信号を含めて与えることで、より精度の高い推論ができる、ということです。
tiny_embedding_conv
これも tiny_conv と同様に、性能は犠牲にしてモデルをコンパクトにしたものということです。コメントを読む限り、tiny_conv との設計意図の違いは分かりませんでした。モデルの構造がかなり違うので、いずれ詳細を追ってみたいです。
preprocess オプション
続いて別のオプションである –preprocess についてです。こちらも面倒です。
speech_commands で全般に用いているアイデアは、入力音声(時間軸サンプル)を振幅(変位)の 1次元データ(ベクトル)で与えるのではなく、周波数軸表現(スペクトログラム)に変換し、時間軸と周波数軸の 2次元データ(マトリクス)の形で与え、CNN(畳み込みニューラルネット)で処理しよう、というものです。
speech_commands では、こちらにもいくつかのオプションを用意しています。input_data.py というファイルが詳しいです。
average
speech_commands では 1秒分の音声信号を得た後、(デフォルトで?)30ミリ秒毎に切り出した音声フレームを 256点の FFT で変換しますが、これをさらに削減して(周波数軸上の) 40個ほどの特徴量に変換しています。時間軸方向では、音声フレームは 10ミリ秒ずつオーバーラップさせることで、1秒の音声を 20ミリ秒毎にストライドさせています。結果として、時間軸方向に 50個、周波数方向で 40個で 50 x 40 程度のマトリクスを作成し、これを 2次元の畳み込み層(Conv2D)に与えます。
この average オプションでは、上記の周波数軸方向の削減において、隣接する 6つの特徴量を平均することで、40個に削減する、というアプローチのようです。
mfcc
これがデフォルトです。
こちらの preprocess オプションでは average と異なり、平均値を求めるのではなく、スペクトログラムをさらに MFCC(メル周波数ケプストラム係数)というものに変換して、新しい特徴量を得ています。MFCC については、こちらが詳しいです。一般的な音響信号でなく、人が発生した音声においては、average を用いるより mfcc を使ったほうが、良い結果が出るということです。
micro
これが一番理解に手間取る preprocess オプションです。詳しい説明が見つからず推測ですが、マイコン上の TensorFlow Lite のために用意したもののようです。音声信号の特徴量としては MFCC が良いのですが、マイコンで実装しようとすると処理量が大きくなる懸念があります(たぶん)。そのため、最適化した preprocess オプションを用意したかったのだと思います。
ところがこの preprocess オプションの問題は、なんと、標準の TensorFlow ではサポートされていない特殊な TensorFlow オペレター lite.experimental.microfrontend.python.ops.audio_microfrontend(frontend_op)を用いるという点です。つまり、TensorFlow をソースから再ビルドしてこのオペレターを有効にしないと、preprocess micro オプションは使えないのです。面倒くさーい。なぜこのようなことになってしまったのか、以下、私の「推測」です。
パソコン等で実現する場合は Python ライブラリによる preprocess を実装することができます。NumPy や SciPy を使えば、そこそこ高速な実装ができるでしょう。しかし、Python で実装してしまうとマイコンでは動作させることができません。パソコンによるモデル学習時には Python(たとえば SciPy)の実装を使い、マイコンでは C で書いた実装を使う、ということもできると思うのですが、おそらく Google の設計者は、それを嫌ったのです。なぜかというと、SciPy と C の実装では、細部に違いが出てしまい、結果として、パソコン上(クラウドでも GPU でも良いけど)でモデルを学習しても、マイコン上の C の実装では十分な精度が得られない可能性がある、ということでしょう。(SciPy 等の実装は、ライブラリのバージョンアップ等により、演算結果が微妙に変わっていってしまう可能性もある。)
そこで設計者は、上記 preprocess micro オプションのための専用の TensorFlow オペレター audio_microfrontend を C++ で実装した訳です。これは(現在のところ)標準の TensorFlow には含まれません。しかし、C++ で実装しているので、ラッパをかぶせれば Python からも利用できますし、C++ コードをマイコンでコンパイルすれば、同等の結果を得ることができるでしょう。
ちなみに、audio_microfrontend オペレターは tensorflow/tensorflow/lite/experimental/microfrontend/python/ops/audio_microfrontend_op.py にあります。C++ による実装は、たぶん tensorflow//tensorflow/lite/experimental/microfrontend/ops/audio_microfrontend_op.cc です。
終わりに
さて。私のパソコンはブンブンとクーリングファンを回しながら、Docker 上で TensorFlow ソースを bazel ビルドしています。今日は正しくビルドできるでしょうか。。。
上記のように、micro_speech をマイコンで動かす、さらに、独自に作ったトレーニング音声(お客様にデモするには、日本語のほうが訴求するでしょう)で学習させ、はたまた、実際にマイクから取り込んだ音声を使って処理するには、なかなか道のりが険しいことが予想されます。
そんなこんなのうちに、TensorFlow Lite for microcontrollers(長い…)が正式リリースになってしまうかも知れませんが、勉強は引き続き続けていきたいと思います。
上記論文に出てきたモデルを Keras だけで実装した実例もあるようですが、音声認識のキモとしては、前処理(つまり MFCC など)にもノウハウがありそうなので、もう少し micro_speech に食らいついていきたいところです。
今日はここまで。