Closed5

EfficientWord-Netでウェイクワード検出

kun432kun432

EfficientWord-Netとは

https://medium.com/ant-brain/efficientword-net-an-open-source-hotword-detector-50058d68149f

https://github.com/Ant-Brain/EfficientWord-Net

いくつかあるウェイクワード検出の一つ。全部見たわけではないけど、多分こんな感じ。

上記と比較してEfficientWord-Netの特徴は以下だと思う。

  • Python製
  • few-shot学習が可能
    • 大量の学習データは不要
    • 無料

実際に試してみる

kun432kun432

環境

  • 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

https://ant-brain.github.io/EfficientWord-Net/

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
kun432kun432

カスタムウェイクワード

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

では学習をやっていく。手順は以下。

  1. 音声データを用意する。2つの方法がある。
  • 自前で音声データを用意する。
  • 用意されたスクリプトを実行する。
  1. カスタムウェイクワード情報を記載した *_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に移してウェイクワードとして認識できるか試してみる。判別しやすいように予め用意されているウェイクワードとカスタムで作成したウェイクワードを両方とも認識するようにしてある。

custom_wake.py
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

ちゃんと認識された。

kun432kun432

まとめ

  • 今回は全て合成音声で作成したけど精度としてはそこそこ。実際の自分の音声データも入れて学習させればもうちょっとあげれそう。
  • パッケージの依存関係キツイので、学習環境と実行環境は分けたほうが良さそう。基本的にオーディオデータをいじるのはいろいろ環境に依存しそうと個人的には思ってる。
  • RPiあたりでやっぱり動かしてみたくなりますね!
このスクラップは2023/03/26にクローズされました