Re:ゼロから始めるML生活

どちらかといえばエミリア派です

自然言語処理について勉強してみた(その5:Seq2Seq・Attention)

この前はLSTMについて勉強してみました。

tsunotsuno.hatenablog.com

今回はもうちょっと進んで、seq2seqとAttentionを見ていきます。

今回も参考にしたのはこちらの本です。

ゼロから作るDeep Learning ? ―自然言語処理編

ゼロから作るDeep Learning ? ―自然言語処理編

今回もPytorchのチュートリアルがあるので、実装はPytorchを使って見ていきたいと思います。

pytorch.org

この辺になるともう結構難しい内容ですが、これでこの本も終わりなので、頑張ってやっていきたいと思います。

seq2seq

この前は国籍と頭文字を指定して名前を生成していました。

tsunotsuno.hatenablog.com

理屈上は文字を結合して単語(名前)を生成するのも、単語を結合して文章を生成するのもそんなに違いません。 (使える空間が26文字なのか英単語分だけあるかは大きく違いますが)

時系列データを生成する問題について、今の所有効とされている手法がseq2seqです。 seq2seqの概念図は下記のようになります。

f:id:nogawanogawa:20190211140142j:plain:w350

入力の時系列データに対してEncoderが有効な情報を詰め込んだ符号へと変換します。 この符号を使用してDecoderが再度変換を行い、出力となる時系列データを生成します。

Auto Encoderの時系列データ版みたいですね。

最適化

生成した時系列データの精度を向上させるためには、最適化が必要になってきます。

Reverse

バカ正直にやってもいいんですが、先行研究でやられている最適化の一つにReverseがあります。 やることは簡単で、入力の時系列のデータの並びを逆転させます。

f:id:nogawanogawa:20190211151258j:plain:w200

こうすると、Decoderが生成する時系列データの最初のデータに対して、入力データの最初のデータが一番影響が大きく反映することができます。 もともとこんな感じで、影響度が減衰しがちだったので、入力の順番を逆にすることで影響が残るようにします。

f:id:nogawanogawa:20190211151244j:plain:w500

細かい理屈いろいろあるでしょうが、直感的な解釈としてはこれで問題ないかと思います。

Peeky

現状だとDecoderが時系列データの後ろの要素を生成しようとすればするほど、Encoderの出力の影響が小さくなってしまいます。

f:id:nogawanogawa:20190211151317j:plain:w500

そこで、Encoderの入力をDecoderすべての要素の生成に使用するPeekyというやり方も有効だそうです。

f:id:nogawanogawa:20190211150621j:plain:w500

これによって、(直感的にはですが)入力の時系列データの影響が出力まできちんと伝わるようになります。

Attention

seq2seqを更に改良していきます。 今のseq2seqでは、時系列データの長さに関係なく、一定の長さのベクトルへ変換されます。 一方で、文章の翻訳を考えると、入力の時系列データ各要素と出力の時系列データの各要素には、並び順こそ違えど、関連性のある"ペア"があることが想定されます。 つまり、いろんな要素がごちゃまぜになったベクトルがEncoderからDecoderに渡されることになり、本来あるはずのペアを有効に活用することができません。

f:id:nogawanogawa:20190211184143j:plain:w350

このような予想に対し、Attentionレイヤを使うことで改善していきます。

Encoderの変更

Encoderは、こんな感じで各要素ごとの出力を最後に一つにまとめ、行列を出力するように書き換えます。

f:id:nogawanogawa:20190211184159j:plain:w500

こうすることで、各要素の出力を長さを保ったままDecoderにわたすことが出来るようになります。

Decoderの変更

Decoderの方も、Encoderに合わせて書き換える必要があります。 具体的には、こんな感じにAttentionレイヤを付加することで行列の要素をDecoderの出力に反映するようにします。

f:id:nogawanogawa:20190211184216j:plain:w500

ここで、Attentionレイヤの中ではEncoderで作られた各要素ごとの出力に対して、行列積によって重み付けをしています。 これによって、ペアになる要素に対して正しく重み付けされるように学習がなされていきます。

f:id:nogawanogawa:20190211184230j:plain:w500

このようにして、各要素の出力を維持したまま、Encoderまで伝播するようにすることで、より精度の高い変換が可能になるようです。

Pytorchでやってみる

ちょうどいいチュートリアルがあるので、この辺やってみます。

pytorch.org

注意

NNの学習にはMacBookで大体40分って書いてありますが、私の環境では3時間半くらいかかりました。 片手間でいろいろバックグラウンドで動かしていたからなのか、想定よりだいぶ時間かかったのでそのへんはご注意ください。

チュートリアルを眺める

やりたいこととしてはこんな感じです。

f:id:nogawanogawa:20190211132759j:plain:w500

何が良くないって、フランス語がわからないから直感的に良し悪しが評価できない、、、 まあいいか。。。

肝になるコードはこの辺でしょうか。

そんでもって実行するとこんな感じです。

input = elle a cinq ans de moins que moi .
output = she is five years younger than me . <EOS>
input = elle est trop petit .
output = she s too short . <EOS>
input = je ne crains pas de mourir .
output = i m not scared to die . <EOS>
input = c est un jeune directeur plein de talent .
output = he s a talented young talented . <EOS>

ちゃんと出てそうですね。←適当 すんません、変換できてるのかよくわかんないっす。。。 とりあえず、英語の方はなんとなく読めますね。

感想

一通り自然言語処理関係のNNは勉強できましたし、本に載ってない形の実装も勉強できたので良かったのではないでしょうか。 総じて、参考にさせていただいたこちらの本がわかりやすかったです。図が多いのでイメージも残りますし。 改めて、いい本だと思いました。

ゼロから作るDeep Learning ? ―自然言語処理編

ゼロから作るDeep Learning ? ―自然言語処理編

とは言うものの、自然言語処理はNNに閉じた話ではなくもっと広い分野なので、今度は一般的な自然言語処理の勉強をしていこうかと思います。