ESP8266で指パッチン検出器を作る②

前回、FFTと相関係数で指パッチンを検出する仕組みを考えました。ところが、実際使ってみると何もしていないのに作動してしまうことがたまにあり、完璧ではありませんでした。

そこで今回は、誤作動しないような判定をSVM(サポートベクターマシン)で実現した話をご紹介します。

SVM(サポートベクターマシン)とは

SVMはデータの集合に対して、データを2つに分類するような境界線(面)を引く手法です。例によって、説明は適当です。詳しく知りたい方は「SVM」で検索すれば簡単に見つかると思います。

今回はこれを使って指パッチンデータとそれ以外のデータを分離したいと思います。分離が出来たら、得られた境界面を使って新たに得られたデータが指パッチンかそれ以外のどちらのグループに所属するか判定します。

SVMは学習データから分離面を求める時こそ多少計算を必要とするものの、一度分離面が得られたら新たなデータを判定するときは単純な計算ですむという利点があります。したがって学習だけパソコンで行えば、判定するときはESP8266でも十分です。

SVMを使って分離面を引く

前回は指パッチンの正解データだけを使いましたが、今回は指パッチンでないデータも必要です。前回同様にマイク入力をサンプリング周波数8000Hz、サンプル数1024のFFTにかけたデータ(512次元)を扱います。指パッチンしたときに出力されたデータと、それ以外の例えば手を叩いたときのデータをそれぞれ100データくらい用意しました。

早速SVMを使って分離面を引きたいと思います。SVMによる学習はpythonのライブラリであるscikit-learnを使いました。

環境を整えるのも面倒なので、Google Colaboratoryを使います。Google ColaboratoryはGoogleが提供しているWeb上でコーディングできるpython環境で、必要なパッケージも全て最初から入っているのでめちゃめちゃ簡単に開発を始められます。初めて使いましたが簡単すぎて驚きました。

scikit-learnでどうやってSVMをするかは下記のサイトがとても参考になりました。

https://data-science.gr.jp/implementation/iml_sklearn_svm.html

(ほとんど上記サイトのままですが、一応僕が使ったコードは後でgitか何かで公開する予定です。)

プログラムを走らせるとサポートベクトルとモデルの精度などの情報が出力されます。

得られた分離面を使ってデータを判定する

得られた分離面を使って、指パッチン判定を行っていきます。分離面といっても上記サイトの例ではRBFカーネルを用いた非線形分離を行っていますので、得られるのは面の式ではなく何個かのサポートベクトルになりますが、この辺の詳しい説明は割愛させていただきます(正直に言うと自分がまだ十分に理解していないからです笑)

とにかく、このサポートベクトルを使って新しいデータを判定するには、次の式に代入すれば良いとのことです。

サポートベクター分類の場合,以下の式を解くことで分類が出力される.上のパラメーターにおいて .support_vectors_ は以下の \(x_i\),.dual_coef_ は \(y_i\alpha_i\),.intercept_ は \(\rho\) である.

$$\begin{eqnarray}\operatorname{sgn}(\sum_{i=1}^{n}y_i\alpha_iK(x_i,x)+\rho)\tag{1}\end{eqnarray}$$

また,カーネルの中身は今回用いた RBF の場合,以下で計算されるが,このとき用いるのが .gamma であり,これは以下の \(\gamma\) である.すなわち,上のパラメーターさえ抽出すればものすごく簡単な計算で予測結果を得ることができる.これは別のプログラミング言語で書き換える際に便利.

$$K(x,x’)=e^{(-\gamma|x-x’|^2)}\tag{2}$$

上記サイトより引用

つまり、式(1)に学習によって得られたサポートベクトルと定数、マイクから拾ったデータを代入すれば、出てきた数値の符号によって指パッチンかそうでないかを判定できます。

この式をプログラムとして実装して、実際に判定するとこんな感じになります。

縦軸がSVMの出力、横軸はデータのindex。ある程度大きい音がしたときのみ判定。

グラフは作ったSVMを実行しながら何回か物音を立ててみた結果です。マイク入力に対して常にFFT→SVMを行うと計算が追いつかないので、ある程度大きい音がしたときだけ判定を行うようにしています。

見て分かる通り、指パッチンしたときだけSVMの出力が正になっていることがわかります。自分で言うのもなんですが、かなりいい精度で検出できます。手や硬いものをぶつけても検出しませんし、後ろで音楽を流していてもちゃんと検出してくれます。たまに指パッチンしても検出しないときがありますが、何もしていないときに誤検出してしまうことはありませんでした。

指パッチン検出器、完成です!
あとはこれを使って色々なアクションを起こすデバイスを作っていく予定です。

最後までご覧いただきありがとうございました!
こんなことできたらいいな、というアイデアがありましたら是非コメントで教えて下さい。

コメントを残す

メールアドレスが公開されることはありません。