データナード

機械学習と自然言語処理についての備忘録 (旧ナード戦隊データマン)

tensor2tensorのn-best出力を言語モデルでリスコアリング

NMTにおけるリスコアリングとは、NMTによって得た出力を、別のモデルで再評価することです。その一つの方法として、言語モデルによるリスコアリングがあります。

概要

tensor2tensor1で訓練した翻訳モデルのn-best出力(良いものから順にn個出力したもの)を、KenLM2を使った言語モデルで再評価し、並び替えます。

言語モデルの利点は、モノリンガルコーパスによって訓練されることです。そのため、パラレルコーパスで訓練したNMTよりもはるかに多いデータを用いて訓練することができます。そのため、n-best出力に対する言語モデルによるリスコアリングが、NMTの評価順よりも優れている可能性があります。

今回行う方法は以下の手順に従います。

  1. 訓練済みtensor2tensor翻訳モデルで翻訳し、n-best出力を得る。
  2. KenLMで言語モデルを作成する。
  3. バイナリ化した言語モデルを読み込み、n-best出力をリスコアリングする。

tensor2tensorでn-best出力を得る

n-best出力を得るには、decode_hparams内にreturn_beams=Trueを指定します。すると、出力結果にはタブで分割された翻訳候補文が得られます。

t2t-decoder \
    --data_dir ../model_data/data_jaen_2 \
    --problem translate_jaen__backtranslation \
    --model transformer \
    --hparams_set=transformer_base_single_gpu \
    --output_dir=../training_results/training_result_jaen_2 \
    --decode_hparams="beam_size=5,alpha=0.6,return_beams=True" \
    --decode_from_file=../data/test.txt.ja.fixed \
    --decode_to_file=../data/test_out/test.hyp.5.en.tmp \
    --t2t_usr_dir=../model

KenLMで言語モデルを作成

KenLMは高速でシンプルな言語モデルツールです。

コンパイル

git clone https://github.com/kpu/kenlm
cd kenlm
mkdir -p build
cd build
cmake ..
make -j 4
pip install https://github.com/kpu/kenlm/archive/master.zip

言語モデルの作成

言語モデル作成のためのモノリンガルコーパスを準備してから作成します。

cat data/train-1.txt.en.fixed data/train-2.txt.en.fixed data/train-3.txt.en.fixed data/dev.txt.en.fixed > /tmp/endata
mkdir -p kenlm_model
kenlm/build/bin/lmplz -o 5 < /tmp/endata > kenlm_model/en.lm
kenlm/build/bin/build_binary -T /tmp kenlm_model/en.lm kenlm_model/en.lm.bin

言語モデルでリスコアリング

リスコアするには、出力済みのn-best出力に対して、kenlmで評価する必要があります。

import kenlm

model = kenlm.Model("../kenlm_model/en.lm.bin")


def rescore(datafile="../data/test_out/test.hyp.5.en.tmp"):
    with open(datafile) as f:
        with open("../data/test_out/tensor2tensor/test.hyp.5.en", "w") as fw:
            for line in f:
                sents = line.strip().split("\t")
                maxscore = -1000
                target_sent = None
                for sent in sents:
                    score = model.score(sent, bos=True, eos=True)
                    if score > maxscore:
                        target_sent = sent
                        maxscore = score
                fw.write(target_sent + "\n")


if __name__ == "__main__":
    rescore()

評価

リスコアしない場合とする場合とでBLEUがどう変わるのか見ます。

[tensor2tensor backtranslation]
BLEU+case.mixed+numrefs.1+smooth.exp+tok.intl+version.1.4.0 = 27.3 61.0/33.6/20.6/13.2 (BP = 1.000 ratio = 1.009 hyp_len = 46013 ref_len = 45606)

[tensor2tensor backtranslation & lm rescoring]
BLEU+case.mixed+numrefs.1+smooth.exp+tok.intl+version.1.4.0 = 29.8 62.1/35.4/22.9/15.6 (BP = 1.000 ratio = 1.000 hyp_len = 45598 ref_len = 45606)

リスコアしない場合は27.3であるのに対し、リスコアした場合は29.8という結果になっています。

考察

言語モデルによってリスコアすることによって性能が上がるらしいということが確認できました。言語モデルの訓練はモノリンガルコーパスだけでいいですし、文法的なチェック機構のような働きをしてくれている可能性があります。

ただ、一度翻訳した後にリスコアするので、実行手順が増加し、翻訳速度が落ちます。KenLMはかなり高速ですが、他の言語モデルツールを使う場合はもっと速度的に悪化する可能性があります。

n-best出力でいくつ出力するのかという選択肢があります。n-bestをどのぐらい出せばスコアがどう変わるのか、という点については検証する必要がありそうです。

リスコアリングの手法には、この種のShallow Fusionの他に、Neural Lattice Search, Ensembleなどの方法があり、実用的な組み合わせを考える必要がありそうです3

参考