Snowboy Hotword DetectionをRaspberry Piで動かす

# はじめに

クラウドサービスに接続して音声ベースでコミュニケーションするデバイスにおいては、「Alexa」や「OK Google」、「Hey Siri」などの「ホットワード」を用いるようになっていることがほとんどです。ホットワードはその後に音声コマンドが続くことを示すために区切りとしての役割を果たしているだけでなく、音声認識の実装においても重要な役割を果たしています。マイクで捉えた音声データを常時サーバに対して送信し続けることは、プライバシーやクラウドサービス利用料金、消費電力など様々な点で問題があります。それに対して、デバイス側ではホットワードの認識までを行い、そこから先は音声データをクラウドサービスに送信してクラウドサービス側で処理するようにすることで、様々な点でバランスのとれた実装になります。

[Snowboy Hotword Detection](https://snowboy.kitt.ai/)は[KITT.AI](http://kitt.ai/)が提供する、クラウドベースで学習したホットワードのモデルデータをRaspberry Piなどの環境で利用できるようにするライブラリです。個人的利用であれば無償で利用でき、商用利用の場合には有償でライセンスとテクニカルサポートが提供されます（ウェブサイト上では商用利用の際の料金に関しては記載されていません）。

# 準備

## ライブラリのインストール

最初に、Pythonからオーディオを扱うためのライブラリなど、Snowboyを動作させるのに必要となるライブラリをインストールします。

```shell-session
$ sudo apt-get install swig3.0 python-pyaudio python3-pyaudio sox
$ pip install pyaudio
$ sudo apt-get install libatlas-base-dev
```

## サウンドデバイスの設定と動作確認

次に、USBマイクをRaspberry Piに接続した状態でオーディオの再生及び録音のために利用できるデバイスを確認します。

```shell-session
$ aplay -l
**** List of PLAYBACK Hardware Devices ****
card 1: ALSA [bcm2835 ALSA], device 0: bcm2835 ALSA [bcm2835 ALSA]
  Subdevices: 8/8
  Subdevice #0: subdevice #0
  Subdevice #1: subdevice #1
  Subdevice #2: subdevice #2
  Subdevice #3: subdevice #3
  Subdevice #4: subdevice #4
  Subdevice #5: subdevice #5
  Subdevice #6: subdevice #6
  Subdevice #7: subdevice #7
card 1: ALSA [bcm2835 ALSA], device 1: bcm2835 ALSA [bcm2835 IEC958/HDMI]
  Subdevices: 1/1
  Subdevice #0: subdevice #0
```

```shell-session
$ arecord -l
**** List of CAPTURE Hardware Devices ****
card 0: Device [USB PnP Sound Device], device 0: USB Audio [USB Audio]
  Subdevices: 1/1
  Subdevice #0: subdevice #0
```

この例では、Raspberry Piのボード上に搭載されているサウンドデバイスはカード1のデバイス0として、USBマイクはカード0のデバイス0として認識されていますので、`$ nano ~/.asoundrc`で設定ファイルを開き、ファイルの中身を以下のように置き換えます。

```
pcm.!default {
  type asym
   playback.pcm {
     type plug
     slave.pcm "hw:1,0"
   }
   capture.pcm {
     type plug
     slave.pcm "hw:0,0"
   }
}
```

これでサウンドの再生及び録音に使用するデバイスが設定できましたので、`$ rec test.wav`で録音し、生成されたファイルを`$ play test.wav`で再生できることを確認します。ここで、音量が小さ過ぎたり大き過ぎたりしたら`$ alsamixer`でALSAミキサーを起動し、入力及び出力の音量を80程度に変更して再度確認し、必要十分な音量で録音及び再生できることを確認します。

# 動作確認

SnowboyをRaspberry Pi上にダウンロードし解凍します。

```shell-session
$ wget https://s3-us-west-2.amazonaws.com/snowboy/snowboy-releases/rpi-arm-raspbian-8.0-1.1.0.tar.bz2
$ tar -xvf rpi-arm-raspbian-8.0-1.1.0.tar.bz2 
$ cd rpi-arm-raspbian-8.0-1.1.0
```

解凍が終わったら、`resources`フォルダに入っているテストデータ`snowboy.umdl`で動作を確認してみます。マイクに向かって「snowboy」と話しかけ、認識されるとコマンドラインにメッセージが表示されると共に確認音が再生されます。

```shell-session
$ python demo.py resources/snowboy.umdl
ALSA lib pcm_dmix.c:1022:(snd_pcm_dmix_open) unable to open slave
ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.rear
ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.center_lfe
ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.side
ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.hdmi
ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.hdmi
ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.modem
ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.modem
ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.phoneline
ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.phoneline
ALSA lib pulse.c:243:(pulse_connect) PulseAudio: Unable to connect: Connection refused

ALSA lib pulse.c:243:(pulse_connect) PulseAudio: Unable to connect: Connection refused

ALSA lib pcm_dmix.c:1022:(snd_pcm_dmix_open) unable to open slave
Cannot connect to server socket err = No such file or directory
Cannot connect to server request channel
jack server is not running or cannot be started
Listening... Press Ctrl+C to exit
INFO:snowboy:Keyword 1 detected at time: 2017-01-29 17:32:17
INFO:snowboy:Keyword 1 detected at time: 2017-01-29 17:32:18
INFO:snowboy:Keyword 1 detected at time: 2017-01-29 17:32:24
```

# 独自のHotwordを追加する

Snowboyウェブサイトで簡単に独自のホットワードを追加し、モデルデータをダウンロードすることができます。

## Snowboyウェブサイトでの登録

ユーザ登録はGitHubなどのアカウントを用いて簡単に行うことができます。ユーザ登録ができるとダッシュボードから登録されているホットワードの一覧を見ることができます。ここでは、言語に「Japanese」を絞り込んだ中で表示されたホットワードの中から最初の1つである「クックパッド」を選んで試してみました。話者によらずユニバーサルに利用できるパブリックなモデルを作成するには2,000人分のデータが必要です。それに対して、パーソナルなモデルであればRecord and Downloadをクリックして3回分のデータを登録することですぐに作成できます。

![FireShot Capture 101 - Snowboy Hotword Detection - https___snowboy.kitt.ai_dashboard.png](https://qiita-image-store.s3.amazonaws.com/0/92577/5e40b3d5-371d-9171-785c-d129e33176a1.png)

Record and Downloadをクリックすると、次のようなダイアログが表示され、ウェブブラウザ上で3回分の音声を登録できます。

![スクリーンショット 2017-01-29 18.07.58.png](https://qiita-image-store.s3.amazonaws.com/0/92577/6b93fc26-6940-61e3-b470-f66060dca214.png)

登録が終わったら、話者の性別と年代と設定し、登録したホットワードが認識できるかどうかのテストを行います。必要に応じて感度を調整しながら何回か話しかけ、無事に認識できることが確認できると登録と共にダウンロードできるようになります。

![スクリーンショット 2017-01-29 18.09.43.png](https://qiita-image-store.s3.amazonaws.com/0/92577/a94ef821-2474-3b36-4d7b-945cefac60bc.png)

## 動作確認

ダウンロードしたデータは、最初にテストで用いたモデルと同様に拡張子が`pdml`のファイルになりますので、Raspberry Pi上に転送し、認識できるかどうか確認してみます。登録した時と同様にマイクに向かって「クックパッド」と話しかけると、無事に認識されました。

```shell-session
$ python demo.py resources/cookpad.pmdl 
...
Listening... Press Ctrl+C to exit
INFO:snowboy:Keyword 1 detected at time: 2017-01-29 18:50:16
INFO:snowboy:Keyword 1 detected at time: 2017-01-29 18:50:19
INFO:snowboy:Keyword 1 detected at time: 2017-01-29 18:50:20
```

# おわりに

以上、ごく簡単ではありますがSnowboyを紹介しました。ユニバーサルなモデルをつくるために必要なデータを集めるにはどうすれば良いか、機械学習一般に共通する課題はありますが、パーソナルなモデルであればすぐ試せるのはプロトタイピングに適していると言えそうです。

# リファレンス

- http://docs.kitt.ai/snowboy/
- https://github.com/alexa/alexa-avs-sample-app/wiki/Raspberry-Pi