言語処理100本ノック2020 (95. サブワード化)

# はじめに
本記事は言語処理100本ノックの解説です。
100本のノックを全てこなした記録をQiitaに残します。
使用言語はPythonです。

https://nlp100.github.io/2020/ja/

今回は第10章: 機械翻訳(95. サブワード化)の解答例をご紹介します。
問題95は文章をサブワード化して問題91~94を再度実施するため、この問題単独で記事にしました。

# 95. サブワード化
>トークンの単位を単語や形態素からサブワードに変更し，91-94の実験を再度実施せよ．

**準備**
データセットをサブワードするためにsubword-nmtを入れます。
```text:コマンド
$ pip install subword-nmt
```
**コード**
```python:コード
!subword-nmt learn-bpe -s 10000 < "[PATH]/kftt-data-1.0/sorce/kyoto-train.en"> "[PATH]/kftt-data-1.0/codec/en_codec.txt"
!subword-nmt learn-bpe -s 10000 < "[PATH]/kftt-data-1.0/sorce/kyoto-train.ja"> "[PATH]/kftt-data-1.0/codec/ja_codec.txt"

def Subword(name):
  !subword-nmt apply-bpe -c "[PATH]/kftt-data-1.0/codec/en_codec.txt" < "[PATH]/kftt-data-1.0/sorce/kyoto-"$name".en" > "[PATH]/kftt-data-1.0/codec/"$name".en"
  !subword-nmt apply-bpe -c "[PATH]/kftt-data-1.0/codec/ja_codec.txt" < "[PATH]/kftt-data-1.0/sorce/kyoto-"$name".ja" > "[PATH]/kftt-data-1.0/codec/"$name".ja"

Subword("train")
Subword("test")
Subword("dev")
```

**コメント**
OSコマンドを関数に組み込んでtrain, test, devのデータを作ります。そして、問題90-94を再び実施します。

# 95-90. データの準備
>機械翻訳のデータセットをダウンロードせよ．訓練データ，開発データ，評価データを整形し，必要に応じてトークン化などの前処理を行うこと．ただし，この段階ではトークンの単位として形態素（日本語）および単語（英語）を採用せよ．

```text:コマンド
$ fairseq-preprocess -s ja -t en \
--trainpref  "[PATH]/kftt-data-1.0/codec/train" \
--validpref "[PATH]/kftt-data-1.0/codec/dev" \
--testpref "[PATH]/kftt-data-1.0/codec/test" \
--destdir "[PATH]/kftt-data-1.0/processed-95/" \
--task translation \
--thresholdsrc 5 \
--thresholdtgt 5
```
**コメント**
問題90~95の記事と全く同じです。

# 95-91. 機械翻訳モデルの訓練
>90で準備したデータを用いて，ニューラル機械翻訳のモデルを学習せよ（ニューラルネットワークのモデルはTransformerやLSTMなど適当に選んでよい）．

```text:コマンド
$ fairseq-train "[PATH]/kftt-data-1.0/processed-95/" \
  --task translation \
  --arch transformer \
  --tensorboard-logdir "logs" \
  --source-lang ja --target-lang en \
  --max-epoch 100 \
  --lr 1e-5 \
  --batch-size 32 \
  --optimizer adam \
  --save-interval 3 \
  --save-dir "[PATH]/kftt-data-1.0/model-95/" \
  --restore-file  "[PATH]/kftt-data-1.0/model-95/checkpoint_last.pt" \
| tee -a "[PATH]/kftt-data-1.0/log-95/train.log"
```

**コメント**
また、学習していきます。

# 95-92. 機械翻訳モデルの適用
>91で学習したニューラル機械翻訳モデルを用い，与えられた（任意の）日本語の文を英語に翻訳するプログラムを実装せよ．

```text:コマンド
$ fairseq-generate "[PATH]/kftt-data-1.0/processed-95/" \
--path "[PATH]/kftt-data-1.0/model-95/checkpoint_best.pt" \
--task translation \
--gen-subset test \
| tee >(grep "^H" | cut -f3 > "[PATH]/kftt-data-1.0/processed-95/test-transform.txt")\
| grep "^T" | cut -f2 >  "[PATH]/kftt-data-1.0/processed-95/test-true.txt"
```
**出力結果**
```text:予測
Some arg@@ ue that this caused the er@@ rors in their ages .
Emperor Tenchi ( Ten@@ ji )
1879 : Osaka Techn@@ ical College
" Ten@@ zo K@@ yok@@ un "
" Fu@@ sh@@ uku Han@@ ho "
```
```text:正解
Thus , er@@ rors were wrong .
Emperor Ten@@ ji ( also pronounced Ten@@ ji )
In 1879 , he became Osaka Sen@@ mon Gakko ( Osaka V@@ oc@@ ational School ) .
" Ten@@ z@@ oku-@@ kyo Sutra "
" H@@ ow to eat g@@ our@@ d rice "
```
これだと分からないので、サブワードから元の表記に戻します。
```text:コマンド
$ cat "[PATH]/kftt-data-1.0/processed-95/test-transform.txt" | sed -r 's/(@@ )|(@@ ?$)//g' > "[PATH]/kftt-data-1.0/processed-95/test-transform-decoded.txt"
$ cat "[PATH]/kftt-data-1.0/processed-95/test-true.txt" | sed -r 's/(@@ )|(@@ ?$)//g' > "[PATH]/kftt-data-1.0/processed-95/test-true-decoded.txt"
```
改めて翻訳結果と正解の文章は以下のようになります。

```text:予測
Thus , errors were wrong .
Emperor Tenji ( also pronounced Tenji )
In 1879 , he became Osaka Senmon Gakko ( Osaka Vocational School ) .
" Tenzoku-kyo Sutra "
" How to eat gourd rice "
```
```text:正解
Some argue that this caused the errors in their ages .
Emperor Tenchi ( Tenji )
1879 : Osaka Technical College
" Tenzo Kyokun "
" Fushuku Hanho "
```
**コメント**
例が悪いのかもですが、できている感じがしないですね。

# 95-93. BLEUスコアの計測
>91で学習したニューラル機械翻訳モデルの品質を調べるため，評価データにおけるBLEUスコアを測定せよ．

**サブワードで評価**
```text:コマンド
$ fairseq-score --sys  "[PATH]/kftt-data-1.0/processed-95/test-transform.txt"\
--ref  "[PATH]/kftt-data-1.0/processed-95/test-true.txt"
```
**結果**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3579871/e183c171-76c0-81e7-ef08-6a4206642c86.png)

**元の文章に戻して評価**
```text:コマンド
$ fairseq-score --sys  "[PATH]/kftt-data-1.0/processed-95/test-transform-decoded.txt"\
--ref  "[PATH]/kftt-data-1.0/processed-95/test-true-decoded.txt"
```
**結果**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3579871/89242d29-eb32-7866-d204-ef9629bb2176.png)

**コメント**
サブワードのままだと23.27、元の文章に戻すと20.94でした。サブワード化すると語彙を少なくできるため評価値が高くなるのかなと。サブワード化せずに学習、評価した結果が前記事で18.69でしたので、サブワード化は有効な手法だと言えますね。

# 95-94. ビーム探索
>トークンの単位を単語や形態素からサブワードに変更し，91-94の実験を再度実施せよ．

```Python:コード
from matplotlib import pyplot
beam_list = []
for i in range(1, 100):
  try:
    beam_temp = !fairseq-generate "[PATH]/kftt-data-1.0/processed-95/" \
      --path "[PATH]/kftt-data-1.0/model-95/checkpoint_best.pt" \
      --task translation \
      --gen-subset test \
      --beam $i  | grep '^Generate'
    beam_list.append(float(beam_temp[-1].split(",")[0].split(" ")[-1]))
  except:
    break

pyplot.plot(beam_list)
```
**出力結果**
![download.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3579871/4ab17253-76a6-11ea-ee90-b4c1a680eb12.png)

**コメント**
だいたい23.25~23.50の間でまとまっていますね。

# 他章の解答例

https://qiita.com/Dukapan100knock/items/d418f2aa37b2b3d16428