Transformerで英日翻訳

TransformerとCloud TPUを使って英語を日本語に翻訳するニューラル翻訳モデルを訓練してみたので紹介します。

#前置き
3年前の2016年に[TensorFlowとGPUインスタンスで英日翻訳](https://qiita.com/yoh_okuno/items/50e7dccfb6ebfa8a7238)という記事を書きましたが、当時の機械翻訳はLSTMにAttentionを追加したモデルが主流で、ニューラル翻訳のモデルもこれから複雑化の一途を辿るのだろうなと思っていました。

しかし、最近久しぶりにニューラル翻訳のサーベイをしてみて、良い意味で期待を裏切られました。2017年に登場したTransformerでは、LSTMを完全に廃止し、Attentionのみで高精度かつ高速な翻訳を可能にしました。2018年のWMT（機械翻訳のトップ会議）のShared Taskではほぼ全ての参加チームがTransformerベースのモデルに切り替えており、普及のスピードに驚きました。

[Attention is All You Need](https://arxiv.org/abs/1706.03762)（Transformerの論文）

早速試してみようと思い調べてみると、TensorFlow 2.0 BetaのチュートリアルにTransformerのページが見つかったものの、AWSやGCPでは2019年現在まだベータ版の2.0には対応していないので諦めました。幸い[Cloud TPU](https://cloud.google.com/tpu/)のチュートリアルに[Tensor2Tensor](https://github.com/tensorflow/tensor2tensor)というライブラリを使ったTransformerのチュートリアルがあったので、今回はこれをベースにすることにします。

[Running the Transformer with Tensor2Tensor](https://cloud.google.com/tpu/docs/tutorials/transformer)

方針としては、まずチュートリアル通りに英語からドイツ語への翻訳モデルを訓練し、次にデータを英語と日本語の対訳コーパスに置き換えて、訓練と推論を再実行します。英日の対訳コーパスとしては、前回と同じく[英辞郎の例文データ](https://qiita.com/yoh_okuno/items/50e7dccfb6ebfa8a7238#%E3%83%87%E3%83%BC%E3%82%BF)を使います。

# 英独翻訳モデルの訓練
まず、英独翻訳のモデルを訓練します。Cloud TPUのチュートリアル通りに実行すると翻訳結果やBLEUスコアがどこに出力されたのかわからなかったので、[Tensor2TensorのREADME](https://github.com/tensorflow/tensor2tensor#translation)を参考に、`t2t-decoder`というコマンドを使います。このとき`translate_ende_wmt32k_packed`のように`_packed`がついた設定で訓練すると`t2t-decodder`は使えないというエラーが出るので、注意が必要です。最終的に実行したコマンドは、以下のようになりました。

```bash
export STORAGE_BUCKET=gs://＜バケット名＞
export DATA_DIR=$STORAGE_BUCKET/data
export TMP_DIR=/tmp/t2t_tmp
export PATH=.local/bin:$PATH
export PROBLEM=translate_ende_wmt32k

# データのダウンロード、前処理、アップロード（２時間ほどかかる）
t2t-datagen --problem=$PROBLEM --data_dir=$DATA_DIR --tmp_dir=$TMP_DIR

export TRAIN_DIR=$STORAGE_BUCKET/training1/transformer_ende_1
export MODEL=transformer
export HPARAMS=transformer_tpu

# ニューラル翻訳モデルの訓練（3時間ほどかかる）
t2t-trainer \
  --model=$MODEL \
  --hparams_set=$HPARAMS \
  --problem=$PROBLEM \
  --train_steps=40000 \
  --eval_steps=3 \
  --data_dir=$DATA_DIR \
  --output_dir=$TRAIN_DIR \
  --use_tpu=True \
  --cloud_tpu_name=$TPU_NAME

# テストデータの英文を作成
export DECODE_FILE=decode_this.txt
echo "Hello world" >> $DECODE_FILE
echo "Goodbye world" >> $DECODE_FILE

# テストデータの英独翻訳を実行
t2t-decoder \
  --data_dir=$DATA_DIR \
  --problem=$PROBLEM \
  --model=$MODEL \
  --hparams_set=$HPARAMS \
  --output_dir=$TRAIN_DIR \
  --decode_from_file=$DECODE_FILE \
  --decode_to_file=translation.de

# 翻訳結果の確認
cat translation.de
Hello Welt
Goodbye Welt

# BLEUスコアの確認
echo "Hallo Welt" > ref-translation.de  
echo "Auf Wiedersehen Welt" >> ref-translation.de
t2t-bleu --translation=translation.de --reference=ref-translation.de
BLEU_uncased =  46.31
BLEU_cased =  46.31
```
無事に英語からドイツ語へ翻訳できました。

# 英日コーパスへの差し替えと再訓練
次に、訓練データを英独から英日に差し替えます。`t2t-datagen`コマンドを実行するとWMTのデータのダウンロード、圧縮ファイルの解凍、前処理、Storageへのアップロードを行いますが、指定された`tmpdir`に既にファイルがあるとダウンロードと解凍をスキップして前処理から再実行するようになっているので、これを利用します。WMT18は複数の言語ペアと複数のデータセットからなりますが、訓練データと開発データのうち１つの英独ペアのファイルを英日ペアに置き換えて、残りは空のファイルにしてしまいます。具体的には`tmp/t2t_tmp`にあるファイルを次のように置き換えます。

| ファイル名              | 置き換え後のファイル   |
|:---------------------|:-------------------|
| commoncrawl.de-en.en | 英語の訓練データ      |
| commoncrawl.de-en.de | 日本語の訓練データ     |
| training/europarl-v7.de-en.en | 空のファイル |
| training/europarl-v7.de-en.de | 空のファイル |
| dev/newstest2013.en | 英語の開発データ       |
| dev/newstest2013.de | 日本語の開発データ     |
| dev/*.en            | 空のファイル |
| dev/*.de            | 空のファイル |
| translate_*         | キャッシュなので削除する |

あとは前節と同じコマンドを実行すると、データのダウンロードをスキップして置き換え後の英日コーパスで訓練が始まります。


#　精度評価
まずは、今回のTransformerと前回のLSTM+Attentionの精度を比較します。テストデータでの翻訳結果の精度を表すBLEUスコアは以下のようになりました。

| モデル       | BLEU |
|:------------|:-----|
| LSTM        | 10.28 |
| Transformer | 24.56 |

モデル以外にも色々違うところはあるものの、かなり大きな違いに驚かされます。

# 翻訳例
次に、実際に英文を日本語に翻訳してみた例を比べてみます。

| 英語                                  | LSTM                            | Transformer                           |
|:-------------------------------------|:---------------------------------|:-------------------------------------|
| Hi, how are you?                     | こんにちは 、 元気 ？                 | こんにちは 、 お 元気 です か ？                       |
| I am a software engineer.            | 私 は エンジニア の エンジニア です 。   | 私 は ソフトウェア エンジニア です 。          |
| My name is Yoh.                      | 私 は _UNK と いう 名前 です 。        | 私 は ヨー と いい ます 。                 |
| This sentence is machine translated. | この 文 は 、 翻訳 文 を 翻訳 し ます 。 |　この 文 は 機械 的 に 変換 さ れ ます 。 |

LSTMと比べるとTransformerの方が自然になっていて、特に同じ単語の繰り返しや翻訳抜けの問題が解決しているように見えます。これは、attentionを積み重ねることで翻訳済みの単語や未翻訳の単語がわかるようになったと推測できます。また、以前は単語ベースだったのがサブワードモデルになったので、未知語も無理やり翻訳してくれます。

また、Transformerが翻訳したテストデータ中のもっと長い文を見てみると、こんな文がありました。

> Although I speak English, there are few people who understand my English in Japan, and also there is no such official organization that understands Arabic languages.
>> 私 は 英語 を 話し ます が 、 日本 で は 英語 を 理解 し て いる 人 は 少ない し 、 アラビア 語 を 理解 し て いる 公式 組織 も あり ませ ん 

> If he decides that your product has some merit for his company, then his superior will enter the discussions and ask you for a more detailed explanation.
>> もし 彼 が あなた の 製品 に 何 ら か の メリット が ある と 判断 すれ ば 、 上司 は 議論 に 加わり 、 さらに 詳しい 説明 を 求め て くる でしょう 

> But in any case, my friend gave me this program, and my intention was to change the editing commands at the top level to make them compatible with the original Emacs that I was used to.
>> で も 、 いずれ に し て も 友人 が この プログラム を くれ た の で 、 僕 の 目的 は トップ レベル で 編集 コマンド を 変え て 、 僕 が 使 っ た オリジナル の エマックス と の 互換 性 を 高める こと だっ た 。

> In the area of pickled vegetables, we have takuan made from daikon radish, which has been told that it was originally created by an Edo period monk.
>> 漬物 の 中 に は 、 大根 から 作ら れ た 大根 が あり 、 江戸 時代 の 僧侶 が 作っ た と いわ れ て い ます 

> Thaksin, currently living in Dubai to avoid corruption charges, says he does not wish to create further problems and will not immediately return to Thailand.
>> 現在 、 ドバイ に 住む タクシン 氏 は 、 汚職 の 罪 を 避ける ため に 暮らし て い ます が 、 さらなる 問題 を 起こし たい と は 思い ませ  ん し 、 すぐ に タイ に 戻ら ない と 話し て い ます 

# 費用
今回は４つのTransformerモデルを訓練し、GCPの利用額は約1万円になりました。新規ユーザーは3万円の無料枠がもらえるので、実質的な負担はコーパス購入にかかった２０００円だけです。

# 最後に
Transformerで英日翻訳を試しました。

3年前と比べると、モデルの進化以外にもCloud TPUが使えるようになって必要な費用や時間が少なくなったりしていて、まだまだ面白い分野だなと思いました。[Google翻訳がニューラルになった](https://arxiv.org/abs/1609.08144)ときは「ニューラル翻訳には大量のGPUと訓練データが必要なのか…」と少しがっかりしたものですが、いつの間にか個人でもこのレベルの翻訳機が、しかも前回と同じ対訳コーパスで作れるようになったのは驚きでした。

今回は対訳コーパスのみでTransformerを訓練しましたが、[単言語コーパスを使ったり](https://arxiv.org/abs/1808.09381)、[多言語の対訳コーパスを使えば](https://arxiv.org/abs/1903.00089)大きく精度が向上すると言われています。Transformer自体の改善としては[デコーダ側をLSTMと組み合わせたり](https://arxiv.org/abs/1804.098490)、[AutoMLでCNNと組み合わせたり](https://arxiv.org/abs/1901.111170)すると精度が上がるようです。更には、文の生成方法も最初の単語から順番に生成するのではなく、[単語の挿入や削除をニューラルでやったり](https://arxiv.org/abs/1905.11006)、[文全体を一気に生成したり](https://arxiv.org/abs/1609.05473)、[GANで系列生成したり](https://arxiv.org/abs/1609.05473)、[強化学習で直接BLEUを最大化する](https://arxiv.org/abs/1607.07086)手法が出てきています。

個人的には言語モデルを使った予測変換の仕事をやってきて、同じく言語モデルの応用である機械翻訳には興味はあったものの必要なリソースが手に入りづらく、ようやく手の届く範囲にやってきたような感覚があります。これからも楽しみな分野であり、機会があれば何らかの形で関われたらと思います。