y_uti のブログ

統計、機械学習、自然言語処理などに興味を持つエンジニアの技術ブログです

統計的機械翻訳システム Moses で遊ぶ

統計的機械翻訳システム Moses を使って、英語から日本語への自動翻訳を試してみます。Moses は、機械翻訳の分野で広く利用されているシステムです。対訳コーパス*1からモデルを学習し、そのモデルを用いて入力文の翻訳結果を出力します。Moses のウェブサイトは以下にあります。
Moses - Main/HomePage

Moses のインストール

Moses のインストール手順は公式サイトの説明どおりです。Git リポジトリからソースコードを取得してビルドします。公式サイトでは bjam にオプション -j8 を指定していますが、これはビルドの並列度を指定するものなので、環境に合わせて変更します。私は並列度 1 でビルドしました。ビルド時間を計測したわけではありませんが、出力されたファイルのタイムスタンプから判断すると、30 分程度かかったようです。なお、最初に作成している smt ディレクトリは今回の記事で用いるファイル全体をまとめるためのもので、特に意味はありません。ディレクトリ名は statistical machine translation の略です。

$ mkdir ~/smt
$ cd ~/smt
$ git clone https://github.com/moses-smt/mosesdecoder.git
$ cd mosesdecoder
$ ./bjam

Moses - Development/GetStarted

次に GIZA++ をインストールします。GIZA++ の代わりに mgiza や Fast Align でもよいようですが、ここでは GIZA++ を使います。GIZA++ は Google code でホストされており、Subversion リポジトリからソースコードを取得できます。

$ cd ~/smt
$ svn checkout http://giza-pp.googlecode.com/svn/trunk giza-pp
$ cd giza-pp
$ make

giza-pp - GIZA++ statistical translation models toolkit - Google Project Hosting

GIZA++ をインストールした後、下記のウェブページに書かれているように、実行ファイルを一箇所に集めておきます。これは Moses のスクリプトの都合で、一つのディレクトリにまとめておかないとスクリプトが正しく動作しないためです。

$ cd ~/smt/mosesdecoder
$ mkdir tools
$ cp -p ~/smt/giza-pp/GIZA++-v2/GIZA++ tools/ 
$ cp -p ~/smt/giza-pp/GIZA++-v2/snt2cooc.out tools/ 
$ cp -p ~/smt/giza-pp/mkcls/mkcls tools/ 

Moses - Moses/Baseline

以上で Moses を利用する準備が整いました。このほかに SRILM または IRSTLM をインストールすることもできますが、Moses に同梱されている KenLM で代用できるので、今回は KenLM を使うことにして先に進みます。

対訳コーパスの準備

Moses を利用するためには、まず、翻訳のお手本となる対訳コーパスを準備する必要があります。今回は、田中コーパスを利用します。田中コーパスは、英語の教科書などから集められた日英対訳コーパスで、CC-BY ライセンスでウェブ上に公開されています。ウェブサイトの Downloads の欄から UTF-8 の complete version を取得します。

$ mkdir ~/smt/corpus
$ cd ~/smt/corpus
$ wget ftp://ftp.monash.edu.au/pub/nihongo/examples.utf.gz

Tanaka Corpus - EDRDG Wiki

ダウンロードした gz ファイルは、展開すると次のようなテキスト形式になっています。A: で始まる各行がそれぞれ日英の対訳です。B: で始まる行は、日本語側の文について読みや基本形を表現しているようですが、今回は利用しません。

$ gzip -dc examples.utf.gz | head
A: ムーリエルは20歳になりました。     Muiriel is 20 now.#ID=1282_4707
B: は 二十歳(はたち){20歳} になる[01]{になりました}
A: すぐに戻ります。     I will be back soon.#ID=1284_4709
B: 直ぐに{すぐに} 戻る{戻ります}
A: すぐに諦めて昼寝をするかも知れない。 I may give up soon and just nap instead.#ID=1300_4727
B: 直ぐに{すぐに} 諦める{諦めて} 昼寝 を 為る(する){する} かも知れない
A: 愛してる。   I love you.#ID=1434_4851
B: 愛する{愛してる}
A: ログアウトするんじゃなかったよ。     I shouldn't have logged off.#ID=1442_4858
B: ログアウト~ 為る(する){する} の{ん} だ{じゃなかった} よ[01]

このファイルから、日英それぞれを別々のファイルに抜き出します。A: で始まる行を抽出して、行内の必要な部分を切り出します。日英はタブ区切りになっているので、cut で簡単に分割できます。日本語の方は mecab を使って単語に分割しておきます。本来は、翻訳精度を高めるために、この段階でさまざまな工夫が必要なのですが*2、今回は手っ取り早く結果を見てみることを優先して、そういった工夫を何も行わず先に進みます。

$ gzip -dc examples.utf.gz | grep ^A: | cut -f1 | sed 's/^A: //' | mecab -Owakati >tanaka.ja
$ gzip -dc examples.utf.gz | grep ^A: | cut -f2 | sed 's/#.*$//' >tanaka.en

言語モデルの学習

日本語側のファイルから、言語モデルを学習します。言語モデルとは、ある単語の列を与えると、日本語としての流暢さによって得点をつけてくれる関数のようなものです。英語から日本語に翻訳する場合、日本語として自然な文を出力する必要があります。機械翻訳では、言語モデルを利用することで、翻訳候補の中から日本語として自然なものが選ばれるようにします。一方、英語側は機械翻訳システムの入力になるので、言語モデルを学習する必要はありません。

今回は、Moses に同梱されている KenLM を用いて言語モデルを学習します。KenLM のウェブサイトはこちらにあります。
kenlm . code . Kenneth Heafield

Moses のウェブページに記載されているとおり、KenLM での言語モデル学習を行うには、以下のようにコマンドを実行します。

$ ../mosesdecoder/bin/lmplz -o 5 -S 80% -T /tmp <tanaka.ja >tanaka.ja.arpa

Moses - FactoredTraining/BuildingLanguageModel

実行結果として出力される tanaka.ja.arpa はテキスト形式のファイルなので、内容を確認できます。興味があれば眺めてみてください。

翻訳モデルの学習

日英の対訳コーパスから、翻訳モデルを学習します。翻訳モデルとは、英語と日本語それぞれの単語列を与えると、対訳としての適切さによって得点をつけてくれる関数のようなものです。翻訳結果として出力される日本語の文は、もちろん入力された英語の文の対訳として適切でなければいけません。機械翻訳では翻訳モデルを利用して、入力文の対訳として適切な出力文が選ばれるようにします。

翻訳モデルを学習する部分が、Moses の主要な機能になります。さまざまなパラメータを指定して学習方法を細かくチューニングすることもできますが、今回はそういった面倒な作業をすべて省略して、Moses に用意されているスクリプトにお任せしてしまいます。以下のように train-model.perl スクリプトを実行します*3

$ cd ~/smt
$ ./mosesdecoder/scripts/training/train-model.perl \
    --root-dir . \
    --corpus corpus/tanaka \
    --f en \
    --e ja \
    --lm 0:5:$HOME/smt/corpus/tanaka.ja.arpa \
    --external-bin-dir ./mosesdecoder/tools

Moses - FactoredTraining/HomePage

オプションの --corpus と --f, --e で対訳コーパスを指定します。--corpus の値に --f と --e の拡張子を追加したものを対訳コーパスの各言語側のファイルとして読み込みます。--f が翻訳元の言語、--e が翻訳先の言語です。機械翻訳の分野では、翻訳元を f (French)、翻訳先を e (English) で表す慣習になっているようです。--lm には言語モデルを指定します。先頭の 0:5: はオマジナイで*4、その後にファイル名を指定します。このファイル名は絶対パスで指定しなければいけません。最後の --external-bin-dir は、Moses 以外のプログラムの実行ファイルが存在するパスを指定します*5

翻訳モデルの学習には時間がかかります。私の環境では 20 分ほどでした。プログラムを実行すると、いくつかのディレクトリに様々なファイルが出力されますが、最終的な出力は model/phrase-table.gz です。展開したものはテキスト形式なので、こちらも興味があれば眺めてみてください。ただし、翻訳を実行するときには gz ファイルのままプログラムに渡すので、圧縮ファイルを消さないように注意してください。

英日翻訳を試す

以上でモデルの学習が完了したので、いよいよ、英日の翻訳を試してみます。実行の前に、model/moses.ini という設定ファイルを一箇所だけ編集する必要があります。このファイルは、翻訳モデル学習によって自動生成されるのですが、利用する言語モデルの種類が SRILM になってしまいます。今回は KenLM を利用していますので、以下のように KENLM に修正します。

$ diff -u model/moses.ini{-,}
--- model/moses.ini-    2014-10-25 20:39:20.939651965 +0900
+++ model/moses.ini     2014-10-25 20:49:34.373698755 +0900
@@ -20,7 +20,7 @@
 PhrasePenalty
 PhraseDictionaryMemory name=TranslationModel0 num-features=4 path=/home/y-uti/smt/model/phrase-table.gz input-factor=0 output-factor=0
 Distortion
-SRILM name=LM0 factor=0 path=/home/y-uti/smt/corpus/tanaka.ja.arpa order=5
+KENLM name=LM0 factor=0 path=/home/y-uti/smt/corpus/tanaka.ja.arpa order=5

 # dense weights for feature functions
 [weight]

次のようにプログラムを実行します。-f オプションに、先ほど編集した設定ファイルを指定します。言語モデル、翻訳モデルの指定などは設定ファイルに書かれているので、実行時に指定する必要はありません。

$ mosesdecoder/bin/moses -f model/moses.ini
...
IO from STDOUT/STDIN
Created input-output object : [8.505] seconds

言語モデル、翻訳モデルが読み込まれた後、入力待ちの状態になります。ここで何か英語の文を入力すると、日本語の文が表示されます。たとえば以下のようになります。

This is a pen.
これ は ペン です 。

他にもいくつか試してみたいのですが、私は英語が苦手なので、そもそも正しい入力文を作る自信がありません。そこで、文科省の学習指導要領解説に載っている例文から適当に選んでみました。
中学校学習指導要領解説:文部科学省

実行結果はこのようになりました。正しく翻訳できているものも、駄目なものもあるようです。

What would you like to drink?
お 飲み物 は 何 です か 。
Orange juice, please.
オレンジ ジュース を お 願い し ます 。
My grandfather knows how to use the computer.
私 の 祖父 は コンピューター の 使い 方 を 知っ て いる 。
We understand that the test is difficult.
私 たち は その テスト を 理解 する こと は 難しい 。
I don't know what he will do next.
私 は 彼 が 次 に 何 を し たら いい の か 知っ て い ます 。
The animal which runs the fastest is the cheetah.
その 動物 の 中 で 一番 速く 走る cheetah. です 。
This is the dog which I like the best.
この 犬 は 、 私 が 一番 好き だ 。

ここまでの例では、小さなコーパスで、ろくなチューニングもしていない割には、そこそこ上手く翻訳できているようにも見えますが、実はこれは、学習に使ったコーパスと翻訳を試した例文の分野が似ているためです。今回利用した田中コーパスは、そもそも英語の教科書などから集められた対訳コーパスでした。KenLM による言語モデル学習も Moses による翻訳モデル学習も、どちらもコーパスをお手本にしてモデルを学習するので、コーパスの内容に近いものは比較的上手く翻訳できる一方、コーパスと異なる分野の文は、なかなか上手く翻訳できません。

異なる分野の文例として、PHP のオンラインマニュアルの文を与えてみます。

PHP is a widely-used open source general-purpose scripting language that is especially suited for web development and can be embedded into HTML.

PHP: What is PHP? - Manual

結果は以下のようになりました。翻訳精度を高めていくためには、さまざまな分野の英語を網羅した大規模なコーパスが必要になります。

PHP は widely-used を 源 と いう general-purpose scripting 言語 と いう こと は 特に ホーム ページ に 向い て 、 発展 に HTML. embedded こと が できる 。

*1:今回の場合は英語と日本語の対訳文をたくさん集めたもの。機械学習 (教師あり学習) では、こういったお手本を元にして、入力から出力を得るためのルールを学習します。

*2:たとえば、英語側についても、小文字に変換したり、ピリオドやコンマを分割したりといった処理を行います。

*3:読みやすさのために改行していますが、もちろん一行に書いて構いません。

*4:それぞれ factor, order を指定することになっています。

*5:これを一つしか指定できないために、最初に GIZA++ の実行ファイル群を一つのディレクトリにまとめる必要がありました