【自然言語処理100本ノック 2020】99. 翻訳サーバの構築【解答】【世界最速公開】【全米号泣】

#概要

　[物工/計数 Advent Calendar 2020](https://adventar.org/calendars/4940)、4コマ漫画に挟まれた12月24日（クリスマスイブ！）の記事です。

　自然言語処理の問題集「[自然言語処理100本ノック 2020 第10章: 機械翻訳](https://nlp100.github.io/ja/ch10.html)」の問99を解き、深層学習モデルによる翻訳のデモシステムを実装します。98までをやっていなくても英仏翻訳が実装できます。多分世界最速公開です。

#イントロ

　[自然言語処理100本ノック 2020](https://nlp100.github.io/ja/) は正規表現といった基礎から近年の深層学習のトレンドに至るまで、自然言語処理のスキルを楽しく<sup>[[要出典](https://ja.wikipedia.org/wiki/Wikipedia:「要出典」をクリックされた方へ)]</sup>身に付けることができる問題集です。

　解く上でMeCabに始まり様々なライブラリを使う必要があり、その環境構築をまともにやるのは非常に面倒なので、`!pip`で簡単に環境を作れるGoogle Colabを利用する人も多いはずです。僕もその一人です。ただし、最後の問99[^1]で大きな問題に直面することになります。

><strong>99. 翻訳サーバの構築</strong>
　ユーザが翻訳したい文を入力すると，その翻訳結果がウェブブラウザ上で表示されるデモシステムを構築せよ．

###　んなことcolabでできるか！

と、思うじゃないですか。

###　実は、できます！！！！！！！！！！

　そう、できます。恒久的なサーバを立てることはできませんが、デモシステムを作るだけならGoogle Colabで完結します。ということで、やっていきたいと思います。

　以下ではpytorchを使い、モデルを読み込んだ状態でスタートします。とりあえずやってみたい、という人は、fairseqを使い、公式の[チュートリアル](https://fairseq.readthedocs.io/en/latest/getting_started.html)にあるように、

```
!curl https://dl.fbaipublicfiles.com/fairseq/models/wmt14.v2.en-fr.fconv-py.tar.bz2 | tar xvjf -
```

で訓練済みの英仏翻訳モデルを落とします。

　これを

```python
!pip install fairseq

from fairseq.models.transformer import TransformerModel

model = TransformerModel.from_pretrained("wmt14.en-fr.fconv-py", "model.pt", "wmt14.en-fr.fconv-py")
model.eval()
```
とすれば`model`には推論モードの英仏翻訳の訓練済みモデルが入っていることになります。そんな感じで、`model`には訓練済みモデルを読み込んでおいてください。



#いざ、ラスボス討伐！

　`flask`というフォルダを作って以下ではそこにいることにします。（`!cd (移動したいフォルダ)`で移動できます。）

　まず、`flask-ngrok`ライブラリをインストールします。flaskはPythonで簡単なウェブアプリを作れるもので、ngrokはローカルのサーバー上のサイトを安全に全世界に公開できるものです。これらを使うと、自分のトレーニングした翻訳モデルを使った翻訳サイトを公開することができます。（ただし、colab上で走らせている時間だけ。）

```python
!pip install flask-ngrok

from flask_ngrok import run_with_ngrok
from flask import Flask, render_template, request
```
　必要なものを適宜読み込みます。

　`templates`というフォルダを`flask`の中に作り、さらに`templates`の中に以下が書かれたファイル`index.html`を作ります。

```html:index.html
<html>
<body>
	<h1>{{ title }}</h1>
	<p>{{ message }}</p>
	<form action="/" method="POST" enctype="multipart/form-data">
		<div>
			<label for="name">Text：</label>
			<input type="text" id="name" name="name" placeholder=" ">	
		</div>
		<div>
			<input type="submit" value="translate">
		</div>
	</form>
</body>
</html>
```
　フォルダとファイルの関係は、以下のようになっています。

```
flask/   <- 今いるところ
|--templates/
　　|--index.html
```
　もう少しちゃんと書けば、もっとちゃんとしたデモシステムができますが、今回は最も簡単なものにします。

　さて、最後に以下をcolabで実行します。

```python
app = Flask(__name__,template_folder="(前略)/templates")
run_with_ngrok(app)

@app.route("/", methods=["GET"])
def get():
	return render_template("index.html", \
		title = "En-Fr Translation (input)", \
		message = "Please enter the text you want to translate here.")
	
@app.route("/", methods=["POST"])
def post():
	text = request.form["name"]
	return render_template("index.html", \
		title = "En-Fr Translation (output)", \
"""
【翻訳パート開始】
"""
		output = model.translate(text, beam=5)
"""
【翻訳パート終了】
"""
		message = text+"　　　　　　　→　"+output)

app.run()
```
　日本語の場合は入力をトークナイズしたりする必要があるので、構築したモデルによって異なりますが、もう少し手間がかかるはずです。ただ、翻訳パートは普通にpythonを書いて良いので、大した負担にはならないでしょう。

　出てきたurlをクリックすると、こんな感じで表示されます。
![スクリーンショット 2020-12-20 0.45.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/966578/570dfc20-d9eb-24ba-a40c-8e2cad210b49.png)

これにて自然言語処理100本ノックは終了です。メリークリスマス！


[^1]: 0-basedなので。

###参考文献

・fairseqで自分でトレーニングしたTransformerモデルをロードする
　https://qiita.com/maechanneler/items/fc3ef22d5b89396ddbcd
・Flask on Google Colab
　https://medium.com/@kshitijvijay271199/flask-on-google-colab-f6525986797b
・【Flask】formからサーバーサイドにテキストや各種コントロール、ファイルを渡す
　https://tanuhack.com/flask-client2server/
・flaskのパスを指定する
　https://qiita.com/mink0212/items/a4eb875f19b0e47718d3