Juliusで独自辞書を使った音声認識を行う

【実験】PaPeRo i本体でJuliusによる音声認識を行うの方法では、chroot環境のためUSBメモリが必要な上、認識時間10秒程度と実用には耐えないものでしたが、

  • chroot/debianは使わず、PaPeRo iのOpenWRTでそのまま動くjulius → USBメモリ不要
  • 認識語数を絞った独自辞書 → 認識時間短縮

を試しました。結果はPaPeRo i本体の音声認識と同程度の認識時間となり、さらにこの方法なら認識語を追加できるアプリを作る事ができます。

OpenWRTでそのまま動くJulius

julius-4.4.2.1をPaPeRo i用にビルドした物を用意しましたのでこちらからダウンロードしてください。ダウンロードファイルは以下の様なディレクトリ構成になっています。

+ julius
  + bin   : juliusコマンド等
  + lib   : 実行時必要なライブラリ

動かすには環境変数を適切に設定する必要がありますが、

# cd /Extension/local
# tar xvzf /tmp/julius.tar.gz

と展開した(juliusコマンドのパスが/Extension/local/julius/bin/juliusとなる)場合には、

# export PATH=/Extension/local/julius/bin:$PATH
# export LD_LIBRARY_PATH=/Extension/local/julius/lib:$LD_LIBRARY_PATH

とした上で

# julius ..(オプション)..

として実行してください。

Julius音声認識パッケージ

 今回もディクテーションキット version 4.4を使用します。http://julius.osdn.jp/index.php?q=dictation-kit.htmlからダウンロードできます。サイズが大きいためPCでUSBメモリに展開してパペロに挿し、独自辞書指定でgmmを実行するのに必要なファイルだけをパペロの内蔵フラッシュ/Extension以下にコピーします。以下はWindowsでFAT32フォーマットのUSBメモリのルートに展開した場合の手順です(FAT32なら自動で/usr/local/usbdiskにマウントされます)。chroot環境で試したext4のUSBメモリがある場合などは適宜読み替えてコピーしてください。コピー先は/Extension/local/julius/dictation-kit-v4.4gmmとしています。

# mkdir /Extension/local/julius/dictation-kit-v4.4gmm
# cd /Extension/local/julius/dictation-kit-v4.4gmm
# cp -a /usr/local/usbdisk/dictation-kit-v4.4/*.txt .
# cp -a /usr/local/usbdisk/dictation-kit-v4.4/am-gmm.jconf .
# mkdir model
# cp -a /usr/local/usbdisk/dictation-kit-v4.4/model/phone_m ./model

なお必要容量は53MByte程度でした。

独自辞書の作成

PaPeRo i上でJuliusの独自辞書を作成します。但しPaPeRo iの端末上で日本語入力するのは難しいので、辞書の元になるテキストはPC等で用意します。
 Juliusの独自辞書作成方法そのものについては公式ページの説明はなかなか難しく、まとめてくださっている方の情報(https://qiita.com/satosystems/items/4d379e57d2370177ead1など)を参考にさせて頂きました。

準備1 Perlのインストール

 独自辞書の作成にはPerlが必要です。少々古いバージョンですが、opkgでインストールするには、PaPeRo iをインターネット接続可能な状態にした上で、

# opkg update
# opkg install perl

としてください。このままだとライブラリの位置が標準と異なるため動作しないので、以下の環境変数を設定してください。

# export PERL5LIB=/Extension/pkgroot/usr/lib/perl5/site_perl/5.22.1:/Extension/pkgroot/usr/lib/perl5/5.22.1

準備2 piconvの修正

 Juliusの出力はUTF-8にするのが扱いやすいのですが、その場合Juliusの仕様では辞書ファイルもUTF-8にする必要があります。ところがJuliusの辞書作成ツール(linuxで動作する場合)はEUC前提となっているため、入力でUTF-8→EUC、出力でEUC→UTF-8とする漢字コード変換が必要になります。PaPeRo iには標準では漢字コード変換ツールはないのですが、Perlをインストールすると一緒にpiconvという漢字コード変換コマンドがインストールされます(/Extension/pkgroot/usr/bin/piconv)。しかしopkgでインストールしたままの状態ではpiconvのshebangが

#!/usr/bin/perl

となっており、インストール先をデフォルトから変更されていることが想定されていません。そこでこれは以下の様に直接書き換えてしまいます。

#!/usr/bin/env perl

これでpiconvコマンドで漢字コードを変換できる様になります。

音素列への変換と単語の定義

 ここでは例として

お知らせ教えて
予定教えて

を認識出来るようにする事を考えます。
 まず共通している「教えて」を分離し、認識語と、ひらがなで表記した読みをTABで区切ったリストを用意します(oshiete.yomi)。漢字コードはUTF-8としてください。以下全てのファイルは/Extension/local/julius/mydictに置いてください。

お知らせ    おしらせ
予定        よてい
教えて      おしえて

このひらがなの読みを、以下のコマンドで音素列に変換します。

# cd /Extension/local/julius/mydict
# piconv -f utf8 -t eucjp oshiete.yomi | perl /Extension/local/julius/bin/yomi2voca.pl | piconv -f eucjp -t utf8 > oshiete.phone

これは以下の様になります。

お知らせ    o sh i r a s e
予定        y o t e i
教えて      o sh i e t e

このファイルをコピー、編集して単語の定義とします(oshiete.voca)。

% OSHIRASE
お知らせ    o sh i r a s e
% YOTEI
予定        y o t e i
% OSHIETE
教えて      o sh i e t e
% NS_B
[s]         silB
% NS_E
[/s]        silE

ここでは一つの単語カテゴリ(% OSHIRASE等)に対し一つの単語しか定義していませんが、複数定義することも可能です。silB、silEはそれぞれ開始、終了を表します。

文法の定義

文法を以下の様に定義します(oshiete.grammar)。

S : NS_B ASK NS_E
ASK : OSHIRASE OSHIETE
ASK : YOTEI OSHIETE

Sは文章、NS_Bが開始、NS_Eが終了を表します。ここでは文章として「お知らせ」/「予定」と、分離した単語「教えて」をそれぞれ続けて定義しています。なおSを複数定義することも可能です。

単語辞書の生成

 以下のコマンドでjuliusが利用できる辞書を生成します。

# cd /Extension/local/julius
# perl /Extension/local/julius/bin/mkdfa.pl mydict/oshiete

成功の場合、以下の様に表示されます。

mydict/oshiete.grammar has 3 rules
mydict/oshiete.voca    has 5 categories and 5 words
---
Now parsing grammar file
Now modifying grammar to minimize states[0]
Now parsing vocabulary file
Now making nondeterministic finite automaton[5/5]
Now making deterministic finite automaton[5/5]
Now making triplet list[5/5]
5 categories, 5 nodes, 5 arcs
-> minimized: 5 nodes, 5 arcs
---
generated: mydict/oshiete.dfa mydict/oshiete.term mydict/oshiete.dict

ひとまず動かしてみる場合、以下の様にします。

# cd /Extension/local/julius
# LD_LIBRARY_PATH=/Extension/local/julius/lib:$LD_LIBRARY_PATH bin/julius -C dictation-kit-v4.4gmm/am-gmm.jconf -nostrip -gram mydict/oshiete

この例の場合、2語しかないためか、認識率、認識時間とも良好でした。色々試したところ、認識語にも依存するとは思いますが、また誤認識の場合の工夫は必要ですが、少なくとも100語程度までであれば実用的に使えそうに思われました。

アプリから利用する

 アプリから利用する場合、juliusのモジュールモードを使用すると簡単にできます。

# cd /Extension/local/julius
# LD_LIBRARY_PATH=/Extension/local/julius/lib:$LD_LIBRARY_PATH bin/julius -C dictation-kit-v4.4gmm/am-gmm.jconf -nostrip -gram mydict/oshiete -module

juliusをPaPeRo i本体で動かす場合でもRaspberry PiのJuliusによる認識結果を、PaPeRo i にしゃべらせるのjulius_sample.pyが動作します。但し、print()で日本語文字列を表示すると例外になってしまうため

print("Recognized: " + sentence)

の行はコメントアウトしてください。また認識語の両端の”[s]”、”[/s]”を削除する必要があります。起動時には以下の様にlocalhostを指定します。PaPeRo iのマイクを直接使用するのでadinnetの指定、adintoolの起動は不要です。

# python3 julius_sample.py localhost

0