RNNを使った言語モデルを設計してみた―RNNで文章生成〈2〉

はじめに

前回の記事↓

RNNで太宰治風の文章を自動生成させたい―RNNで文章生成〈1〉

 第2回の今回は、僕が設計したRNN(リカレントニューラルネットワーク)を使った言語モデルの構造について説明していこうと思います。といっても簡単な説明のみなので、わからない用語とかがあったらググってください。僕よりももっとわかりやすく説明しているサイトがあると思うので...。他にもいろいろと問題のある部分や間違いがあるかもしれませんが、その辺は個人でやってるので大目に見てください。

こちらがこれから作る言語モデルの構造を図にまとめたものです。

ネットワークの構造図

モデルの設計には下記の本やサイトを参考にして作りました。

ゼロから作るDeep Learning ❷ ―自然言語処理編(Amazon) Text generation using a RNN with eager execution(Google Seedbank)

ちなみに図の作成にはDraw.ioというサイトを使わせていただきました。上記のサイトに使われているような図を作りたいと思って探していると、このサイトを見つけました。すごく使いやすいです。

Drow.io

では、学習の流れに沿ってモデルの概要を説明していきます。

入力データの前処理

 入力データには太宰治の小説を集めたコーパスを使いますが、そのままの形ではモデルに突っ込むことができません。そこで、文を単語ごとに分割し、さらに単語を単語IDに変換して扱いやすい形にします。単語単位での分割にはMeCabを使います。単語から単語IDの変換には辞書が必要になります。つまり、こんな風にそれぞれが対応しているものです。

これらを使うと一つの文章をIDの配列に変換することができます。

単語idへ変換

※動詞などの活用をどうするかは後述するword2vecの仕様に応じて決めるつもりです。

ここからバッチ数や時間の長さに従って整形すると、入力ベクトル(Input vector)が出来上がります。(ミニバッチ学習をするので全体としては行列になりますが)

入力

※バッチ数Nを2、時間の長さTを3とした場合。

Embedding Layer

 入力するベクトルが出来上がったら、次はそのベクトルをEmbedding Layerに突っ込みます。ここでは単語IDからその単語の分散表現(単語の意味をベクトルに落とし込んだもの、だと思う。いや、埋め込んだ(embedding)ものかな?)を抜き出します。これから作るモデルでは学習を速くするための転移学習として外部のword2vecをここで使わせてもらおうと思っています。

LSTM

 Embedding layerで抜き出したベクトル(Embedding)を次はLSTM層に時系列順に渡していきます。LSTMとはRNNの一種で、ゲート付きのRNNというRNNのグレードアップ版みたいなものです。ゲート付きRNNにはLSTMのほかにもGRUなどがありますが、今回は「ゼロから作るDeep Learning ❷」で詳しく解説されていたLSTMのほうを利用します。

 RNNは時系列順にデータを処理するのが特徴です。また、RNNは前の時刻の情報を後ろの時刻に渡していくことができます。この伝搬役を担うのがが図の中でh1、h2と示されている「隠れ状態」です。図の中には書いていませんが、さらにLSTMでは「記憶セル」というものが内部で時系列的に受け渡しされています。したがって、図のLSTM層では入力データが平行に分岐しているようになってますが、実際は左から右に列ごとに順々に進んでいくことになります。そうして最後の(T-1)列が出力するまで進めたら、おのおのの時刻の出力(Output1~(T-1))を一つにまとめます。

 ここでは学習をうまく進めるために、「ゼロから作るDeep Learning ❷」に書いてあった様々な学習テクニックを詰め込んでみました。主なものは4つあります。

 一つ目はLSTMの多層化です。図の通り、今回作るモデルにはLSTMが2つ重なっています。どのようなニューラルネットワークでも層を重ねるほど表現力が増すようです。欠点は計算コストがかかることです。層の数にも適切なものがあるそうですが、今回は計算量を少なくするために2層にとどめました。

 二つ目はDropoutです。ノードのいくつかランダムに選び、それらをわざと無効にすることで、過学習を避けることができるようです。中でもより効果の高い変分Dropoutを使います。

 三つ目は勾配クリッピングです。勾配爆発という勾配が大きくなりすぎる現象を防ぐためです。

 四つ目は重み共有です。これは次の項で述べます。

Dense Layer

 Affine layerやFully connected layerとも言ったりします。入力に重みをかけてバイアスを足して出力するだけの比較的単純な層です。ただしここで使う重みはEmbedding layerで使用した行列を転置したものです。こうした重み共有(weight tying)を利用することによって、学習パラメータを減らせる上に、精度向上が図れるそうです。

Softmax

 Dense layerの出力を確率に変換します。

 学習済みのモデルで推論をするときは、ここで出力される確率が終点です。あとはこの確率に従って次に来る単語を予測します。こうして予測された単語を入力として言語モデルをもう一度使うと、さらに次の単語が予測されます。これを繰り返すことで一つの文章が出来上がるというわけです。

 学習時には、ここでの出力と正解ラベル(labels)を使って交差エントロピー誤差を求め、それを損失(Loss)とします。そしてこの損失を小さくする方向に学習していきます。

おわりに

 一通りモデルの構造の解説をしてみました。誰かへの説明というより自分の理解のための説明という側面が強かったですが。

 言語モデルの設計を終えたので次はコーパス収集に行こうと思います。ではまた。

次回の記事↓

Webスクレイピングによるコーパス収集―RNNで文章生成〈3〉