Teachable Machine を使った音声からの任意のキーワードの検出（ブラウザ上で機械学習）

**【2020/2/6 追記】 1回の操作で録音可能な時間（デフォルト2秒）の変更について、追記をしました。**
**【2020/2/14 追記】 音を学習させて ブラウザ上のJavaScript のプログラムで制御してみた際の動画ツイートを追記しました**
**【2020/2/15 追記】 上記の JavaScript のプログラムでモデルを用いた音の判別のやり方について続きの記事を公開した話を追記しました**

## はじめに

昨年の 11月に、Facebook に[情報の投稿](https://www.facebook.com/yosuke.toyota/posts/2514608971985613)をしたものの、全く試していなかった Google さんの [Teachable Machine](https://teachablemachine.withgoogle.com/)。
<img width="411" alt="Facebook投稿" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/adc56901-8733-e9d3-27f3-f1ab0937db17.jpeg">
それを、ちょっと前に参加したもくもく会で、機械学習系の話題がでたときに試してみようと思い、軽く試してみました（この時試したのは、画像を対象にした機械学習）。
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">Teachable Machineで試した内容は<br>カメラ撮影された3つの動作（1 手を振る、2 何もしない、3 手をグーにしてかざす）を取り込んで学習、その後カメラにうつった内容が3クラスのどれか判別するもの。<br><br>試せてはないけど、モデルのダウンロードや、特定言語で実装するときのサンプルコードもでてくる。 <a href="https://t.co/bB0UDiS4sO">https://t.co/bB0UDiS4sO</a></p>&mdash; you (@youtoy) <a href="https://twitter.com/youtoy/status/1223515764751138816?ref_src=twsrc%5Etfw">February 1, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
その時に、すごく簡単に「ブラウザ上で学習から推論まで行えた」のが印象に残り、また、試す過程で目にした「音声の機械学習」が気になったりもして、自宅に戻ってから音声を使うものを試しました。

### この記事で扱う内容
今回の記事で扱うのは、その音声を用いた機械学習についてです。
<img width="491" alt="Teachable_Machine_音声.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/a0960b49-9ba6-68dc-dbae-4fbed3916b7f.jpeg">
具体的には、しゃべっている内容（マイクで取得した音声）からの特定のキーワードの検出になります。

## 音声を Teachable Machine で扱う

### 音声からのキーワード抽出を試した結果

まず、ブラウザ上でどのようなことが試せたか、ご覧ください。
こちらのツイートの動画は、ブラウザ上での学習でモデル作成をした後の段階、推論（音声からのキーワード検出）を行っているところです。
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">Teachable Machine を使うと、音声からの任意のキーワード検出ができてしまうのでは？<br>と思って、試しに「Googleアシスタント、Alexa、Siri、Clova」のウェイクワードを音声から判別するものを作ってみました。<br><br>そうしたら、思いのほか簡単にサンプルができてしまった。<br>Teachable Machine すごいw <a href="https://t.co/D8Lg1Y55Lw">pic.twitter.com/D8Lg1Y55Lw</a></p>&mdash; you (@youtoy) <a href="https://twitter.com/youtoy/status/1223565241318920197?ref_src=twsrc%5Etfw">February 1, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
動画について軽く補足しておくと、動画の真ん中あたりに「Output」という書かれたところがあって、さらにその下には
　・Background Noise
　・Class2
　・Class3
　・Class4
　・Class5
という文字と、その横で長さが変わっている色つきのバーが表示されています。

そして、色つきのバーが横に伸びるのは、以下のような対応関係になっているのが分かります。
　・Background Noise ⇒ 背景ノイズが入力されている（何もしゃべっていない）
　・Class2 ⇒ 「OK Google」というキーワードを検出
　・Class3 ⇒ 「Hey Siri」というキーワードを検出
　・Class4 ⇒ 「Alexa」というキーワードを検出
　・Class5 ⇒ 「ねぇClova」というキーワードを検出
これは、マイクからリアルタイムに入力される音声の中から、自分が Teachable Machine を使って学習させた4つのキーワード（4つのAIアシスタントのウェイクワード）が検出されたかどうか、を表示している形になります。

それでは、学習をどのように行ったか、という話を書いていきます。

### ブラウザ上での Teachable Machine での音声の機械学習
まず、[Teachable Machine のプロジェクト選択のページ](https://teachablemachine.withgoogle.com/train)で「Audio Project」を選択します。

そうすると以下の、左・真ん中・右の3つの部分におおまかに分かれたような画面が出てきます。
さらに、左側の部分には、以下の画像の赤い矢印で示した「Background Noise」、「Class2」「Add a Class」といった部分が縦に並んでいます。
<img width="585" alt="Audio_Model_-_Teachable_Machines.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/62079d6f-7e2a-b2a1-4a5f-d290fa476ce1.jpeg">

#### Background Noise
Background Noise については、下記の黒背景の部分の説明のとおり、
　「最低20秒以上の、背景ノイズ」
を登録する部分のようです。また、この背景ノイズは削除・無効化はできない必須のデータとなるようです。
<img width="512" alt="Audio_Model_-_Teachable_Machines_ノイズ.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/6583d0f9-b1c4-c837-9c3c-c32a9df7c970.jpeg">
特殊なノイズがある環境下での音声認識をやる等といった話でなければ、「Micボタン」を押して、20秒以上の何もしゃべっていない状態の音を録音すれば良いかと思います。
「Micボタン」を押すと以下の画面になるので、設定はそのままで「Record 20 Seconds と書かれたボタン」を押して録音をします。
<img width="542" alt="Audio_Model_-_Teachable_Machines_ノイズ2.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/a4733d10-24ed-bd5f-5dd2-33ba0887adc9.jpeg">
録音が終わると、以下のような画面になったかと思います。
以下の画像の赤い矢印で示した、「Extract Sample と書かれたボタン」を押してください。
<img width="560" alt="Audio_Model_-_Teachable_Machines_ノイズ3.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/f6c78383-5206-afce-c781-1c68f12b6025.jpeg">
そうすると、以下の画面のように上記で白背景だった部分に音声データを1秒ずつ切り出したと思われるデータが並びます。
<img width="545" alt="Audio_Model_-_Teachable_Machines_ノイズ5.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/4ec0aac6-0b8b-4680-0b2a-bd2dfec34e3a.jpeg">
切り出された個々のデータをクリックすると、1秒分のデータの再生・削除ができるようです（以下の画像を参照）。
<img width="548" alt="Audio_Model_-_Teachable_Machines_ノイズ5.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/bc3dbe09-de3c-bc05-1c31-30bc603a0a55.jpeg">
とりあえず、ここは特殊な背景ノイズを録音する意図もないので、ここで得られたデータ全てを利用することとします。

#### 検出したい音声の入力 〜その1〜
それでは、検出をしたい音声（キーワード）を入力していきます。
以下の画像にある、Class2 のところに音声を入力していきます。以下の赤い矢印で示した部分から、Class2 という名前は変更もできるようですが、今回はそのまま使っていきます。
<img width="566" alt="Audio_Model_-_Teachable_Machines_クラス2a.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/9948aab7-6420-8d58-a29f-9a33278ccddc.jpeg">
「Micボタン」を押すと、以下のような画面になります。
左下のボタン、右上の領域に書かれた内容を見てみると「2秒の音声を録音」して、「少なくとも8秒分以上のデータが必要」となるようです。
（**【2020/2/6 追記】 デフォルトは2秒となっている録音時間を、設定から変更可能です**）
<img width="548" alt="Audio_Model_-_Teachable_Machines_クラス2b.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/f1cc6dfb-7a2a-aa5c-e6ea-9a97cb83df02.jpeg">
2秒分の音声を録音した後、以下の画像の赤い矢印の部分の「Extract Sample と書かれたボタン」を押すと、以下の画像の赤い枠で囲まれた部分（2秒分の音声データ）が右の領域にも表示された状態になります。
<img width="559" alt="Audio_Model_-_Teachable_Machines_クラス2c.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/b257fe35-49a4-737b-cbb2-68d8edaeebc0.jpeg">
以下の画面の赤い枠で囲まれた部分を見ると、2つの音声サンプル（1秒分の音声データが2つ分）が登録された状態になったようです。以下の画像の赤い矢印で示した部分では、上記の「Background Noise」の登録の手順でもあったように、1秒分の音声データの再生や削除ができるようです。
この例では、「OK Google」としゃべるのを、2秒の間に2回行ってみています（1回の録音で、「OK Google」としゃべった1秒の音声が、2つ登録された状態になっています）。
<img width="559" alt="Audio_Model_-_Teachable_Machines_クラス2d.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/c2f39f84-80db-d8b1-65cc-9cb2599713a6.jpeg">
そして、同じことをあと3回繰り返しました。その結果が、以下の画面です。
<img width="561" alt="Audio_Model_-_Teachable_Machines_クラス2e.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/f8eaeba4-74b3-477d-8509-dfc5a2492629.jpeg">

> **【2020/2/6 追記】 下記の手順で、1回で録音可能な時間（＋ Micボタン押下後の録音開始までの待ち時間）を変更可能なようです**）
・以下の赤い矢印で示した歯車アイコンを押下
<img width="367" alt="録音の設定.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/8d3e2eb1-bd93-c0b5-ee25-1934f3ad9e2b.jpeg">
・以下の赤い矢印で示した2箇所の数字で、「Micボタン押下後の録音開始までの待ち時間」と「1回で録音可能な時間」を設定
<img width="369" alt="録音の設定2.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/cebc72fd-43e4-1f5f-f6e3-6b4de25a09f0.jpeg">
（↑ 設定変更の手順、ここまで）

この状態で、以下で示した画面の真ん中あたりにある「Train Model と書かれたボタン」が、グレーアウトしていた状態だったのが青い色になります。
<img width="617" alt="Audio_Model_-_Teachable_Machines_トレーニング.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/764de386-1654-92b1-a733-48cebe210da6.jpeg">
このボタンを押すと、録音した音声データから学習が行われて、推論を実行することができるようになります（今の状態では、「OK Google」というキーワードが検出されたかどうかが分かるだけ）。

今回は、複数のキーワードを検出する仕組みにしたいので、さらに音声データの追加を行っていきます。

#### 検出したい音声の入力 〜その2 から その4〜
では、以下の画面の「Add a class と書かれたボタン」を押してみましょう。
<img width="670" alt="Audio_Model_-_Teachable_Machines_クラス3以降.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/3d532fdf-224b-c556-c10c-111e703838d7.jpeg">
そうすると、以下のように、上記の Class2 のデータを入力していく前の状態の部分が追加されるかと思います。後は、上記の Class2 で音声を入力していったのと同じ手順で、この Class3 でも異なるキーワードを入力していきます。
冒頭に動画で紹介した内容では、Class3 は「Hey Siri」というキーワードを入力しています。
<img width="579" alt="Audio_Model_-_Teachable_Machines_クラス3以降2.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/00508a77-900f-2dde-9598-bd067968fc16.jpeg">
最低でも、8秒分のデータを入力しましょう。
<img width="628" alt="Audio_Model_-_Teachable_Machines_クラス3以降3.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/1a63ac88-9a5d-cafc-d7a7-3b238382b7b2.jpeg">
そして、さらに「Add a class と書かれたボタン」を押して Class4 と Class5 を追加し、それぞれ「Alexa」 と 「ねえ Clova」というキーワードをそれぞれ入力していきます。

これで、必要なデータが揃いました。
<img width="350" alt="Audio_Model_-_Teachable_Machines_全クラス.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/cc84a1cd-0742-0beb-03df-a1ca835a9180.jpeg">

#### 学習の実行
それでは、画面内の「Train Model と書かれたボタン」を押して、学習を行います。
<img width="535" alt="Audio_Model_-_Teachable_Machines_トレーニング実行.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/ca086a30-b24d-ecdd-f826-f3dfe0ce9959.jpeg">
そうすると、以下の赤枠で示したようなメッセージがでたり、表示がされたりして学習が実行されていきます。
<img width="653" alt="Audio_Model_-_Teachable_Machines_学習中.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/423299d9-8b6a-fdfd-e6ac-47d974c07e13.jpeg">

#### 推論（キーワードの検出）
学習が終了すると、自動的に以下の画像のように、画面の右のほうでマイクから入力される音声からのキーワード検出が試せる部分が表示されます。
以下の画像は「OK Google」とマイクでしゃべった直後のキャプチャになります。
<img width="656" alt="Audio_Model_-_Teachable_Machines_推論.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/3b9e8eee-8b6c-39b4-9d91-060ef2867ede.jpeg">
そのため、Class2 の部分の色つきのバーが横にのびています。
どうやら、入力された音声は
　100％、「OK Google」としゃべっている（他の、Background Noise である可能性や、Class3 から Class5 で入力した音声の可能性はない）
と判断しているようです。

### 作成したモデルをプログラムで利用する

このブラウザ上で作成したモデルは、クラウドにホスティングしたり、ダウンロードしたりすることができるようです。
以下の「Export Model」というボタンを押してみます。
<img width="566" alt="Audio_Model_-_Teachable_Machines_エクスポート.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/c78d44eb-7e4c-359a-ca3d-1ce3336bd0fc.jpeg">
そうすると、以下のようなウィンドウが画面内に表示されます。
<img width="835" alt="Audio_Model_-_Teachable_Machines_エクスポート2.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/752bae99-c503-f11a-8f5e-55c7eac92f89.jpeg">
画面内の「Upload my model と書かれたボタン」を押すとモデルデータのアップロードが行われ、それが完了すると以下の赤枠で示したとおりアップロード先のモデルを参照するための URL が発行されます。
<img width="842" alt="Audio_Model_-_Teachable_Machines_クラウド.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/a252e5fa-b9f1-40fc-0ab0-cfe541f597a5.jpeg">
また、「Downloadボタン」を押すと、以下のような3つのデータが含まれたZIPファイルでダウンロードすることができます。
<img width="449" alt="ダウンロードしたモデル.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/f63d59e6-9fca-8b6a-6e2a-e83998560915.jpeg">

上記で、クラウドにアップロードしたり、PC へとダウンロードしたモデルは、自分が書いたプログラムで利用することができます。
それを実行するための簡単なサンプルについては、以下の赤枠や赤い矢印で示したとおり、「JavaScript」か「p5.js」のソースコードを選択して見ることができます。
<img width="822" alt="Audio_Model_-_Teachable_Machines_使い方.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/88e46aa8-3733-0910-657c-29db6afc73e8.jpeg">
クラウドにアップロードした場合も、PC へダウンロードした場合も試してみましたが、サンプルのプログラムを少し書きかえたり等の追加対応をすることで、プログラムから利用することができました。
また、軽く確認してみたところ、モデルをダウンロードして行う場合はオフラインでの動作も可能なようでした。

## おわりに

Teachable Machine を使うことで、ブラウザの上だけで以下の内容を簡単に試すことができました。
　・音声の録音
　・録音したデータを使った学習（モデルの作成）
　・作成したモデルを使った推論（音声からのキーワード検出）
　・作成したモデルのホスティングやダウンロード
録音できる時間の制約はあるので、認識できる音声に制約はできそうですが、簡単なキーワード検出くらいであれば比較的簡単に実現できそうです。

また、今回は音声データを最低限必要な8秒分ずつしか用意しませんでしたが、キーワード検出は高精度に行えていたようでした。
今後も、学習したモデルをプログラムで利用するものをきちんと試してみたり、これをうまく活用した使い道を考えてみたりできればと思います。

### 【2020/2/14 追記】 音を対象にしたモデル作成とJavaScriptでの利用（ [toio](https://toio.io/) を制御）のデモ動画
タンバリンや卓上ベルの音を学習させてモデルを作成し、それらの音の違いでロボットトイの [toio](https://toio.io/) を制御するプログラムをJavaScriptで書いてみました（ブラウザ上でプログラムを動かし、toio との通信部分は Web Bluetooth API を利用）。
<blockquote class="twitter-tweet" data-conversation="none"><p lang="ja" dir="ltr">この前の実験の続き。<br><br>音の種類によって <a href="https://twitter.com/hashtag/toio?src=hash&amp;ref_src=twsrc%5Etfw">#toio</a> が異なるスピードで回転したり、前に動いたり。<a href="https://twitter.com/hashtag/toiotomo?src=hash&amp;ref_src=twsrc%5Etfw">#toiotomo</a> <a href="https://t.co/U5QU2rLMFl">pic.twitter.com/U5QU2rLMFl</a></p>&mdash; you (@youtoy) <a href="https://twitter.com/youtoy/status/1228017144375341056?ref_src=twsrc%5Etfw">February 13, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

### 【2020/2/15 追記】 音を対象にしたモデル作成とJavaScriptでの利用（ [toio](https://toio.io/) を制御）のやり方に関する Qiita の記事

上記のデモ動画の仕組みを作った話に関する Qiita の記事を公開しました。

●toio を音で制御してみた（Audio用の Teachable Machine でベルやタンバリンの音を機械学習） - Qiita
　https://qiita.com/youtoy/items/37f70bb4ce630e6cbd92

### 【2020/10/7 追記】 音と声を利用したtoioの制御（Maker Faire Tokyo 2020 の出展にも利用）
10/3・4（土・日）に toio のコミュニティで Maker Faire Tokyo 2020 に出展して、その際に Teachable Machine を使った作品も展示の 1つに加えてました。
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr"><a href="https://twitter.com/hashtag/MFTokyo2020?src=hash&amp;ref_src=twsrc%5Etfw">#MFTokyo2020</a> の <a href="https://twitter.com/hashtag/toiotomo?src=hash&amp;ref_src=twsrc%5Etfw">#toiotomo</a> のブースでは、実際の作品をいくつか展示しつつ、コミュニティメンバーの作品の動画展示も合わせて行う予定なのですが、<br>その動画展示に関して自分も動画を出すために、過去の作品のいくつかを60秒くらいにまとめてみました。<a href="https://t.co/BESQ1qTy7n">https://t.co/BESQ1qTy7n</a><a href="https://twitter.com/hashtag/toio?src=hash&amp;ref_src=twsrc%5Etfw">#toio</a> <a href="https://t.co/wb4wZMWJBt">pic.twitter.com/wb4wZMWJBt</a></p>&mdash; you (@youtoy) <a href="https://twitter.com/youtoy/status/1311445321470210048?ref_src=twsrc%5Etfw">September 30, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr"><a href="https://twitter.com/hashtag/MFTokyo2020?src=hash&amp;ref_src=twsrc%5Etfw">#MFTokyo2020</a> の初日の会場にて、音の機械学習をやり直したら（会場のノイズ込みの学習データになるようにしたら）、<br>2日目の会場では、昨日と別物であるかのように良い感じに動くようになった！<a href="https://twitter.com/hashtag/toio?src=hash&amp;ref_src=twsrc%5Etfw">#toio</a> <a href="https://twitter.com/hashtag/toiotomo?src=hash&amp;ref_src=twsrc%5Etfw">#toiotomo</a> <a href="https://t.co/ejcn43IjIe">https://t.co/ejcn43IjIe</a> <a href="https://t.co/vXpTSq3Sd6">pic.twitter.com/vXpTSq3Sd6</a></p>&mdash; you (@youtoy) <a href="https://twitter.com/youtoy/status/1312576667391459328?ref_src=twsrc%5Etfw">October 4, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">青いベルの音と緑のベルの音を機械学習で判別。地味に難しいことやってる<a href="https://twitter.com/hashtag/MFTokyo2020?src=hash&amp;ref_src=twsrc%5Etfw">#MFTokyo2020</a> <a href="https://twitter.com/hashtag/toio?src=hash&amp;ref_src=twsrc%5Etfw">#toio</a> <a href="https://t.co/fpqogGMZ5w">pic.twitter.com/fpqogGMZ5w</a></p>&mdash; ロボ先輩@3rd Factory (@3rd_factory_ro) <a href="https://twitter.com/3rd_factory_ro/status/1312615966870695936?ref_src=twsrc%5Etfw">October 4, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>