RaSCとJuliusを用いた音声認識サーバーの構築

2014-04-14

約1ヶ月前に開催された言語処理学会にて、RaSC (Rapid Service Connector)というツールがNICTから発表されました。

RaSCは,既存の形態素解析器や係り受け解析器などのプログラムを大量のWebページに高速に適用することを念頭に開発されたものであり,多種のユーザプログラムを複数起動し,それらを相互に接続して分散並列実行させるためのミドルウェアです.

RaSC上で稼働するユーザプログラムのプロセスは,一度起動されると計算機に常駐します.そのため,辞書ファイルをロードする言語処理プログラムのように,巨大ファイルのロードなどにより起動時間が長くなっているプログラムであっても,効率的に実行できます.また,ユーザプログラムはネットワークを介して容易に利用でき,複数件の入力があった際には,複数の計算機上で並列化・分散実行することによって高速化されます.ユーザプログラム同士は,UNIXのパイプのように,簡便にストリーム通信により接続できる他,ユーザプログラムの実行はユーザが意識することなく並列に行われます

このツールのすごい点、それは_非常に簡単な手順で、プロセスを常駐化させ、ネットワーク経由でアクセスできること_ではないでしょうか。

今回はRaSCを用いてJuliusを常駐化させてみましょう。

補足

Juliusに関しては、とあるプロジェクトでWebUIと連携を行う際に少々触れた程度の知識しか持っておりませんので(勉強不足です)、以下のJuliusに関する内容は誤りがあるかもしれません。

間違い等ありましたら、遠慮なくコメント欄でご指摘いただければ幸いです。

RaSCを使うメリット

そもそもなぜRaSCを用いて常駐化を行う必要があるのでしょうか。

それは、RaSCページの引用文にも示されている通り、_辞書ファイル等の読み込みが発生するプログラムを毎回起動させるコストを減らすことができる_からです。

また今回サービス化を行うJuliusには、ネットワーク・ソケット経由で解析が可能になるモードが用意されておりますが、Julius独自のプロトコルの_実装コスト_や_並列分散化_(Juliusは1対1の通信のみを想定しているため、複数のリクエストがあった時の処理が困難)を考えると、**RaSCのメリットは非常に大きい**のではないでしょうか。

他にもMeCabやCaboChaなど、起動時にモデルを読み込み、起動後は標準入力を受け取り結果を返すようなインタラクティブな動作(?)をすることができるツールには非常に応用が利きそうですね。

以下にRaSCサービス化可能なプログラムの要件を引用しておきます。

  • 標準入出力を介して入出力を行う
  • 1件の入力が与えられると,対応する結果を出力し,終了することなく次の入力を待つ.
  • 入力と出力に明示的な終端文字列を出力する

RaSCを試してみる

RaSCドキュメントには、非常に丁寧なチュートリアルが掲載されています。

以下のページのチュートリアルに従うことで、たった数分でMeCabをサービス化することができます。

MessagePack RPCでユーザプログラムを呼び出す — RaSC 1.0.1 documentation

Juliusのサービス化を行う

Juliusの準備

では、本題に入ります。

まずJuliusは上で述べた”RaSCサービス化可能なプログラムの要件”を満たしておりません。

Juliusの実行結果を見ればわかる通り、解析結果の最後にEOF等の区切り文字が出力されないのです。

今回は、まずJuliusにパッチを当てていきます。

Juliusの準備
1
2
3
4
5
$ # http://sourceforge.jp/projects/julius/downloads/60273/julius-4.3.1.tar.gz/ から、最新のソースコードを入手する
$ tar zxvf julius-4.3.1.tar.gz
$ cd julius-4.3.1 # 展開後のフォルダへ移動
$ wget "http://downloads.bonprosoft.com/file/patch/patch_rasc_julius.diff" # Juliusのパッチをダウンロード
$ patch -p0 < patch_rasc_julius.diff # パッチを当てる

以上でJuliusへパッチを当てることができました。

あとはこのソースコードのビルドを行いましょう。

1
2
3
$ ./configure # 必要に応じて prefix等のオプションをつけてください
$ make

以上で実行可能なバイナリが生成されます。

では試しに音声ファイルを渡して解析を行ってみましょう。

Julius実行結果例
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$ ./julius -C settings/am-gmm.jconf -C settings/main.jconf -input rawfile
STAT: include config: settings/am-gmm.jconf
STAT: include config: settings/main.jconf
STAT: jconf successfully finalized
STAT: *** loading AM00 _default
## 中略 ##
Notice for feature extraction (01),
        *************************************************************
        * Cepstral mean normalization for batch decoding:           *
        * per-utterance mean will be computed and applied.          *
        *************************************************************

enter filename->temp/sound.wav # インタラクティブモードで起動したので、試しにファイルを解析する
Stat: adin_file: input speechfile: temp/sound.wav
STAT: 72192 samples (4.51 sec.)
STAT: ### speech analysis (waveform -> MFCC)
pass1_best:    これ テスト す
sentence1:  これ は テスト です
sentence2:  、 これ は テスト です 。
sentence3:  と これ は テスト です
EOS

enter filename->

21行目に注目してください。

このように、EOSが出力されていれば成功です。

RaSCの導入

ではいよいよRaSCの導入を行います。

_RaSCの実行には JDK7以上 が必要_ですので、まずは導入されているかどうかを確認し、導入されていない場合には先にそちらを導入してください。

1
2
3
4
5
$ wget "https://alaginrc.nict.go.jp/rasc/resources/rasc-core-1.0.1.zip" # RaSCコアパッケージの取得
$ unzip rasc-core-1.0.1.zip # ダウンロードしたファイルの解凍
$ mv WEB-INF/serviceimpl/SampleService.xml WEB-INF/serviceimpl/JuliusService.xml
# WEB-INF/serviceimpl 以下に置かれたサービス定義XMLのファイル名(拡張子を除いた部分)がサービス名になります
# この場合、JuliusServiceがサービス名となります

また、JuliusService.xmlを開いて、 **_\_PATH_TO_PROGRAM__**となっている箇所に、実行したユーザープログラムのコマンドラインを設定します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
  <bean id="target"
    class="jp.go.nict.langrid.servicecontainer.handler.TargetServiceFactory">
    <property name="service">
      <bean class="jp.go.nict.rasc.wrapper.StandardInputService">
        <property name="cmdLine" value="___PATH_TO_PROGRAM___" />
        <property name="delimiterIn" value="\n" />
        <property name="delimiterOut" value="EOS\n" />
      </bean>
    </property>
  </bean>
</beans>

例えば、バイナリが /home/user/binary にある場合、以下のように設定できます。

1
<property name="cmdLine" value="/home/user/binary" />

今回の場合は、設定ファイル等のパラメータを渡す必要があるため、_cmdArray_パラメータを使います。

先ほどの行を削除し、例として以下のように設定することができます。

1
2
3
4
5
6
7
8
9
<property name="cmdArray">
    <list>
        <value>/home/user/julius</value>
        <value>-C</value>
        <value>/home/user/settings/am-gmm.jconf</value>
        <value>-C</value>
        <value>/home/user/settings/main.jconf</value>
    </list>
</property>

ファイル全体の設定例は以下のようになります。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
    <bean id="target"
        class="jp.go.nict.langrid.servicecontainer.handler.TargetServiceFactory">
        <property name="service">
            <bean class="jp.go.nict.wisdom.wrapper.StandardInputService">
                <property name="cmdArray">
                    <list>
                        <value>/home/user/julius</value>
                        <value>-C</value>
                        <value>/home/user/settings/am-gmm.jconf</value>
                        <value>-C</value>
                        <value>/home/user/settings/main.jconf</value>
                    </list>
                </property>
                <property name="delimiterIn" value="\n" />
                <property name="delimiterOut" value="EOS\n" />
            </bean>
        </property>
    </bean>
</beans>

他にも様々なパラメータが用意されております。

詳しくは以下を参照してください。

サービス定義XML — RaSC 1.0.1 documentation

では、RaSCサービスを起動させてみましょう。

server.shの使い方は以下の通りです。

./server.sh [サービス名] [ポート番号] {start|stop}

今回の場合は、例として以下のように起動させることができます。

1
2
$ ./server.sh JuliusService 1999 start
JuliusService started.

Pythonから呼び出してみる

RaSCサービスを呼び出すのは非常に簡単です。ここでは例としてPythonを利用していきます。

なお、プログラムの実行には msgpack-rpc-python パッケージが必要ですので、easy_install 等で導入をしておきましょう。

1
2
3
4
5
6
7
#!/usr/bin/env python
# -*- coding: UTF-8 -*-

import msgpackrpc
cli = msgpackrpc.Client(msgpackrpc.Address("localhost", 1999))
result = cli.call("analyze", "temp/sound.wav")
print result

これで、temp/sound.wav を解析した結果が表示されます。

あとは、PythonでWebサーバーを構築して、リクエストで受け取った音声データを必要に応じてsoxコマンド等で加工、RaSCサービス化したJuliusを呼び出せばよいでしょう。

(Juliusには -input stdin オプションを指定することで、標準入力から音声データを受け取ることが可能ですが、この場合1回の解析でJuliusが終了してしまったため、今回ファイル指定という形をとりました。

標準入力でもインタラクティブモードが可能であれば、そちらのほうが良いでしょう。その場合、cli.call(“analyze”,[音声データ])とすることで、音声データが送信されます。

最後に

いかがでしたか?

今回RaSCを使用することで、非常に簡単な手順でプロセスのサービス化に対応することができました。

今回は並列化に関しては触れませんでしたが、RaSCのページにはその点に関しても非常に親切なチュートリアルが掲載されておりますので、一度目を通してみることをおすすめします。

RaSCは非常に汎用性の高いツールですので、自然言語処理だけでなく、様々な場面で活用していけそうです。

参考ページ

このエントリーをはてなブックマークに追加
« MVP Community Camp 2014でLTしました! プログラミング生放送第28回仙台に参加します! #pronama »