日記とか、工作記録とか

自分に書けることを何でも書いてゆきます。作った物、買ったもの、コンピュータ系の話題が多くなるかもしれません。

Raspberry pi 3 Model B + Juliusで音声認識

まえおき

ここしばらく、AIアシスタント付きのPCやスマホやスピーカーが汎用品になってきました。

私もiPhoneのSiri、Surface ProのCortana、Echo dotのAlexaとちょっとずつ使ってみましたけど、まだ少しだけ新しいことができるという段階で、生活が便利になるにはまだまだ発展が必要かなぁという感じですね。ただ、短期間のうちにSiriもAlexaもできることが増えたり、こちらの言うことに正しく応答してくれるようになってきました。この先が楽しみです。

で、私もマネをしてみたくなったので、多くの人の後追いではありますが、音声認識を試してみることにしました。おいおいやっていこうとしている遠大な(?)計画はあるのですが、ひとまずは私の言うことを聞き取るところを目指そうと思います。今回は日本語音声を認識して文字にしてくれるJuliusを試してみます。

準備

いつものRaspberry pi 3 Model Bです。今回、32GBのmicroSDカードを使ってRaspbian OSのインストールからやり直しました。

f:id:WindVoice:20180826151438j:plain

ところで、スイッチサイエンスさんの調査で、pi 3は快適に使うには5V / 2.5Aの電源が必要とあるのですが、これがなかなか難しい条件で、私の部屋で見つけられるのは、iPad用の充電アダプタ(2.4A)が精一杯でした。確かに、言われてみれば/var/log/messagesにVoltage関係のメッセージがでていたりするので、これまでは電源不足から制限がかかった状態で動いていたのだなぁと認識した次第です。iPadの電源でもまだVoltageエラーは出るのですが、以前よりは明らかに快速になったので、しばらくはこれで行こうかと思います。

Juliusのダウンロード

Juliusは、最新版がGitHubに置かれていますので、git cloneで取り寄せます。現在の最新版は4.4.1です。

pi@raspberrypi:~ $ git clone https://github.com/julius-speech/julius.git
Cloning into 'julius'...
remote: Counting objects: 2341, done.
remote: Total 2341 (delta 0), reused 0 (delta 0), pack-reused 2341
Receiving objects: 100% (2341/2341), 8.54 MiB | 3.21 MiB/s, done.
Resolving deltas: 100% (1059/1059), done.
pi@raspberrypi:~ $

実はうまく動作させることができず、いろいろ試行錯誤したのですが、結果的には下の要領でconfigureしてうまくいきました。--with-mictypeというのは、音声入力をOSのどの仕組みから受け取るか、を選択するものですが、最初OSS(Open Sound System)経由で受け取ることになっていました。しかし、これは動作させることはできませんでした。

ALSA(Advanced Linux Sound Architecture)のほうが新しい仕組みなのだそうで、こちらを使うと動作しました。パッケージも試行錯誤の途中でいろいろ入れたので、どれが重要だったかわからないのですが、ALSA関係は必要だったはずです。

他に使えるconfigureオプションは、ここにリストがあります。

pi@raspberrypi:~ $ sudo apt-get install alsa-utils
pi@raspberrypi:~ $ sudo apt-get install sox
pi@raspberrypi:~ $ sudo apt-get install libsox-fmt-all
pi@raspberrypi:~ $ sudo apt-get install osspd-alsa
pi@raspberrypi:~ $ sudo apt-get install libasound2-dev libesd0-dev libsndfile1-dev
:
:
:
pi@raspberrypi:~/julius $ ./configure --enable-words-int --with-mictype=alsa

ちょっとでも速く動いてほしいので、コンパイル最適化オプションをつけてmakeします。

pi@raspberrypi:~/julius $ export CFLAGS="-O3"
pi@raspberrypi:~/julius $ make
pi@raspberrypi:~/julius $ make install
:
:
:
pi@raspberrypi:~/julius $ julius -setting
JuliusLib rev.4.4.2.1 (fast)

Engine specification:
-  Base setup   : fast
-  Supported LM : DFA, N-gram, Word
-  Extension    : WordsInt LibSndFile
-  Compiled by  : gcc -O3

Library configuration: version 4.4.2.1
- Audio input
    primary A/D-in driver   : alsa (Advanced Linux Sound Architecture)
    available drivers       : alsa
    wavefile formats        : various formats by libsndfile ver.1
    max. length of an input : 320000 samples, 150 words
- Language Model
    class N-gram support    : yes
    MBR weight support      : yes
    word id unit            : integer (4 bytes)
- Acoustic Model
    multi-path treatment    : autodetect
- External library
    file decompression by   : zlib library
- Process hangling
    fork on adinnet input   : no
- built-in SIMD instruction set for DNN

    NONE AVAILABLE, DNN computation may be too slow!

Try `-help' for more information.
pi@raspberrypi:~/julius $

USBマイクの優先順位を上げる

Raspberry pi 3にはマイク端子は付属していないので、USBマイクを使います。こちらも手元にあるものを使うということで、こちらを引っ張り出してきました(ゲーム用ですが)。マイクとスピーカーが一体になっているので、USBポートが一個で間に合うのはちょっと便利かな。

接続したら、lsusbで認識していることを確認します。1行目ですね。

pi@raspberrypi:~ $ lsusb
Bus 001 Device 007: ID 046d:0a6d Logitech, Inc.
Bus 001 Device 003: ID 0424:ec00 Standard Microsystems Corp. SMSC9512/9514 Fast Ethernet Adapter
Bus 001 Device 002: ID 0424:9514 Standard Microsystems Corp. SMC9514 Hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
pi@raspberrypi:~ $

音声関係のデバイスが複数ある場合、どれに優先してデータを流すかを決める必要があります。下のコマンドでデバイスの優先度がわかるのですが、このままだとUSBポートに接続したヘッドフォンの優先度が低いので、これを最上位(ゼロ)に持ってきます。alsa-base.confファイルを作成して、下の通り記載して保存したらRaspberry piを再起動です。options ... index=0が最上位のデバイスになります。

pi@raspberrypi:~ $ sudo cat /proc/asound/modules
 0 snd_bcm2835
 1 snd_usb_audio
pi@raspberrypi:~ $ sudo vi /etc/modprobe.d/alsa-base.conf
options snd slots=snd_usb_audio,snd_bcm2835
options snd_usb_audio index=0
options snd_bcm2835 index=1
pi@raspberrypi:~ $
:
(再起動)
:
pi@raspberrypi:~ $ sudo cat /proc/asound/modules
 0 snd_usb_audio
 1 snd_bcm2835
pi@raspberrypi:~ $

録音テスト

他の方のJuliusの利用例では、arecordコマンドを使っていることが多いのですが、Juliusには同梱の録音ツールadinrecがあります。トラブルシューティング的には、こちらを使ったほうが良いかと思います。arecord -lで録音可能なデバイス(=マイク)の一覧を取得すると、カード0、デバイス0でヘッドフォンが認識されていることがわかります。環境変数ALSADEVに、"plughw:0,0"を設定してからadinrecを起動すれば、ファイルに録音することができます。

pi@raspberrypi:~/julius $ arecord -l
**** ハードウェアデバイス CAPTURE のリスト ****
カード 0: Headset [G433 Gaming Headset], デバイス 0: USB Audio [USB Audio]
  サブデバイス: 1/1
  サブデバイス #0: subdevice #0
pi@raspberrypi:~/julius $
pi@raspberrypi:~ $ export ALSADEV="plughw:0,0"
pi@raspberrypi:~ $ adinrec -nostrip /tmp/test.wav
STAT: ###### initialize input device
Stat: adin_alsa: device name from ALSADEV: "plughw:0,0"
Stat: capture audio at 16000Hz
Stat: adin_alsa: latency set to 32 msec (chunk = 512 bytes)
Stat: "plughw:0,0": Headset [G433 Gaming Headset] device USB Audio [USB Audio] subdevice #0
STAT: AD-in thread created
.............Error: adin_alsa: error in snd_pcm_wait() (Input/output error)
Error: adin thread exit with error

14528 samples (29056 bytes, 0.91 sec.) recorded
pi@raspberrypi:~ $
pi@raspberrypi:~/julius $ aplay -D plughw:0,0 /tmp/test.wav
再生中 WAVE '/tmp/test.wav' : Signed 16 bit Little Endian, レート 16000 Hz, モノラル
pi@raspberrypi:~/julius $

実は、動作しているようなのに録音されない…… という問題にかなり悩みました。マイクに近づいて、ハキハキ喋ると発生を認識してくれます。ある程度音圧がないと、認識が始まらないようです。できあがる録音ファイルは、無音部分がなくなって「発声」として認識したところだけが保存されています。aplayコマンドを上のように使うと、再生することができます。

ディクテーションキットの入手

日本語音声をテキストデータに変換するには、日本語の音の特徴がどういうものか、というデータが必要になります。例えば「あ」という声が、どのような音でできているのか、あらかじめ知っている必要があるわけです。Juliusの場合、日本語用のキットが用意されているので、これを活用させてもらいます(便利!)。

pi@raspberrypi:~/julius-kit $ wget --trust-server-names 'https://osdn.net/frs/redir.php?m=iij&f=julius%2F66544%2Fdictation-kit-v4.4.zip'
--2018-08-24 22:16:28--  https://osdn.net/frs/redir.php?m=iij&f=julius%2F66544%2Fdictation-kit-v4.4.zip
osdn.net (osdn.net) をDNSに問いあわせています... 202.221.179.17
osdn.net (osdn.net)|202.221.179.17|:443 に接続しています... 接続しました。
HTTP による接続要求を送信しました、応答を待っています... 302 Found
場所: http://iij.dl.osdn.jp/julius/66544/dictation-kit-v4.4.zip [続く]
--2018-08-24 22:16:28--  http://iij.dl.osdn.jp/julius/66544/dictation-kit-v4.4.zip
iij.dl.osdn.jp (iij.dl.osdn.jp) をDNSに問いあわせています... 202.232.140.70, 2001:240:bb8f::1:70
iij.dl.osdn.jp (iij.dl.osdn.jp)|202.232.140.70|:80 に接続しています... 接続しました。
HTTP による接続要求を送信しました、応答を待っています... 200 OK
長さ: 426837341 (407M) [application/zip]
`dictation-kit-v4.4.zip' に保存中

dictation-kit-v4.4.zip        100%[==============================================>] 407.06M  7.56MB/s    in 44s     

2018-08-24 22:17:12 (9.26 MB/s) - `dictation-kit-v4.4.zip' へ保存完了 [426837341/426837341]

pi@raspberrypi:~/julius-kit $

これをunzipコマンドで展開して、ディレクトリ内にあるmain.jconfと、am-gmm.jconfファイルに一般的な設定が作られていますので、これをJuliusコマンドで読み込み、あとは適宜オプションを追加して起動する、ということになります。ちなみに、うまくいかないときのトラブルシューティング用資料がこのディレクトリのTROUBLE.txtにあるので、参考にしました(SJISで書かれたテキストファイルなので、読むときはiconv -f sjis -t utf8 TROUBLE.txtで読みます)。

起動後は、マイクに向かってハキハキ喋ります。-recordオプションをつけておくと、認識した音声を細切れのファイルに保存しておいてくれます。発音に所定の無音時間があると別の発言と認識されるので、一続きに読み上げるように話す必要があります。

pi@raspberrypi:~ $ unzip dictation-kit-v4.4.zip
pi@raspberrypi:~ $ cd dictation-kit-v4.4
pi@raspberrypi:~/dictation-kit-v4.4 $ julius -C main.jconf -C am-gmm.jconf -input mic -nostrip -record /tmp
:
:
:
(だーっと長い出力があって……)
:
:
:
------
### read waveform input
Stat: adin_alsa: device name from ALSADEV: "plughw:0,0"
Stat: capture audio at 16000Hz
Stat: adin_alsa: latency set to 32 msec (chunk = 512 bytes)
Stat: "plughw:0,0": Headset [G433 Gaming Headset] device USB Audio [USB Audio] subdevice #0

recorded to "/tmp/2018.0826.112825.wav" (74112 bytes, 2.32 sec.)
pass1_best:  こんにちは 今日 は い て です 。
pass1_best_wordseq: <s> こんにちは+感動詞 今日+名詞 は+助詞 い+動詞 て+助詞 です+助動詞 </s>
pass1_best_phonemeseq: silB | k o N n i ch i w a | ky o: | w a | i | t e | d e s u | silE
pass1_best_score: -6249.713867
### Recognition: 2nd pass (RL heuristic best-first)
STAT: 00 _default: 42918 generated, 2459 pushed, 535 nodes popped in 230
sentence1:  こんにちは 今日 は いい 天気 です 。
wseq1: <s> こんにちは+感動詞 今日+名詞 は+助詞 いい+形容詞 天気+名詞 です+助動詞 </s>
phseq1: silB | k o N n i ch i w a | ky o: | w a | i: | t e N k i | d e s u | silE
cmscore1: 1.000 0.819 0.412 0.255 0.294 0.277 0.764 1.000
score1: -6235.160645

<<< please speak >>>^C
pi@raspberrypi:~/dictation-kit-v4.4 $

トラブルシューティングとか

GitHubからダウンロードされるファイルは、ところどころ日本語で書かれたものがあるのですが、日本語部分のキャラクタコードはSJISなので、Raspberry pi(UTF-8)では文字化けして読めません。iconvコマンドは、覚えておく必要があると思います。こんな感じですね。

pi@raspberrypi:~/julius $ iconv -f sjis -t utf8 Release-ja.txt | less
4.4.2.1 (2016.12.20)
====================
- AndroidとiOS用の細かい修正
- msvcディレクトリの整理

4.4.2 (2016.09.12)
===================
- dnnconf 内の相対パスをそのdnnconf自身からの相対パスとして扱うよう修正
- DNN使用時、認識処理の第2パスが異常に遅いことがある不具合を修正
- AVX非対応のCPUでDNN計算が動作しない不具合を修正
- ARMとVisualStudioでのビルドを改善

4.4.1 (2016.09.07)
===================
- DNNのSIMD計算の高速化と安定化:SSE命令、FMA命令、ARMのNEON命令を実装
- DNN使用時にCPUを自動チェックして最適なSIMDコードを選択使用するよう拡張
- VisualStudio2013でのコンパイル用に PortAudio と zlib のソースを同梱
- mkbinhmmlistで作ったバイナリHMMListが正しく読めないことがあるバグを修正
- adintool-gui コンパイル時のSDL周りが動かないのを修正
- "INSTALL.txt" に各OSでのビルドのセットアップと実行方法を記述
- pkg-config に対応
- その他バグの修正

juliusコマンドの出力の日本語は、何もしなくてもUTF-8で出力されます。これはおそらくLANG環境変数が設定されているからだと思います。

今後

Juliusの日本語の認識能力は「特定の発言がある」と分かっている場合は、文法ファイルをあらかじめ作っておくことで精度をあげることができるようです。「アレクサ」と言うことがあるとわかっていれば、準備をしておくことで認識しやすくなるわけですね。命令でいろいろRaspberry piを動かすのが目標ですので、文法ファイルの作り方も、おいおい試してみようと思います。