Python機械学習プログラミング
読み会
第16章
系列データのモデル化 - リカレントニューラルネットワーク
1
[第2版]
基盤 江口春紀
目次
● 系列データ
● リカレントニューラルネットワーク:シーケンスモデルの構築
● 多層RNNの実装:TensorFlowでのシーケンスモデルの構築
● プロジェクト1:多層RNNを使ったIMDb映画レビューの感情分析
● プロジェクト2:文字レベルの言語モデルとして RNNをTensorFlowで実装
2
3
系列データ
系列データ
● 系列データ(シーケンス)の特徴
● データの要素が特定の順序で並んでいて、互いに無関係でない。
● 系列データを表現する
● 本章では、シーケンスを と表す。
(各サンプル点 x^(t) が特定の時間 t に属している。)
4
シーケンスモデルのさまざまなカテゴリ
● シーケンスモデル
● 言語間の翻訳、画像キャプショニング、テキスト生成
● 入力データと出力データの関係
● 入力データと出力データの
どちらかがシーケンスである
としたら3種類のカテゴリに
分類される。
5
6
リカレントニューラルネットワーク
リカレントニューラルネットワーク:シーケンスモデルの構築
● RNNの構造とデータの流れを理解する
● RNNのアーキテクチャ
7
ネットワークが過去の事象に関する
記憶を持つことが可能になる
リカレントニューラルネットワーク:シーケンスモデルの構築
● RNNのアーキテクチャと情報の流れ
8
入力層からの事前活性化と
1つ前の時間刻み t-1 の同じ
隠れ層から入力を受け取る
RNNで活性化を計算する
● 単層RNNの重み行列
● : 入力 x^(t) と隠れ層 h の重み行列
● : リカレントエッジに関連付けられた重み行列
● : 隠れ層と出力層の重み行列
9
RNNで活性化を計算する
● 活性化の計算
● 隠れ層の総入力
● 隠れユニットの活性化
● 出力ユニット
10
RNNで活性化を計算する
11
RNNで活性化を計算する
● RNNの学習
● BPTT (Backpropagation Through Time)
● 全損失Lを時間 t=1 から t=T までの全ての損失関数の総和であると考える。
● 勾配の計算
12
長期的な相互作用の学習
● BPTTの課題
● 勾配消失(Vanishing gradient)
● 勾配発散(Exploding gradient)
13
長期的な相互作用の学習
● BPTTの課題の解決
● T-BPTT(Truncated Backpropagation Through Time)
● 指定された閾値の上で勾配を刈り込むことで勾配発散問題を解決する。
● LSTM(Long short-term memory)
14
LSTMのユニット
● LSTM(Long short-term memory)
● 勾配消失問題を解決する手法。
● メモリセルと呼ばれるものが隠れ層と置き換わり、入力ゲート、出力ゲート、忘却ゲート
が設けられる。これらは、メモリセルの内容を書き換えるか・出力するか・忘れるかを決定する。
15
多層RNNの実装
16
1. 感情分析
2. 言語モデルの構築
17
多層RNNを使った
IMDb映画レビューの感情分析
データの準備
● 使用するデータセット
● IMDb(Internet Movie Database)から取得した50,000件の映画レビュー
データセットで、映画の評価が星 1個から10個までの10段階で行われている。
映画レビューデータセットは、「肯定的」または「否定的」に分類されており、
6個以上の星がついているものを「肯定的」、それ以下は「否定的」であることを意味する。
● データの半分をトレーニングデータに、残りの半分をテストデータに使用する。
18
review sentiment
0 In 1974, the teenager Martha Moxley (Maggie Gr... 1
1 OK... so... I really like Kris Kristofferson a... 0
2 ***SPOILER*** Do not read this, if you think a... 0
データの準備
● データのエンコード
● collectionsパッケージのCounterを使って
データセットに含まれる一意な単語ごと
の出現回数をカウントする。
● シーケンス(文章)の長さを200に設定する。
200語未満は0パディング、200語以上は
最後の200語を使用する。
19
埋め込み
● 単語を入力特徴量に変換する。
● one-hotエンコーディングをすると、語彙が多い場合に次元の呪いに陥る可能性もある。
● 埋め込み(embedding)を使うことで、次元の呪いを抑制する。
実数値の要素を持つ固定サイズのベクトル
に各単語をマッピングする。
(tf.nn.embedding_lookup()を使う)
20
RNNモデルの構築
● SentimentRNNクラス
● コンストラクタ
● buildメソッド
● プレースホルダの宣言。
● 埋め込み表現を入力とした多層 RNNの構築。
● trainメソッド
● ミニバッチでトレーニングを行う。
● 学習したモデルの保存。
● predictメソッド
● トレーニングプロセスで保存されたモデルを使用して、
テストデータで予測値を生成。
21
SentimentRNNクラス
● コンストラクタ
22
class SentimentRNN(object):
def __init__(self, n_words, seq_len=200,
lstm_size=256, num_layers=1, batch_size=64,
learning_rate=0.0001, embed_size=200):
self.n_words = n_words
self.seq_len = seq_len
self.lstm_size = lstm_size ## number of hidden units
self.num_layers = num_layers
self.batch_size = batch_size
self.learning_rate = learning_rate
self.embed_size = embed_size
self.g = tf.Graph()
with self.g.as_default():
tf.set_random_seed(123)
self.build()
self.saver = tf.train.Saver()
self.init_op = tf.global_variables_initializer()
SentimentRNNクラス
● buildメソッド
● 多層RNNモデルのセルと初期状態を定義する。
23
def build(self):
...
embedding = tf.Variable(
tf.random_uniform((self.n_words, self.embed_size), minval=-1, maxval=1),
name='embedding'
)
embed_x = tf.nn.embedding_lookup(embedding, tf_x, name='embeded_x')
cells = tf.contrib.rnn.MultiRNNCell(
[tf.contrib.rnn.DropoutWrapper(
tf.contrib.rnn.BasicLSTMCell(self.lstm_size),
output_keep_prob=tf_keepprob) for i in range(self.num_layers)]
)
self.initial_state = cells.zero_state(self.batch_size, tf.float32)
...
SentimentRNNクラス
● buildメソッド
● セルとそれらの初期状態に基づいて RNNモデルを作成する
24
def build(self):
...
lstm_outputs, self.final_state = tf.nn.dynamic_rnn(
    cells, embed_x,
    initial_state=self.initial_state
)
logits = tf.layers.dense(
inputs=lstm_outputs[:, -1],
units=1, activation=None,
name='logits'
)
logits = tf.squeeze(logits, name='logits_squeezed')
y_proba = tf.nn.sigmoid(logits, name='probabilities')
...
SentimentRNNクラス
● trainメソッド
25
def train(self, X_train, y_train, num_epochs):
with tf.Session(graph=self.g) as sess:
sess.run(self.init_op)
iteration = 1
for epoch in range(num_epochs):
state = sess.run(self.initial_state)
for batch_x, batch_y in create_batch_generator(
X_train, y_train, self.batch_size):
feed = {'tf_x:0': batch_x, 'tf_y:0': batch_y, 'tf_keepprob:0': 0.5,
self.initial_state : state}
loss, _, state = sess.run(['cost:0', 'train_op', self.final_state],
feed_dict=feed)
iteration +=1
if (epoch+1)%10 == 0:
self.saver.save(sess, "model/sentiment-%d.ckpt" % epoch)
エポック毎に、セルの現在の状態を
初期状態にリセット
SentimentRNNクラス
● predictメソッド
26
def predict(self, X_data, return_proba=False):
preds = []
with tf.Session(graph = self.g) as sess:
self.saver.restore(
sess, tf.train.latest_checkpoint('model/'))
test_state = sess.run(self.initial_state)
for ii, batch_x in enumerate(
create_batch_generator(X_data, None, batch_size=self.batch_size), 1):
feed = {'tf_x:0' : batch_x, 'tf_keepprob:0': 1.0, self.initial_state : test_state}
if return_proba:
pred, test_state = sess.run(['probabilities:0', self.final_state], feed_dict=feed)
else:
pred, test_state = sess.run(['labels:0', self.final_state], feed_dict=feed)
preds.append(pred)
return np.concatenate(preds)
感情分析RNNモデルのトレーニングと最適化
● モデルのトレーニング
● クラスラベルの予測
27
rnn.train(X_train, y_train, num_epochs=40)
Epoch: 1/40 Iteration: 20 | Train loss: 0.70637
Epoch: 1/40 Iteration: 40 | Train loss: 0.60539
Epoch: 1/40 Iteration: 60 | Train loss: 0.66977
...
Epoch: 40/40 Iteration: 9960 | Train loss: 0.00000
Epoch: 40/40 Iteration: 9980 | Train loss: 0.00000
Epoch: 40/40 Iteration: 10000 | Train loss: 0.00001
preds = rnn.predict(X_test)
y_true = y_test[:len(preds)]
print('Test Acc.: %.3f' % (np.sum(preds == y_true) / len(y_true)))
Test Acc.: 0.860
28
文字レベルの言語モデルとして
RNNをTensorFlowで実装
文字レベルの言語モデルとしてRNNをTensorFlowで実装
● 英語の文章の生成
● 文字レベルの言語モデルでは、 RNNに文字が1文字ずつ供給され次の文字を予測する。
29
データの準備
● 入力データ
● シェイクスピアの「ハムレット」 (The Tragedie ofHamlet)のテキスト
● 文字と整数値のマッピング
● 今回は英数字と記号合わせて 65文字出現している。
30
データの準備
● 文字と整数値のマッピング
● 整数値をシーケンスのバッチに適用
31
文字レベルのRNNモデルの構築
● CharRNNクラスの実装
文字レベルのRNNモデルを構築するためのクラス
● コンストラクタ
● buildメソッド
● プレースホルダの定義、 LSTMセルを使ったRNNの作成。
● trainメソッド
● ミニバッチ処理
● sampleメソッド
● 与えられた文字列を元に、次の文字の確率を計算し文字を選択する。
このプロセスを繰り返し、選択した文字をつなぎ合わせて文字列を生成する。
32
CharRNNクラス
● コンストラクタ
33
class CharRNN(object):
def __init__(self, num_classes, batch_size=64, num_steps=100, lstm_size=128,
num_layers=1, learning_rate=0.001, keep_prob=0.5, grad_clip=5,
sampling=False):
self.num_classes = num_classes
self.batch_size = batch_size
self.num_steps = num_steps
self.lstm_size = lstm_size
self.num_layers = num_layers
self.learning_rate = learning_rate
self.keep_prob = keep_prob
self.grad_clip = grad_clip
self.g = tf.Graph()
with self.g.as_default():
tf.set_random_seed(123)
self.build(sampling=sampling)
self.saver = tf.train.Saver()
self.init_op = tf.global_variables_initializer()
CharRNNクラス
● buildメソッド
34
def build(self, sampling):
if sampling == True:
batch_size, num_steps = 1, 1
else:
batch_size = self.batch_size
num_steps = self.num_steps
...
x_onehot = tf.one_hot(tf_x, depth=self.num_classes)
y_onehot = tf.one_hot(tf_y, depth=self.num_classes)
...
tvars = tf.trainable_variables()
grads, _ = tf.clip_by_global_norm(tf.gradients(cost, tvars), self.grad_clip)
optimizer = tf.train.AdamOptimizer(self.learning_rate)
train_op = optimizer.apply_gradients(zip(grads, tvars), name='train_op')
前節とは対照的に、one-hotエンコーディング
を使用。
勾配発散問題回避するために勾配刈り込みを適
用させる。
CharRNNクラス
● trainメソッド
35
def train(self, train_x, train_y, num_epochs, ckpt_dir='./model/'):
if not os.path.exists(ckpt_dir):
os.mkdir(ckpt_dir)
with tf.Session(graph=self.g) as sess:
sess.run(self.init_op)
n_batches = int(train_x.shape[1]/self.num_steps)
iterations = n_batches * num_epochs
for epoch in range(num_epochs):
new_state = sess.run(self.initial_state)
loss = 0
bgen = create_batch_generator(train_x, train_y, self.num_steps)
for b, (batch_x, batch_y) in enumerate(bgen, 1):
iteration = epoch*n_batches + b
feed = {'tf_x:0': batch_x, 'tf_y:0': batch_y, 'tf_keepprob:0': self.keep_prob,
self.initial_state : new_state}
batch_cost, _, new_state = sess.run(
['cost:0', 'train_op', self.final_state], feed_dict=feed)
self.saver.save(sess, os.path.join(ckpt_dir, 'language_modeling.ckpt'))
エポック毎に、セルの現在の状態を
初期状態にリセット
CharRNNクラス
● sampleメソッド
● 観測したシーケンスに基づいて次の文字の確率を計算する。
その際、下の関数を使い、確率値の上位 top_n 個から次の文字をランダムに 1つ選択する。
36
def get_top_char(probas, char_size, top_n=5):
p = np.squeeze(probas)
p[np.argsort(p)[:-top_n]] = 0.0
p = p / np.sum(p)
ch_id = np.random.choice(char_size, 1, p=p)[0]
return ch_id
CharRNNモデルのトレーニングと文章の生成
● 文章の生成
● トレーニングしたモデルを読み込み、出力文字数を指定して文章を生成する。
37
rnn = CharRNN(len(chars), sampling=True)
print(rnn.sample(ckpt_dir='./model-200/', output_length=500))
The spare and word in to in mine
ther so may.
Enter.
King. The werare the weard, and the soure they beought weakes will,
But the shall be things, this whale a man thought,
That wime hered one, it ald wards to the tree to the whine
Ham. Ild haue hould thy bin the sornes will be ow ere heate,
To bet an hather and thongile and as the Criust, that Ild deelath
they,
All to be is me wat my shall well heaue,
The King and was it on the the master, mont whine the
Treest stath of all the spalaino thele
まとめ
● Recurrent Neural Network (RNN)
● ネットワークが過去の事象に関する記憶を持つことが可能で
時系列データを処理することができる。
● LSTM(Long short-term memory)
● 勾配消失問題を解決する手法。
38

[第2版]Python機械学習プログラミング 第16章