英日翻訳器を環境構築なしで10分程度で作ってみた

## やること

英日翻訳器を作ります．

ところで，データを頑張って集めていろいろな処理をして，学習するというのを全部自分で作ろうとすると，とても大変です．
しかも，ニューラルネットを使った機械翻訳システムの学習には，一般に十分な計算リソースや計算時間が必要となります．

今回は，そういう面倒な思いをしなくても英日翻訳器を作れる方法を紹介します．

この記事で用いるコードはGitHub Gistでも公開しました．

https://gist.github.com/nymwa/d584be8ed36b9513a31e31f5b8f50112

このリンクを開いて，"Open in Colab"を押せば実際に翻訳器を学習して実行することができます．ぜひどうぞ．


## 環境

Google Colaboratory上であれば環境構築をする必要はありません．
実行する際には，ランタイム->ランタイムのタイプを変更のハードウェアアクセラレータがGPUになるように気をつけてください．

自分で環境構築をする場合は，Python3とCUDAが動くようにしてください．

## データの入手と前処理

データには[tatoeba.org](https://tatoeba.org)にある英語と日本語の並行コーパスを用います．データはすぐに使えるように予めフィルタリング，トークン化，小文字化の前処理を施したものを用います．用意したデータは[こちら](https://github.com/nymwa/light_enja2/tree/main/corpus)から見れます．

英文のトークン化には[Sacremoses](https://github.com/alvations/sacremoses)，日本語は[GiNZA](https://github.com/megagonlabs/ginza)を用いています．さらに，トークン数が4〜16でない文，英文日本語文トークン数比率が0.5〜1.8でない文，重複する文，低頻度な語彙を含む文を取り除いています．


前処理をしたデータは，訓練データ10万文，検証データ5000文，テストデータ5000文の合計11万文からなっていて，例えば，訓練事例の冒頭10件は以下のようになっています．

```
i was busy , but i helped mother .      私 は 忙しかっ た が 母 の 手伝い を し まし た 。
i finally penetrated the meaning of the riddle .        ついに その なぞ の 意味 が 解け た 。
they say it will rain . 雨 が 降る そう だ 。
he is dead to all sense of shame .      彼 は まったく の 恥 知ら ず だ 。
we decided to put off the meeting until next sunday .   私達 は 会合 を 次 の 日曜 まで 延期 する こと に 決め た 。
she was kissed by him . 彼女 は 彼 に キス さ れ た 。
each individual paid 1,000 dollars .    一人 あたり 千 ドル 払っ た 。
you have to have a piece of pie .       ぜひ パイ を お 召し上がり ください 。
there is no other way . 他 に 方法 は ない 。
what do i have to do to get a library card ?    どう やっ て 図書館 カード を 手 に 入れ ます か ?
```


必要なデータやライブラリは以下のコードを実行してダウンロード・インストールします．使うデータやコードは[light_enja2](https://github.com/nymwa/light_enja2)から参照できます．

``` shell
git clone https://github.com/nymwa/light_enja2.git
cd light_enja2
pip install -r requirements.txt
```

未知語や低頻度語になるべく対処できるように，単語をさらにサブワードに分割する処理をします．
上のコードでダウンロードしてきた`light_enja2`ディレクトリで，以下のコマンドでシェルスクリプトを実行するとサブワードの学習などの前処理を全部やってくれます．`preproc.sh`の中身は[こちら](https://github.com/nymwa/light_enja2/blob/main/preproc.sh)から見れます．

``` shell
bash preproc.sh
```

## 訓練

ニューラル翻訳モデルのライブラリである[Fairseq](https://github.com/pytorch/fairseq)に実装されているTransformerモデルを用いて英日翻訳器を学習します．

以下のコマンドを実行すると学習ができます．1GPUを使うことを想定しているので，複数GPUがある環境では`CUDA_VISIBLE_DEVICES=0`などとして使用するGPUを指定する必要があります．一般的に用いられるものよりも軽量でかつ正則化が強いモデル設定になっていますが，このために高速に学習できるようになっています．Google ColaboratoryのK80でも10分ぐらいで学習ができます．

```
fairseq-train \
    data-bin \
    --save-interval 10 \
    --max-epoch 10 \
    --update-freq 1 \
    --max-tokens 4000 \
    --arch transformer \
    --encoder-normalize-before \
    --decoder-normalize-before \
    --encoder-embed-dim 512 \
    --encoder-ffn-embed-dim 1024 \
    --encoder-attention-heads 4 \
    --encoder-layers 4 \
    --decoder-embed-dim 512 \
    --decoder-ffn-embed-dim 1024 \
    --decoder-attention-heads 4 \
    --decoder-layers 4 \
    --share-all-embeddings \
    --dropout 0.3 \
    --optimizer adam \
    --adam-betas '(0.9, 0.999)' \
    --lr 0.002 \
    --lr-scheduler inverse_sqrt \
    --warmup-updates 2000 \
    --warmup-init-lr 1e-07 \
    --clip-norm 1.0 \
    --weight-decay 0.01 \
    --criterion label_smoothed_cross_entropy \
    --label-smoothing 0.6
```

## 生成・評価

以下のコマンドでテストデータでの生成・評価ができます．

```
fairseq-interactive data-bin \
    --buffer-size 1024 \
    --batch-size 128 \
    --path checkpoints/checkpoint10.pt \
    --beam 5 \
    --lenpen 0.6 \
    < test.en \
    | grep '^H' \
    | cut -f 3 \
    | python src/decode.py \
    | tee output.txt \
    | sacrebleu corpus/test.ja
```

評価尺度BLEUを用いて出力結果の評価をしています．実装には[SacreBLEU](https://github.com/mjpost/sacrebleu)を用いました．出力結果は以下のようになっています．"30.6"の部分がBLEUの値です．

```
BLEU+case.mixed+numrefs.1+smooth.exp+tok.13a+version.1.5.1 = 30.6 66.3/41.9/27.1/18.6 (BP = 0.891 ratio = 0.896 hyp_len = 45908 ref_len = 51230)
```

テストデータでの結果の冒頭10文を示します．うまくいっている例もあれば，だめな例もあります．
ちなみに，データサイズを増やしたり，モデルサイズを大きくしたりするとよりよい翻訳結果を得られるようになります．

```
==> 翻訳元の入力データ <==
her mother is a pretty woman .
there were six sheep in the field .
i was able to take a great photo during this morning 's walk .
i 'll call you up this evening .
she is what we call a bookworm .
the ceremony has ended .
i asked him if he had enjoyed himself the day before .
he is free from care .
the train is always on time .
if they started at two , they should arrive at six .

==> 翻訳器の出力 <==
彼女 の 母 は 美しい 女性 です 。
畑 に は 6 人 の 羊 が い た 。
今朝 は 散歩 の 間 に 大きな 写真 を 撮る こと が でき た 。
今晩 電話 し ます 。
彼女 は 本 の 虫 だ 。
式 は 終わっ た 。
私 は 彼 に 前日 自分 で 楽しん だ か どう か 尋ね た 。
彼 は 注意 し て い ない 。
列車 は いつも 定刻 に いる 。
2 時 に 出発 すれ ば 、 彼 ら は 6 時 に 着く べき だ 。

==> 正解データ <==
彼女 の お母さん は きれい な 女 の 人 です 。
野原 に は 六 頭 の 羊 が い た 。
この 朝 の 散歩 で とても 素晴らしい 写真 を 撮る こと が でき た 。
今夜 電話 し ます 。
彼女 は いわゆる 本 の 虫 です 。
式典 は 終わり まし た 。
昨日 は 楽しかっ た か 、 と 私 は 彼 に 尋ね た 。
彼 に は 心配 事 が ない 。
その 列車 は いつも 時刻 通り だ 。
2 時 に 出発 し たら 、 6 時 に は つく はず だ 。
```

## おわりに

英日翻訳器を作ってみました．めんどうな部分が予めやってしまってあれば，翻訳器はかんたんに作れることがわかります．また，無料の計算資源での10分程度の学習であっても，そこそこのものが作れることがわかります．別のデータでも，データの前処理などをやれば同様にできるので，やってみるのもいいかもしれません．

翻訳器に好きな英文を入れて翻訳結果を見てみるのもいいと思います．"やること"のところに貼ったリンクの[gist](https://gist.github.com/nymwa/d584be8ed36b9513a31e31f5b8f50112
)の一番下の方に，好きな文を翻訳するコードがあるので，そちらでできます．

おわり