リカレントニューラルネット(RNN)で翻訳AIを自作する【作って理解するディープラーニング#10】

リカレントニューラルネット(RNN)で翻訳AIを自作する【作って理解するディープラーニング#10】

こんにちは、えびかずきです。

今回はリカレントニューラルネット(RNN)で翻訳AIを自作する方法についてです。

ディープラーニングの連載も今回で最終回。

気合入れていきましょう!

過去の記事はこちらから:
https://ebi-works.com/deeplearning-0/

開発環境

OS:macOS Catalina ver10.15.2

使用したフレームワーク:
TensorFlow2.0

tensorflowは下のようにpipでインストールできます。

$ pip install tensorflow 

使用した外部ライブラリ:
numpy1.18.1
matplotlib3.0.3
scikit-learn

エディタ:jupyter notebook

ソースコード

github/ebikazuki/deeplearning

本記事では、Githubリポジトリ内の「#10」のフォルダを使用します。

コードは「詳解ディープラーニング第2版」を改変して活用させていただきました。

RNNとは

リカレントニューラルネット(RNN)は再帰構造を含むネットワークです。

主に時系列データの認識に使われます。


例えば時間によって変わる天気の情報であるとか、

前後の繋がりがある自然言語の認識などに使われたりします。

RNNの構造

リカレントは再帰とか回帰という意味です。

したがってRNNはその名の通り、

情報を途中で取り出してもう一度上流に流し込む、すなわち再帰を行います。


一方でこれまでに学んだように、

全てのデータを通常のネットワークにぶちこむという方法もありますが、

時間的なつながりのあるデータを処理する場合には、効率が悪いです。


そこでRNNの出番です。

RNNの構造は下の図をみるとわかりやすいと思います。


この図はRNNの構造を簡単に示しています。

入力データとして時間的なつながりのあるデータ「X」を、

ニューラルネットで学習させています。

流れとしては、

①最初の時間のデータであるx(t=1)をモデルに入力する。

②活性化関数を介して隠れ層から出てきた情報を上流にいったん戻す。

③戻ってきた情報と、次のx(t=2)の2つをモデルへ入力する。

となります。

このようにしてぐるぐると情報を再帰して、

最後の時間のデータx(t=n)までいったら、出力yを得るという流れになります。

この方法を使えば、

データの時間的なつながりを考慮しつつ、大幅に重みのパラメータを削減できます。

Sin波を学習させてみた例

ごちゃごちゃと説明するばかりでは始まらないので、実際にRNNを試してみましょう!

今回は「TensorFlow」というニューラルネットワークを構築できるフレームワークを使用して実装していきたいと思います。

TesorFlowを使うと、後で出てくるLSTMブロックが簡単に実装できて便利です!

コード:sin-wave.ipynb(Githubリポジトリの「#10」というフォルダ内)
モデル:入力(25)-隠れ層(30)-出力(1)
活性化関数:tanh

RNNでは再帰による繰り返しで勾配が消失しやすいですが、

比較的それを低減できるtanh関数が活性化関数としてよく使われます。


学習結果を可視化したものが下の図になります。

薄い線がSin関数の真値を示していて、濃い線が学習したモデルによって導いた値です。


「入力-隠れ層-出力」という非常に単純なモデルでありながら、

しっかりとSin波を学習できていることがわかります。


次は、隠れ層のユニット数(次元)をもっと減らして実験してみましょう。

隠れ層は入力を抽象的な情報に変換する役割を果たします。

単純な特徴量を想像すると、

①直前の時間のxの大きさ
②プラス方向への復元力
③マイナス方向への復元力

これら3つくらいがあればSin波のような振動を表現できそうだと想像することができます。

ということで隠れ層のユニットを30から3に変更してみましょう。

コード:sin-wave.ipynb(Githubリポジトリの「#10」というフォルダ内)
モデル:入力(25)-隠れ層(3)-出力(1)
活性化関数:tanh

結果を図示したものが下図です。


綺麗なSin波とはいきませんが、

たった3ユニットだけでも振動する波を学習することができました。

LSTMについて

続いて、RNNを実践で使うために必要不可欠なLSTMについて説明します。

LSTM(Long Short Term Memory)は、長期と短期の時間記憶を効果的に活用するためのテクニックです。

実は、上で紹介した単純なRNNでは繰り返し再帰が行われることで、

勾配が消失してしまうという問題があります。

これでは過去の情報に由来する最適化が実行しにくいという問題があります。

そこでこれを解決するのが、LSTMです。

LSTMの構造

LSTMは隠れ層のユニットとして、LSTMブロックという形でRNNモデルに組み込まれます。

その構造を示したものが、下図になります。

LSTMブロックの中身は複雑で、ぱっと見では意味不明だと思いますが、

順を追って一つずつ構造を説明していきたいと思います!

CEC(Constant Error Carousel )

CECは直訳すると「一定の誤差がくるくる回る」というような意味で、

古い時間の勾配が消えてしまわないように情報をとどめておく役割があります。


RNNの通常の再帰経路とは別に設置します。

LSTMブロックの中でCECとして働く部位は、下図中薄い青で示した領域になります。


LSTMブロックに入り活性化関数を通過したデータをそのまま一箇所に保存し、

次の時間の入力データが入ってきたときに、単純に加算してやります。


こうしてやることで、昔の誤差を保存しておくことができます。

入・出力ゲート

次に入・出力ゲートです。

CECで誤差を記憶できるようになったのは良いのですが、実は未解決な問題があります。

それは重み衝突という現象です。

重み衝突とは、時間依存性の強い信号と弱い信号とで重み調整の方向性が相殺してしまう現象です。

これは時間違いのデータが、同じ重みで繋がったネットワーク内をぐるぐる回っている限り避けられない問題です。

それを解決するのが入・出力ゲートです。


「時系列的な傾向がなさそうな入力や出力は無視する」ためのゲートを設置することで効率よく重み調整を実施できるようになります。

このようにゲートを用意して重みパラメータを準備して学習を実行することで、あとはモデルが勝手にゲートの開け閉めを最適化してくれます。

忘却ゲート

続いては忘却ゲートです。

忘却ゲートはCECに溜め込まれた過去情報を破棄してリセットする役割があります。

例えば、時間的なパターンが劇的に変化して過去の情報をいったん消去した方が良いような場合に、ゲートを閉めてリセットしてやります。


図のように各ユニットと繋いでおくことで、

ゲートを閉めるべきタイミングをモデルが勝手に判断できるように準備しておきます。

覗き穴結合

最後に覗き穴結合です。

覗き穴結合は、ゲート制御の結合を増やしてモデルの柔軟性を高める役割があります。


ゲート開閉の判断にCECの値も活用することで、より柔軟なモデルを作ることが可能になります。

翻訳AIの実装

それではいよいよ翻訳AIの実装に入っていきましょう!

RNN Encoder-Decoderの構造

今回作成する翻訳AIは、下のようなEncoder-Decoderを組み合わせたモデルで実装します。

コード:
encoder-decoder.ipynb(Githubリポジトリの「#10」というフォルダ内)
モデル:
Encoder:入力-隠れ層(128unit)-出力
Decoder:入力-隠れ層(128unit)-出力

学習モデルのアウトプットが以下になります。


学習の進んでいない1エポック目では、全然翻訳ができていませんが、

30エポックまでいくと、ある程度意味を理解しているようなアウトプットになっています。

google翻訳のような実用的なプログラムを作るのは難しいですが、自作でもある程度の性能を実感することができました。

1エポック目の学習進捗:

epoch: 1
loss: 3.536, val_loss: 3.59
> at the end of a working day everybody is in a hurry to get home .
= 一 日 の 仕事 が 終わ る と 皆 家路 を 急 ぐ 。 </s>
< 彼 は 彼 は 彼 は 彼 は は は は は は は を を い 。 。 。

> i am as much in the wrong as you are about not writing before this .
= ご 無 沙汰 は お 互い さま で す 。 </s>
< 彼 は 彼 は 彼 は 彼 は は は は は は は は を い は い 。

> my father boasts of the fact that he has never had a traffic accident .
= 父 は 車 の 無 事故 を 自慢 し て い る 。 </s>
< 彼 は 彼 は 彼 は 彼 は は は は は は は は を い は い 。

> mr smith and i have been acquainted with each other for a long time .
= スミス 私 と は 長 い 間 の 知り合い で す 。 </s>
< 彼 は 彼 は 彼 は 彼 は は は は は は は は を い は い 。

> it is hard for an old man to change his way of thinking .
= 老人 が 考え を 変え る の は 難し い 。 </s>
< 彼 は 彼 は 彼 は 彼 は は は は は は を を い 。 。 。 。

> as soon as you get the wall painted , you can go home .
= 壁 を <unk> 終え たら 、 すぐ に 帰宅 し て も い い よ 。 </s>
< 彼 は 彼 は 彼 は は は は は は は を を い 。 。 。 。 。

> i haven 't made up my mind <unk> enough to agree with you .
= まだ 決心 が つ か な い の で 同意 でき ま せ ん 。 </s>
< 彼 は 彼 は 彼 は 彼 は は は は は は を を い 。 。 。 。

> if you are going to have a party , please count me in .
= もし パーティー を 開 く なら 私 も 仲間 に 入れ て 下さ い 。 </s>
< 彼 は 彼 は 彼 は は は は は は は を を い 。 。 。 。 。

> when he got into trouble , he turned to his parents for help .
= 困難 に 、 彼 は 両親 に 助け を 求め た 。 </s>
< 彼 は 彼 は 彼 は 彼 は は は は は は を を い い 。 。 。

> i sometimes look back on the good days i had in london .
= ロンドン で 過ご し た 楽し い 日々 の こと を 時々 思い出 す 。 </s>
< 彼 は 彼 は 彼 は は は は は は は を を い 。 。 。 。 。

30エポック目の学習進捗:

--------------------
epoch: 30 loss: 2.064, val_loss: 2.8 > she doesn 't have any <unk> . = 彼女 に は 敵 は い な い 。 </s> < 彼女 は は 少し も 持 っ て い な い 。 </s> </s> </s> </s> </s> </s> </s> </s> > she played the piano well enough . = 彼女 は かなり うま く ピアノ を 弾 い た 。 </s> < 彼女 は ピアノ で ピアノ を 弾 き ま し た 。 </s> </s> </s> </s> </s> </s> </s> </s> > i don 't like sad movies . = 私 は 悲し い 映画 は 嫌い だ 。 </s> < 私 は 映画 を 見 な い い い 。 </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> > i 'll be here by monday . = 月曜 日 まで に ここ に 来 て い ま す 。 </s> < 私 は ここ に に に に に い 。 。 。 </s> </s> </s> </s> </s> </s> </s> </s> > how beautiful the rising sun is ! = 朝日 は なん と 美し い の だ ろ う 。 </s> < 世界 は 元気 に な な 。 。 </s> 。 </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> > nothing is so precious as time . = 時間 ほど 貴重 な 物 は な い 。 </s> < 時間 は 時間 も 何 も な な 。 い 。 </s> </s> </s> </s> </s> </s> </s> </s> </s> > she expressed satisfaction with her life . = 彼女 は 自分 人生 に 満足 し て い る と い っ た 。 </s> < 彼女 は 自分 の 人生 に 満足 し て い た 。 。 た 。 </s> </s> </s> </s> </s> > they won by force of <unk> . = 彼 ら は 数 の 力 で 勝 っ た 。 </s> < 彼 ら は みんな に 勝 っ て い る 。 </s> </s> </s> </s> </s> </s> </s> </s> </s> > he always takes sides with her . = 彼 は いつ も 彼女 の 見 方 を する 。 </s> < 彼 は いつ も 彼女 の 味方 を する 。 </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> > what a bad boy he is ! = 彼 は なん と 悪 い 子 な の だ ろ う 。 </s> < 彼 は なんて 悪 い い で の で 。 う 。 </s> </s> </s> </s> </s> </s> </s> </s>

まとめ

ディープラーニングの連載も今回で最終回。

今回はRNNで翻訳AIを自作する方法について説明しました。

特にLSTMブロックの働きや役割を理解するのが難しかったのではないかと思います。

LSTMは簡単にいってしまえば、RNNを効果的に活用するための部分的な工夫です。

この記事だけで理解できなかったという方は、下の参考文献やネット上の

自分が調べた限りちゃんと構造を説明しているのは英文の記事が参考になりました。

日本語のサイトでもいくつかLSTMを説明する記事はありましたが、表面的な説明のものが多く、本質的な理解に繋がるものは少ないように感じました。


それでは今回は以上です。

最後まで読んでいただいてありがとうございました!

参考文献

この記事を執筆するにあたって、以下の書籍を参考にさせていただきました。

詳解 ディープラーニング第2版

機械学習カテゴリの最新記事