AutoML Vision と RasPi でリビングのいろいろな音を認識する

#raspi #おうちIoT #お手軽ML #gcpja

Cloud AutoML Vision は、画像とその分類ラベルをクラウドにアップロードするだけで画像認識の機械学習モデルを作成できるサービス。ラーメン二郎のどんぶり画像から 95% 精度で店舗を当てたりできる高性能だけど、機械学習のディープな知識や経験がなくても使える。

で、これでまず試してみたかったのが、音の認識だ。画像じゃなくて音。

2 年くらい前に、画像認識用の CNN を使って音声を認識する論文が Microsoft Research から出てて、へぇーっと思った。スペクトログラムっていう、よく犯罪捜査で出てくる声紋のアレを使って音を画像にして、その模様から音の特徴を認識する。

CNN で音声認識(Microsoft Research)

なるほどなあ。。つまり、世の中のどんなデータでも、捉えたい特徴を画像の特徴として表せれば、画像認識で識別できる……ってことだ。

そして、AutoML Vision で高精度の画像認識モデルを作れるということは、スペクトログラムによる音認識も、自分で TensorFlow で CNN いじったりしなくても簡単に試せるのか。まじか。と思った。

そこで、年末年始のヒマな時間を使って試してみたら、4 日くらいでこんなのができた。

リビングで聴こえるいろんな音を RasPi のマイクで拾い、その種類を認識する。来客ベル、電子レンジの終了チャイム、人の声、キッチンで何かしてる音、くしゃみやせき、鼻をかむ、掃除機、ドアの閉まる音、その他、の 9 種類を検知できた。

これは何に使えるか。例えば、

  • お風呂が沸いた・ご飯が炊けた・来客ベルが鳴ったことを、IFTTT 経由の LINE 通知Google Home の push 通知(非公式)でお知らせ
  • リビングで誰かがしゃべったり、掃除機かけたりといった生活行動を検知。Google Sheet でライフログ集めたり Nature Remo や Switchbot で家電を動かす
  • くしゃみ・せき・鼻をかむ音の回数が多くなったらスマートスピーカーが「もしかしてカゼですか? あったかくしてくださいね」と気づかい、加湿器が動き出し、アマゾンで勝手に薬を発注する

ってあたりが思いつく(それぞれ実利的かはおいといて)。リビングだけじゃなくて、オフィスや野外など、音が拾える場所ならどこでも使える。

開発に必要なものは、 RasPi と USB マイク。学習に使う AutoML Vision は、そこそこの精度でいいなら無償枠で十分。5 万円かけてじっくり学習すれば、もっと精度の高いモデルができる。これを、ML 専門家でなくてもさくっと作れるのがいいところ。

以下、この音認識ガジェットを作るためのおおまかな手順を紹介したい。今回は bash スクリプトですべて済ませた。コードはここ

RasPi3 で録音の設定

まずは RasPi3 に USB マイクをつなげて録音する準備。この記事を参考に、arecord と sox が動くことを確認。いろいろ試したのち、arecord はこんな設定で落ち着いた。

arecord --max-file-time 1 —-use-strftime raw_%Y%m%d-%H%M%S.wav \
-q -c 1 -D plughw:1,0 -r 16000 -f S32_LE &
  • -f S32_LE :32 bit little endian の wav ファイルで保存
  • -q :いろいろ細かいメッセージは出さない
  • -c 1 :チャンネル数は 1(モノラル)
  • -r 16000:サンプリングレート 16 kHz で録音
  • -D plughw:1,0:lsusb で確認した USB マイクのカード番号 0 とサブデバイス番号 1
  • --max-file-time 1 :wav ファイルを 1 秒ごとに保存
  • --use-strftime:wav ファイル名のフォーマットを指定(例:raw_20190101–120000.wav)

これで、 ctrl-c で止めるまでずっと録音されっぱなしになり、1 秒ごとに wav ファイルが、

raw_20190101-120000.wav
raw_20190101-120001.wav
raw_20190101-120002.wav
...

って感じに保存される。

音が鳴ったら切り出す

ここからは、sox で音を加工するbash スクリプトを書いていく。sox は音を切ったり貼ったりたいていの音加工はなんでもできるスゴいツールだ。

arecord から出てくる細切れの wav ファイルから、ある程度の音量の音だけを切り出したい。まずは 2 秒分をひとつの wav にマージ。

sox <マージするファイル名のリスト> <保存ファイル名>

これで 2 秒間の wav ファイルができる。つぎに、この記事を参考に sox の silence フィルタを使って無音部分をカット。

sox <元ファイル名> <保存ファイル名> silence 1 0.1 3%

音量 3% 以上の音が 0.1 秒以上続いたところから後を保存する。

ここで、1 秒間に満たないファイルができた場合は削除して処理終了。1 秒以上のファイルになった場合は、冒頭の 1 秒間だけを取り出す。

sox <元ファイル名> <保存ファイル名> trim 0 1

これで、「何かの音が鳴ったらそこだけ切り取って 1 秒間の wav ファイルにするスクリプト」ができた。1 秒間って長さは適当である。いずれにせよ、長さがバラバラよりも揃えておいた方が認識しやすいだろうなと思った。

この bash スクリプトを半日くらい動かしたら、2700 個の wav ファイルが貯まった。例えばこんな音。

ドアが閉まる音
電子レンジの終了チャイム
キッチンで何かしてる音

音のラベル付け

この音にラベルを付けてくためのスクリプトを書いた。wav ファイルをひとつずつ再生して、ラベルを手入力する。

$ ./label.sh
Label for 20190107-094321_out.wav (r: replay, d: delete): r
Label for 20190107-094321_out.wav (r: replay, d: delete): bell
Label for 20190107-094322_out.wav (r: replay, d: delete): bell

実際にラベル付けする時には、ひとつ前の音と同じラベルを入れることが多い。なので、ひとつ前のラベルをデフォルト値として出し、あとは改行だけでぽんぽんラベル付けできるようにした。ラベル付けにかかった時間は 1〜2 時間くらい。

ラベル付けといっても大したことはしてなくて、ラベルと同じ名前のディレクトリにファイルを移動してるだけ。今回はこんなラベルを付けたけど、用途に応じて好きなラベルを定義すればよい。

  • 0:その他
  • kitchen:キッチンで何かやってる音
  • voice:しゃべり声
  • door:ドアが閉まる音
  • bell:来客ベル
  • blow:鼻をかむ音
  • cough:せきとくしゃみ
  • cleaner:掃除機
  • microwave:電子レンジのできましたチャイム

「お風呂が沸きました」や「ご飯が炊けました」の認識も試したかったけど、どちらも一日一回しか鳴らないので音をたくさん集めるのが難しく、まだやってない。

音の data augmentation

2,700 個の音が集まったけど、学習するには数が少ないなと思ったので、data augmentation すなわち学習データの水増しの方法を考えた。

画像認識の場合は、元画像に対して回転・移動・変形・ノイズ追加・白色化など、いろいろ適用して数十倍に増やすやり方がよく使われ、 Keras 等ではそうした data augmentation 機能が簡単に呼び出せる。でも、音認識の場合はどうするんだろう。。とググったら、いくつか論文やライブラリが出てきた。音程を変えたり、ノイズを加えたり、いくつかのやり方が見つかった。

しかし今回の用途では音源もマイクも変化しないので、音程を変えたりノイズ入れたりしてもあんまり意味なさそう。なので、今回は音に normalization をかける方法だけ使った。これは小さい音も大きな音も一定の音量になるよう揃える加工である。sox の norm フィルタを使い、

for ((i=-7; i != -2; i++));
sox --norm=$i <元ファイル名> <保存ファイル名>
done

ってループを書いて、-7 dB から -3 dB まで、それぞれの音量に normalize された 5 つの wav ファイルを作った。つまり 5 倍の水増しで、合計 13,500 個になった。

この data augmentation が認識精度にどれだけ貢献しているのか……果たして他のフィルタの方がよいのか……めんどくさくて検証してないからわからない。俺たちは雰囲気で ML をやっている。

スペクトログラム画像を作る

学習データ作成の仕上げ。こんなふうにしてスペクトログラム画像を作る。sox なんでもできるな。

sox <元ファイル名> -c 1 -n rate 16k spectrogram -r -h -0 \
<保存ファイル名>
  • -c 1:チャンネル数は 1(モノラル)
  • -n:これ何だっけ。。
  • rate 16k:16 kHz 音声データとして読む
  • spectrogram:スペクトログラム画像を生成
  • -r:デフォルトで画像内に追加される凡例を削除
  • -h:色の派手な high-color 配色を使用。こっちの方が音の特徴が出るらしい
  • -o:png ファイル名を指定

ここまで、2,700 個の wav ファイルに対して normalization による水増しとスペクトログラム生成を RasPi3 上で実行すると、1〜2 時間くらいかかる。終わると、こんな画像ができる。

ドアが閉まる音
電子レンジの終了チャイム
キッチンで何かしてる音

これを人の目で「この音!」と見分けるのは難しい。電子レンジの音は特徴的な横線が入っててわかりやすいけど。AutoML Vision で見分けられるかやってみる。

AutoML Vision にアップロード

AutoML Vision を初めて使うときの手順はここで説明されているので省略。スペクトログラム画像を AutoML Vision にアップロードして学習を行うのだけど、その方法は 2 つある。

  • zip にまとめてブラウザからアップロードする(画像ファイルを収めたディレクトリ名がラベルとして認識される)
  • Google Cloud Storage に画像ファイルをアップロードしておいて、そのURI とラベルを並べた CSV ファイルをアップロードする

今回はスペクトログラム画像が 13,500 枚、1.9 GB ぶんある。なんとかブラウザからアップロードできる大きさなので、てっとり早く前者の zip ファイル方式を選んだ。

アップロードが終わると、AutoML Vision のダッシュボードに以下のようにサムネと読み込み件数が表示される。重複した画像は自動的に取り除かれるので、件数がすこし減っている。

AutoML Vision に画像をアップロードしたところ

各ラベルの件数も比較できる。

ラベルごとの画像枚数

普通に日常生活をしながら音を集めてると、どうしても偏ってしまう。そして後ほど実際に試してわかったけど、やはり集めた数の少ない音は認識精度も低くなる。なので、上記件数のうち bell や door は繰り返し録音して件数を増やしたものだ。ドアをなんどもバタンバタンしたり。

繰り返し録音できない音はちょっときびしそうだ…… USB マイクをいくつもつなげて同時録音するしかないかな。また、data augmentation をもうちょっと工夫して、多すぎる音を間引いたり少なすぎる音を水増ししたりすれば、より精度が向上するかもしれない。

AutoML Vision で学習

学習用の画像とラベルが正しく読み込まれたことを確認したら、TRAIN タブをクリックして学習を開始。

学習のご予算を選ぶ

ここで 2 つの選択肢があって、

  • 1 compute hour:15分〜数時間で終わるお手軽モード。精度はそこそこ。毎月 10 回まで無償
  • 24 compute hour:24 時間かけて学習する Google の本気モード。学習データにぴったりな ML モデルを ML が自動作成する仕組みで、ML エキスパートがじっくり時間をかけてカスタマイズしたような精度が出る。1 回あたりおよそ 5 万円かかる(2019 年 1 月現在。料金表はここ

とりあえずは前者の 1 compute hour を試してみる。小一時間で学習が終わるので、EVALUATE タブでモデルの精度を確認できる。こんな結果が出た。

AutoML Vision による音の予測結果(predicted label)と正解(true label)を比べた表

これは混同行列っていう表で、AutoML Vision による音の認識テスト結果(predicted label)と正解(true label)を比べたもの。青いところがラベルごとの精度を表していて、お手軽モードにしてはわるくない。スペクトログラム画像で音の認識ってほんとにできるんだな、と思った。

cleaner や bell なんかは高精度が出てるけど、cough と microwave はサンプルの少なさもあってあんまりいい精度が出ていない。ちなみにモデル全体の precision(適合率:認識が正しい割合)は 95.2%、recall(再現率:認識できた割合)は 76.4% であった。つまり、音が鳴ったとき検知できるのは 7〜8 割くらい。もうちょっと精度がほしい。

で、Google の本気モードである 24 compute hours を試してみた。こんな精度。

高すぎる……。precision/recall も見てみよう。

precision は 99.8%で recall は 95.6%、ROC カーブはほぼ直角。これ過学習(モデルが答えを丸暗記してしまう現象)になってそう。この結果はこのまま鵜呑みにできないので、実際に音認識を試したときの精度を追って検証する。

AutoML Vision の REST API を呼び出す

モデルの学習が終わったら、AutoML Vision の REST API を呼び出すだけで、このモデルを使った画像認識(prediction)をすぐに行える。自分でサーバを立てて ML ライブラリやモデルを入れたりする必要はない。サーバレスって便利だ。

PREDICT タブを開くと、こんなふうに呼び出せば使えるよという例が表示される。

つまり、画像を base64 にエンコードして JSON に埋め込み、curl で POST すればよい。

といっても、これを実行するまえに認証の設定を済ませておく必要がある。詳しい説明がこのドキュメントにあるけど、やるべきことは、

である。この準備ができたら、画像を base64 にしてリクエスト用の JSON ファイルに埋め込み、

curl -s -X POST -H "Content-Type: application/json" \
-H "Authorization: Bearer `cat access_token`" \
https://automl.googleapis.com/v1beta1/projects/<プロジェクトID>/locations/us-central1/models/<モデルID>:predict \
-d @api_request.json > api_response.json

として curl で API を呼び出す。今回はお手軽に済ませたのですべて bash スクリプトで 書いたけど、Python から API を呼び出すライブラリもある。REST API を呼び出して 3 秒ほどすると、認識結果のラベルとスコアが返ってくる。

{
"payload": [
{
"classification": {
"score": 0.8718641
},
"displayName": "kitchen"
}
]
}

これらを見て、RasPi のディスプレイに絵を描くなり、IFTTT で家電連携するなりすればよい。スコアが低い場合(0.5 とか 0.6 とか)は認識結果があまり当てにならないのでスキップしといた方がよい。

これで音認識ガジェットのできあがり。

音認識の精度とレイテンシとコスト

できあがったガジェットで、実際の音認識の性能をいろいろ検証してみた。

  • 実際の認識精度:実際にマイクを通してそれぞれの音を 10 回ずつ聞かせてみたところ、こんな結果になった。
声:100%
ドア:80%
せき:70%
来客ベル:100%
電子レンジ:100%
鼻をかむ:50%
キッチン:70%
掃除機:100%

決まった音しか鳴らない電子レンジや掃除機、来客ベルは誤認識がほとんど起きない。これらの用途なら、今回の仕組みでそのまま実用になりそうだ。

一方、ひとつひとつの音が毎回異なるもの、例えば鼻をかむ、せき、キッチンの音は、7 割程度に落ちる。とくに鼻をかむ音は、集めた音の数が少ない(あんまり何度も意味なく鼻をかむ気になれない)せいか半分しか当たらなかった。AutoML Vision の画面上で精度がとても高くなったのは、おそらく過学習のせいだろう。これらの認識精度を上げるには、もっともっと音のサンプルを集める必要がある。

もうひとつ気づいたのは、複数の音が混ざると誤認識が多い点。これは data augmentation を工夫すれば改善するかもしれない。もとの音に対して、別の音や環境雑音をランダムに選んで小さな音でミックスする等。

  • 認識のレイテンシ:上述のとおり、AutoML Vision の画像認識は、高精度な一方でレイテンシが高い。us-central1 リージョンで動いているせいもあるけど、curl で呼んでから答えが返るまでに 3 秒くらいかかるので、即時性が要求される用途にはあまり向かない。来客ベルを検知してお知らせしてる間にお客さん返っちゃう
  • 認識のコスト:AutoML Vision の画像認識は毎月 1,000 回までは無償で、その後は 1,000 回の認識あたり $3 かかる。一日 1,000 個の音が検出されるとして、一日あたりおよそ 300 円くらい。お手軽さと高精度を考えればこんなものだろうけど、たくさん音が検出される用途ではコストが気になりそう

まとめ

冒頭で書いたように、この音認識ガジェットは正月休みの 4日間くらいで作れた。音の加工や data augmentation などの前処理は自分で工夫する必要があったけど、ML モデル開発でいちばんハードルの高い作業はスキップできる。つまり、いまどきのイケてるモデルを見つけて理解して TensorFlow で書いたり、 TF 学習環境を用意したり、ハイパラチューンしたり……これらはすべて AutoML まかせ。なので、ML のディープな知識と経験がなくても、precision/recall 等の精度の見方、検証方法や過学習の防ぎ方といった基本知識があれば扱える。

このお手軽 ML で、身の回りのいろんな問題を解いてみたいなと思った。音認識だけでなく、センサーデータや時系列データ、ビジネスデータ等々、いろいろなデータの特徴を画像として取り出し、 AutoML Vision に入れるとどうなるか。人がぱっと見てわからないような特徴もばっちり捉えられるのだから、へーこんなことも分かるのか! って使い方がたくさん出てくるはず。


Disclaimer: この記事は個人的なものです。ここで述べられていることは私の個人的な意見に基づくものであり、私の雇用者には関係はありません。