OpenNMT-py (PyTorch) をGoogle Colabで実行する。

# はじめに
Google Colabでも、機械学習による翻訳が動くので試してみた。OpenNMT-pyのデフォルトデータでは、英独翻訳ができる。さらに、英和翻訳のコーパス(KFTT等)を使えば、英和翻訳が出来る。

#手順(英独翻訳)
## 環境設定
OpenNMT-pyをダウンロードする。

```shell
!pwd
!ls
!git clone https://github.com/OpenNMT/OpenNMT-py.git
```
Pythonパッケージの設定を行う。

```shell
!cd OpenNMT-py;pip install -r requirements.txt 
```
## 前処理
学習前の処理を行う。

```
!python OpenNMT-py/preprocess.py -train_src '/content/OpenNMT-py/data/'src-train.txt -train_tgt '/content/OpenNMT-py/data/'tgt-train.txt -valid_src '/content/OpenNMT-py/data/'src-val.txt -valid_tgt '/content/OpenNMT-py/data/'tgt-val.txt -save_data '/content/OpenNMT-py/data/'demo
```

## 学習
GPUを使用するため、gpu_ranksフラグを設定する。しかし、GPUを使っても、デフォルトでは、学習に10時間程度かかる。ここでは、ステップ数を一桁減らしてtrain_stepsで指定して1万で行う。なお、CPUのみで実行した場合、20倍以上余分に計算時間がかかる。また、Google Colabでは、K80からTesla T4に変わったようで、2.5倍近く処理速度が上がっている。
また、引数はいろいろあり細かく設定出来るので、helpコマンドもしくは、ソースコード(下記)を見て確認したほうが良い。

```
!python OpenNMT-py/train.py -gpu_ranks 0 --train_steps 10000 -save_model '/content/OpenNMT-py/data/'data-model -data '/content/OpenNMT-py/data/'demo
```
たとえば、以下のような出力が現れる。accは、accuracyを示す。このため100に出来るだけ近いほうが良い。pplは、perplexityを示す。このため、1に出来るだけ近いほうが良い。xentは、CrossEntropyを示す。このため、出来るだけ小さい数字が良い。

```
[2019-03-26 15:09:20,472 INFO] Step 10000/10000; acc:  74.59; ppl:  2.75; xent: 1.01; lr: 1.00000; 4195/4177 tok/s;   3528 sec
```
## 翻訳
英独翻訳であるが以下のようにして出来る。

```shell
!python OpenNMT-py/translate.py -model '/content/OpenNMT-py/data/'data-model_step_10000.pt -src '/content/OpenNMT-py/data/'src-test.txt -output pred.txt -replace_unk -verbose
```
#手順(英和翻訳)
## 環境設定
英独翻訳と同じである。なお、データが異なるのでその部分だけ記載する。
KFTTは、京都に関するWikipediaを人手で翻訳したコーパスである。このため、翻訳記事は京都に限られると考えられる。詳細は、コーパスの説明を参照のこと。

```
!wget http://www.phontron.com/kftt/download/kftt-data-1.0.tar.gz
!tar xzf kftt-data-1.0.tar.gz
!ls kftt-data-1.0/data/tok
!cp -r kftt-data-1.0/data/tok OpenNMT-py/kftt
!ls OpenNMT-py/kftt
```
## 前処理
前処理を、行う。この際、引数は以下の表の通りである。なお、ディレクトリは、dataをkfttに置き換えている。

```
!python OpenNMT-py/preprocess.py -train_src '/content/OpenNMT-py/kftt/'kyoto-train.en -train_tgt '/content/OpenNMT-py/kftt/'kyoto-train.ja -valid_src '/content/OpenNMT-py/kftt/'kyoto-dev.en -valid_tgt '/content/OpenNMT-py/kftt/'kyoto-dev.ja -save_data '/content/OpenNMT-py/kftt/'demo
```

|引数　　　　 | KFTTデータ    | OpenMNT-py英独データ | 
|:-----------:|:---------------:|:---------:|
| train_src  | kyoto-train.en | src-train.txt|
| train_tgt  | kyoto-train.ja | tgt-train.txt|
| valid_src  | kyoto-dev.en | src-val.txt |
| valid_tgt  | kyoto-dev.ja | tgt-val.txt |

## 学習
基本的に行う処理は、英独翻訳と同じである。

```
!python OpenNMT-py/train.py -gpu_ranks 0 --train_steps 10000 -save_model '/content/OpenNMT-py/kftt/'data-model -data '/content/OpenNMT-py/kftt/'demo
```
以下に、英和翻訳の学習の値を比較してみる。(本来テストデータを比較すべきであるが、暫定的に学習データのまま)
明らかに、同じステップ数では精度が上がらない(難しい言語である)ことがわかる。

```
[2019-03-27 16:47:41,631 INFO] Step 10000/10000; acc:  45.61; ppl: 22.33; xent: 3.11; lr: 1.00000; 3301/3572 tok/s;   4007 sec
```
10000ステップ学習後の値は以下の通りである。英和だと更なる学習が必要であることがわかる。

|パラメータ　　　　 | KFTTデータ    | OpenMNT-py英独データ | 
|:-----------:|:---------------:|:---------:|
| acc  | 45.61 | 74.59|
| ppl  | 22.33 | 2.75|
| xent  | 3.11 | 1.01|

## 翻訳
```
!python OpenNMT-py/translate.py -model '/content/OpenNMT-py/kftt/'data-model_step_10000.pt -src '/content/OpenNMT-py/kftt/'kyoto-test.en -output pred.txt -replace_unk -verbose
```

# 最後に
ほかにもいろいろな機能がある。たとえば、REST APIサーバ(server.py)とか、TensorBoardへの出力が出来る。さらに精度を上げる方法について。ご存知の通り、Google Colabでは12時間でインスタンスは止まるが、チェックポイント機能(5000エポックで自動的に保存される)を使いgoogle drive等に保存しながら、繰り返し実行し、精度を上げることが出来る。とはいえ、Volta等高性能GPUを使うなどを考えたほうが良いとおもう。

# 参考資料
+ OpenNMT
   +  [フォーラム](http://forum.opennmt.net/)　(OpenNMTに関する質問等はここに投げる。)
   + (論文)[OpenNMT: Open-Source Toolkit for Neural Machine Translation](OpenNMT: Open-Source Toolkit for Neural Machine Translation)
   + OpenNMT-py
      + [OpenNMT-py](http://opennmt.net/OpenNMT-py/)
      + [Quick Start](https://github.com/OpenNMT/OpenNMT-py/blob/master/README.md#quickstart)
      + [OpenNMT-py/onmt/opts.py](https://github.com/OpenNMT/OpenNMT-py/blob/master/onmt/opts.py)　各種スクリプトの引数を記載しているコード
      + [preprocess.py](https://github.com/OpenNMT/OpenNMT-py/blob/0.9.1/preprocess.py#L214)
          + [main()](https://github.com/OpenNMT/OpenNMT-py/blob/0.9.1/preprocess.py#L167)
      + [train.py](https://github.com/OpenNMT/OpenNMT-py/blob/0.9.1/train.py#L196)
          + [main()](https://github.com/OpenNMT/OpenNMT-py/blob/0.9.1/train.py#L20)
              + [onmt.single_main.main](https://github.com/OpenNMT/OpenNMT-py/blob/0.9.1/onmt/train_single.py#L42)
                 + [build_model](https://github.com/OpenNMT/OpenNMT-py/blob/0.9.1/onmt/model_builder.py#L221)(モデル生成器)
                     + [build_base_model](https://github.com/OpenNMT/OpenNMT-py/blob/0.9.1/onmt/model_builder.py#L107) (Generator等生成)
                         + [build_encoder](https://github.com/OpenNMT/OpenNMT-py/blob/0.9.1/onmt/model_builder.py#L69)
                         + [str2enc](https://github.com/OpenNMT/OpenNMT-py/blob/0.9.1/onmt/encoders/__init__.py#L11)(TransformerEncoder等のへのマッピング)
                             + [TransformerEncoder](https://github.com/OpenNMT/OpenNMT-py/blob/0.9.1/onmt/encoders/transformer.py#L59)
                                 + [TransformerEncoderLayer](https://github.com/OpenNMT/OpenNMT-py/blob/0.9.1/onmt/encoders/transformer.py#L12) 
                                 + [MultiHeadedAttention](https://github.com/OpenNMT/OpenNMT-py/blob/0.9.1/onmt/modules/multi_headed_attn.py#L11)
                 + [build_dataset_iter()](https://github.com/OpenNMT/OpenNMT-py/blob/0.9.1/onmt/inputters/inputter.py#L766)(データセットのローダ)
                     + [DatasetLazyIter](https://github.com/OpenNMT/OpenNMT-py/blob/0.9.1/onmt/inputters/inputter.py#L664)
                 + [build_trainer()](https://github.com/OpenNMT/OpenNMT-py/blob/0.9.1/onmt/trainer.py#L20)(学習用コード)
                     + [Trainer](https://github.com/OpenNMT/OpenNMT-py/blob/0.9.1/onmt/trainer.py#L77)
                     + [train()](https://github.com/OpenNMT/OpenNMT-py/blob/0.9.1/onmt/trainer.py#L192)
                         + [_gradient_accumulation()](https://github.com/OpenNMT/OpenNMT-py/blob/0.9.1/onmt/trainer.py#L329)
                             + [build_loss_compute](https://github.com/OpenNMT/OpenNMT-py/blob/0.9.1/onmt/utils/loss.py#L15) (loss_computeの本体)
                             + [NMTLossCompute(LossComputeBase)](https://github.com/OpenNMT/OpenNMT-py/blob/0.9.1/onmt/utils/loss.py#L216)
+ コーパス
   + [日本語対訳データ](http://www.phontron.com/japanese-translation-data.php?lang=ja)
+ その他
   + [OpenNMT(Google Colab勉強会@京都情報大学院大学)2018/11作成](https://colab.research.google.com/drive/1B8RATKNxUnXVl8XScSstNe1R1hZb2SGn)
      + 英日翻訳を[KFTTコーパス](http://www.phontron.com/kftt/#dataonly)を使って行っている。
   + [Diving into OpenNMT 2019/1作成](https://python.gotrained.com/google-colab-gpu-deep-learning/#Diving_into_OpenNMT)