EfficientWord-Netでウェイクワード検出
EfficientWord-Netとは
いくつかあるウェイクワード検出の一つ。全部見たわけではないけど、多分こんな感じ。
- Snowboy ※開発終了
- Pocketsphinx ※英語のみ・日本語未対応?
- Porcupine([Picovoice] (https://github.com/Picovoice/picovoice)) ※学習は有料
上記と比較してEfficientWord-Netの特徴は以下だと思う。
- Python製
- few-shot学習が可能
- 大量の学習データは不要
- 無料
実際に試してみる
環境
- macOS Monterey 12.6.3
- CPU Intel Core i7 2.9GHz Quad Core
インストール
EfficientWord-Netはpython-3.6〜3.9である必要がある。
Python Version Requirements
This Library works between python versions: 3.6 to 3.9
PyAudioが必要なライブラリをインストールしておく
$ brew install portaudio
仮想環境作成
$ mkdir EfficientWord-Net && cd EfficientWord-Net
$ pyenv install 3.9.16
$ pyenv local 3.9.16
$ pip install --upgrade pip
$ pyenv rehash
$ python -m venv env
$ . ./env/bin/activate
EfficientWord-Netインストール
$ pip install EfficientWord-Net
デモ
$ python -m eff_word_net.engine
初回は足りないパッケージをインストールしてくれる。
No module named 'tflite_runtime' installing the right version for your system now...
以下のようなメッセージが表示されればOK
All done! Carry on.
Say Mycroft / Alexa / Siri
適当に喋ってみる
Hotword: Alexa ,Confidence 0.9130
Hotword: mycroft ,Confidence 0.9137
Hotword: Siri ,Confidence 0.9083
Hotword: mycroft ,Confidence 0.9325
Hotword: Siri ,Confidence 0.9010
Hotword: mycroft ,Confidence 0.9175
Hotword: mycroft ,Confidence 0.9515
Hotword: mycroft ,Confidence 0.9192
Hotword: mycroft ,Confidence 0.9017
Hotword: Alexa ,Confidence 0.9443
Hotword: mycroft ,Confidence 0.9178
カスタムウェイクワード
EfficientWord-Netではカスタムなウェイクワードをいくつかの音声サンプルから生成することができる。
ちなみに最初はmac上でやってみたんだけど、どうやらモジュールの依存関係とかで上手く行かなかった。githubのissueを見た感じLinux/x86_64で生成して他の環境に移すのがベターみたい。
ということで、今回はVagrant上のUbuntu20.04LTSを使って生成する。
$ vagrant init ubuntu/focal64
$ vagrant up
$ vagrant ssh
Ubuntu20.04LTSのpythonは3.8.10のようなのでこのままで。python環境を用意する。
$ python3 --version
Python 3.8.10
$ sudo apt update
$ sudo apt install -y python3-pip python3.8-venv
$ python3 -m venv env
$ . env/bin/activate
EfficientWord-Netをインストール。EfficientWord-Netのnumpyのバージョン指定が厳しくて、学習に必要な他のパッケージと合わなくなるので、事前に入れておく。
$ sudo apt install -y portaudio19-dev libsndfile1-dev ffmpeg
$ pip install wheel
$ pip install "numpy==1.20.0" "librosa==0.9.2" tflite_runtime
$ pip install EfficientWord-Net
では学習をやっていく。手順は以下。
- 音声データを用意する。2つの方法がある。
- 自前で音声データを用意する。
- 用意されたスクリプトを実行する。
- カスタムウェイクワード情報を記載した
*_ref.json
ファイルを生成する
まず音声データ。2つの方法があるが、後者のほうは
- EfficientWord-Netが予め用意しているスクリプト
eff_word_net.ibm_generate
を使う - どうやらIBM Natural TTS Demo APIを使って音声データを生成するらしい。GitHubを見ると、あまり使いすぎないでほしい、と書いてあるので、作者のAPIキーを使っているのかもしれない。
- 実際に一度試してみたけど、
- 日本語には対応していない(スクリプトでは英語のTTS音声を使用するようになっていた。修正すればできるかもしれない)
- 英語でもなぜか音声ファイルが生成されなかった
ということで、今回は自前で用意することとする。Amazon Pollyを使って生成することにする。
$ WORD="タチコマ"
$ AUDIO_OUT="sample/audio"
$ mkdir -p $AUDIO_OUT
$ aws polly describe-voices | jq -r '.Voices[] | select(.LanguageName=="Japanese") | [.Id, .SupportedEngines[0]] | @tsv' | while read voice_id voice_engine; do aws polly synthesize-speech --text "$WORD" --engine "${voice_engine}" --voice-id "${voice_id}" --output-format mp3 --sample-rate 16000 ${AUDIO_OUT}/${voice_id}.mp3;done
{
"ContentType": "audio/mpeg",
"RequestCharacters": "4"
}
{
"ContentType": "audio/mpeg",
"RequestCharacters": "4"
}
{
"ContentType": "audio/mpeg",
"RequestCharacters": "4"
}
{
"ContentType": "audio/mpeg",
"RequestCharacters": "4"
}
これで4個、音声ファイルが作成される。
で、ドキュメントには学習用のサンプルは4〜10個と書いてあるけど、4個だとエラーになるのでもう1つ足しておく。今回はmacのsayコマンドを使った。
$ say -v "Kyoko" タチコマ -o tmp.aiff
$ ffmpeg -i tmp.aiff -ar 16000 Kyoko.mp3
$ rm tmp.aiff
こんな感じで日本語の音声で出力データを作成した。
$ ls sample/audio
Kazuha.mp3 Takumi.mp3 Kyoko.mp3
Mizuki.mp3 Tomoko.mp3
では学習させる。
$ python -m eff_word_net.generate_reference
INFO: Created TensorFlow Lite XNNPACK delegate for CPU.
音声データのパスとref.jsonの出力先、あとウェイクワード名を入力。日本語のディレクトリ名とかウェイクワード名はやめておいたほうが良さそう。
Paste Path of folder Containing audio files:./sample/audio
Paste Path of location to save *_ref.json :./sample
Enter Wakeword Name :Tachikoma
warningが出るけど。
/home/vagrant/env/lib/python3.8/site-packages/librosa/util/decorators.py:88: UserWarning: PySoundFile failed. Trying audioread instead.
return f(*args, **kwargs)
/home/vagrant/env/lib/python3.8/site-packages/librosa/util/decorators.py:88: UserWarning: PySoundFile failed. Trying audioread instead.
return f(*args, **kwargs)
/home/vagrant/env/lib/python3.8/site-packages/librosa/util/decorators.py:88: UserWarning: PySoundFile failed. Trying audioread instead.
return f(*args, **kwargs)
/home/vagrant/env/lib/python3.8/site-packages/librosa/util/decorators.py:88: UserWarning: PySoundFile failed. Trying audioread instead.
return f(*args, **kwargs)
/home/vagrant/env/lib/python3.8/site-packages/librosa/util/decorators.py:88: UserWarning: PySoundFile failed. Trying audioread instead.
一応出力された様子。
$ ls -lt sample/
total 16
-rw-rw-r-- 1 vagrant vagrant 11853 Mar 25 21:05 Tachikoma_ref.json
drwxr-xr-x 2 vagrant vagrant 4096 Mar 25 20:30 audio
埋め込みが行われていることがわかる。
$ cat sample/Tachikoma_ref.json | jq .
{
"embeddings": [
[
0.0018650750862434506,
0.0007926417165435851,
(snip)
-0.0014287818921729922,
0.0013382398756220937
],
[
0.0006884402246214449,
0.002232786500826478,
(snip)
-0.001346395118162036,
-2.9567121600848623e-05
],
[
0.001782684470526874,
0.0012871884973719716,
(snip)
-0.0012055747210979462,
0.0005732822464779019
],
[
0.0003983845526818186,
0.003195548430085182,
(snip)
-0.0007530294824391603,
-0.0015159585746005177
],
[
0.0016636208165436983,
0.001577697228640318,
(snip)
-0.0002467365702614188,
0.0010160977253690362
]
]
}
ではこれをrefs.jsonをmacに移してウェイクワードとして認識できるか試してみる。判別しやすいように予め用意されているウェイクワードとカスタムで作成したウェイクワードを両方とも認識するようにしてある。
import os
from eff_word_net.streams import SimpleMicStream
from eff_word_net.engine import HotwordDetector, MultiHotwordDetector
from eff_word_net import samples_loc
alexa_hw = HotwordDetector(
hotword="Alexa",
reference_file = os.path.join(samples_loc,"alexa_ref.json"),
)
siri_hw = HotwordDetector(
hotword="Siri",
reference_file = os.path.join(samples_loc,"siri_ref.json"),
)
mycroft_hw = HotwordDetector(
hotword="mycroft",
reference_file = os.path.join(samples_loc,"mycroft_ref.json"),
)
tachikoma_hw = HotwordDetector(
hotword="Tachikoma",
reference_file = "/Users/kun432/repository/EfficientWord-Net/tachikoma_ref.json",
threshold=0.9, #min confidence required to consider a trigger
relaxation_time = 0.8 #default value ,in seconds
)
multi_hw_engine = MultiHotwordDetector(
detector_collection = [
alexa_hw,
siri_hw,
mycroft_hw,
tachikoma_hw
],
)
mic_stream = SimpleMicStream()
mic_stream.start_stream()
print("Say Mycroft / Alexa / Siri / Tachikoma")
while True :
frame = mic_stream.getFrame()
result = multi_hw_engine.findBestMatch(frame)
if(None not in result):
print(result[0],f",Confidence {result[1]:0.4f}")
実行して確認してみる。
$ python custom_wake.py
Say Mycroft / Alexa / Siri / Tachikoma
Hotword: Siri ,Confidence 0.9326
Hotword: mycroft ,Confidence 0.9146
Hotword: mycroft ,Confidence 0.9540
Hotword: mycroft ,Confidence 0.9258
Hotword: Alexa ,Confidence 0.9665
Hotword: Tachikoma ,Confidence 0.9063
Hotword: Tachikoma ,Confidence 0.9022
ちゃんと認識された。
まとめ
- 今回は全て合成音声で作成したけど精度としてはそこそこ。実際の自分の音声データも入れて学習させればもうちょっとあげれそう。
- パッケージの依存関係キツイので、学習環境と実行環境は分けたほうが良さそう。基本的にオーディオデータをいじるのはいろいろ環境に依存しそうと個人的には思ってる。
- RPiあたりでやっぱり動かしてみたくなりますね!
こういうのもあった