LSTM/RNNの実装
通常のニューラルネットワーク
一般のニューラルネットワークにおける順伝搬は下記のように表せる。
下図のニューラルネットワークでは、下記の式になる。
と置けば、下記のように書ける。
...(1)
...(2)
誤差逆伝搬法
損失関数をLとすると、誤差の勾配は(1), (2)式を用いて下記のように書ける。
各パラメータの勾配は下記のようになる。
はテンソル積。
RNN
- 過去の隠れ層の状態も入力につかう
- 系列データを扱うのに適したニューラルネットワークである。
- 過去の情報を保持する
- 可変長入力
RNNの順伝搬
RNNの誤差逆伝搬
- BPTT
時間展開したうえでの誤差逆でパン
- Truncated BPTT
遡るステップ数を限定したBPTT
LSTM
RNNの問題を解決し、長期的な依存関係を学習できるようにしたものがLSTMである。
セルとゲートを導入した。
LSTMが勾配消失を避けられる理由
cについての勾配は、1ステップ前においても活性化関数の微分を通さず、忘却ゲートの値倍となる。
忘却ゲートの値が1に近ければ、誤差は消失しない。
=δ_t^{(c)}f_t]
主要フレームワークのAPI
RNNのAPI(TensorFlow)
- tf.contrib.rnn.BasicRNNcell(num_units, activation=tanh)
RNNセルを生成
- tf.contrib.rnn.BasicLSTMCell(num_units,forget_bias=1.0,activation=tanh)
LSTMセルを生成
- tf.contrib.rnn.static_rnn(cell, inputs,initial_state=None)
時間方向に展開。出力と最後の内部状態を返す。
LSTMのAPI(Chainer)
- chainer.links.LSTM(in_size,out,forget_bias_init=1.0)
LSTM層を生成
__call__(x)
内部情報を更新
LSTMの出力を返す
実装演習
TensorFlowによるRNN・LSTMの実装
環境準備
%tensorflow_version 1.x import numpy as np import tensorflow as tf import matplotlib.pyplot as plt % matplotlib inline import seaborn as sns sns.set()
学習用データを設定する
# sin曲線+ノイズ ts = np.linspace(0, 10 * np.pi, 500) ys = np.sin(ts) + np.random.normal(scale=0.05, size=len(ts)) plt.plot(ts, ys)
学習データの設定(n_stepsごとに分割した訓練・テストデータの準備)
# 学習設定 batch_size = 32 # ミニバッチサイズ n_steps = 50 # 入力系列の長さ input_size = 1 # 入力の次元 hidden_size = 50 # 中間層の次元 output_size = 1 # 出力層の次元 lr = 0.005 # 学習率(SGD) n_iter = 500 # イテレーション回数
# 訓練データとテストデータに分割 train_ratio = 0.8 data = [] for i in range(len(ys) - n_steps - 1): data.append(ys[i: i+n_steps+1]) data = np.array(data, dtype=np.float32) n_train = int(train_ratio * len(data)) x_train, y_train = np.split(data[:n_train], [-1], axis=1) x_test, y_test = np.split(data[n_train:], [-1], axis=1) x_train = np.reshape(x_train, [-1, n_steps, input_size]) x_test = np.reshape(x_test, [-1, n_steps, input_size])
モデル構築
def inference(x): """ 推論(順伝播) x: (batch_size, n_steps, input_size) """ # static_rnn関数の引数に適切な形に変形 x = tf.transpose(x, [1, 0, 2]) # (n_steps, batch_size, input_size) x = tf.reshape(x, [-1, input_size]) # (n_steps*batch_size, input_size) x = tf.split(x, n_steps, axis=0) # [(batch_size, input_size)] * n_steps # RNN(LSTM)セルを定義 rnn_cell = tf.contrib.rnn.BasicRNNCell(hidden_size) # rnn_cell = tf.contrib.rnn.BasicLSTMCell(hidden_size, forget_bias=1.0) # RNNセルを時間方向に伝播 hs, _ = tf.contrib.rnn.static_rnn(rnn_cell, x, dtype=tf.float32) # 出力層の定義 W_out = tf.Variable(tf.random_normal([hidden_size, output_size])) b_out = tf.Variable(tf.random_normal([output_size])) return tf.matmul(hs[-1], W_out) + b_out tf.reset_default_graph() # 入出力プレースホルダーの定義 x = tf.placeholder("float", [None, n_steps, input_size]) y = tf.placeholder("float", [None, output_size]) # オペレーションの定義 pred = inference(x) loss = tf.losses.mean_squared_error(y, pred) # train_step = tf.train.GradientDescentOptimizer(learning_rate=lr).minimize(loss) train_step = tf.train.AdamOptimizer().minimize(loss)
学習
init = tf.global_variables_initializer() # セッション sess = tf.Session() # 変数初期化 sess.run(init) # 訓練データのインデックスをランダムに perm = np.random.permutation(len(x_train)) for i in range(n_iter): idx = (i * batch_size) % len(x_train) batch_x, batch_y = x_train[perm[idx: idx+batch_size]], y_train[perm[idx: idx+batch_size]] feed_dict = {x: batch_x, y: batch_y} sess.run(train_step, feed_dict=feed_dict) if i % 50 == 0: l = sess.run(loss, feed_dict=feed_dict) print("step: {}, loss {:.5f}".format(i, l))
結果
step: 0, loss 2.82473 step: 50, loss 0.01717 step: 100, loss 0.02267 step: 150, loss 0.01304 step: 200, loss 0.01130 step: 250, loss 0.01892 step: 300, loss 0.00806 step: 350, loss 0.00597 step: 400, loss 0.00722 step: 450, loss 0.00508
テストデータに対する予測
# テストデータに対する予測 prediction = sess.run(pred, feed_dict={x: x_test}) # 1次元配列に prediction = prediction.reshape(-1) true_y = y_test.reshape(-1) # テストデータに対する予測を可視化 xx = np.arange(len(prediction)) plt.plot(xx, true_y, label='true') plt.plot(xx, prediction, label='prediction') plt.legend()
- 再帰的な予測
テストデータの最初の値からスタートして、モデルの予測結果を利用して再帰的に予測をする。
モデルのトレーニング回数は2000回で行っている。
# テストデータの最初のデータからスタートし、 # モデルの予測を利用し再帰的に予測 curr_x = x_test[0] predicted = [] # 予測するステップ数 N = 200 for i in range(N): # 予測 predicted.append(model.predict(curr_x[None])) # 入力を更新 curr_x = np.insert(curr_x, n_steps, predicted[-1], axis=0)[1:] # 1次元配列に predicted = np.array(predicted).reshape(-1) #再帰的な予測を可視化 plt.plot(xx, true_y, label='true') plt.plot(np.arange(N), predicted, label='prediction') plt.legend()
為替(USD/JPY)のLSTMによる予測
準備
import pandas as pd import numpy as np import tensorflow as tf import matplotlib.pyplot as plt % matplotlib inline import seaborn as sns sns.set()
データ読み込み
!mkdir data !wget https://dl.dropbox.com/s/rzzcc89lokvzu3r/usd_jpy.csv -O data/usd_jpy.csv
データ構造は以下。
日付 | 始値 | 高値 | 安値 | 終値 | |
0 | 2007/04/02 | 117.84 | 118.08 | 117.46 | 117.84 |
1 | 2007/04/03 | 117.84 | 118.98 | 117.72 | 118.96 |
2 | 2007/04/04 | 118.92 | 119.08 | 118.56 | 118.72 |
3 | 2007/04/05 | 118.72 | 118.99 | 118.44 | 118.72 |
4 | 2007/04/06 | 118.72 | 119.39 | 118.67 | 119.27 |
終値を図示してみる。
plt.figure(figsize = (18,9)) plt.plot(np.arange(df.shape[0]), df['終値'].values) plt.xticks(np.arange(0, df.shape[0], 260), df['日付'].loc[::260], rotation=45)
終値をターゲットに、予測モデルを作成する。
データを作成する。
close_prices = df['終値'].values train_ratio = 0.8 n_train = int(len(close_prices) * train_ratio) train, test = close_prices[:n_train], close_prices[n_train:] train = train.reshape(-1, 1) test = test.reshape(-1, 1)
MinMaxScalerを使ってデータを0-1に正規化する。
from sklearn import preprocessing scaler = preprocessing.MinMaxScaler() train = scaler.fit_transform(train) test = scaler.transform(test)
トレーニングデータ数は2340である。
データジェネレータのクラスを用意する。
import math class DataGenerator(tf.keras.utils.Sequence): def __init__(self, data, batch_size, n_steps, input_size, output_size): self.data = data self.batch_size = batch_size self.n_steps = n_steps self.input_size = input_size self.output_size = output_size def __len__(self): return math.floor((len(self.data) - self.n_steps) / self.batch_size) def __getitem__(self, idx): xs = np.zeros((self.batch_size, self.n_steps, self.input_size)) ys = np.zeros((self.batch_size, self.output_size)) for i in range(self.batch_size): step_begin = (idx * self.batch_size) + i step_end = (idx * self.batch_size) + i + self.n_steps x = np.zeros((self.n_steps, self.input_size)) y = np.zeros((self.output_size)) if step_end >= len(self.data): break else: x = self.data[step_begin: step_end] y = self.data[step_end] xs[i] = x ys[i] = y return xs, ys
学習の設定をする。
# 学習設定 batch_size = 32 # ミニバッチサイズ n_steps = 50 # 入力系列の長さ input_size = 1 # 入力の次元 hidden_size = 50 # 中間層の次元 output_size = 1 # 出力層の次元 n_epochs = 30 # エポック数 train_gen = DataGenerator(train, batch_size, n_steps, input_size, output_size)
モデル構築を行う
def create_model(): inputs = tf.keras.layers.Input(shape=(n_steps, input_size)) lstm = tf.keras.layers.LSTM(hidden_size)(inputs) output = tf.keras.layers.Dense(output_size)(lstm) model = tf.keras.Model(inputs, output) model.compile(optimizer='adam', loss='mse', metrics='accuracy') return model model = create_model() model.summary()
Model: "model" _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= input_1 (InputLayer) [(None, 50, 1)] 0 lstm (LSTM) (None, 50) 10400 dense (Dense) (None, 1) 51 ================================================================= Total params: 10,451 Trainable params: 10,451 Non-trainable params: 0
上記のようにパラメータ数10,451のLSTMを作成した。
学習を行う
model.fit_generator(train_gen, epochs=30, shuffle=True)
Epoch 3/30 71/71 [==============================] - 1s 13ms/step - loss: 0.0018 - accuracy: 8.8028e-04 Epoch 4/30 71/71 [==============================] - 1s 13ms/step - loss: 0.0013 - accuracy: 8.8028e-04 Epoch 5/30 71/71 [==============================] - 1s 14ms/step - loss: 0.0014 - accuracy: 8.8028e-04 Epoch 6/30 71/71 [==============================] - 1s 13ms/step - loss: 0.0014 - accuracy: 8.8028e-04 Epoch 7/30 71/71 [==============================] - 1s 14ms/step - loss: 0.0010 - accuracy: 8.8028e-04 Epoch 8/30 71/71 [==============================] - 1s 13ms/step - loss: 0.0011 - accuracy: 8.8028e-04 Epoch 9/30 71/71 [==============================] - 1s 13ms/step - loss: 0.0012 - accuracy: 8.8028e-04 Epoch 10/30 71/71 [==============================] - 1s 14ms/step - loss: 9.7010e-04 - accuracy: 8.8028e-04 Epoch 11/30 71/71 [==============================] - 1s 14ms/step - loss: 9.1534e-04 - accuracy: 8.8028e-04 Epoch 12/30 71/71 [==============================] - 1s 14ms/step - loss: 0.0011 - accuracy: 8.8028e-04 Epoch 13/30 71/71 [==============================] - 1s 14ms/step - loss: 9.7462e-04 - accuracy: 8.8028e-04 Epoch 14/30 71/71 [==============================] - 1s 14ms/step - loss: 8.6392e-04 - accuracy: 8.8028e-04 Epoch 15/30 71/71 [==============================] - 1s 14ms/step - loss: 8.6688e-04 - accuracy: 8.8028e-04 Epoch 16/30 71/71 [==============================] - 1s 14ms/step - loss: 0.0010 - accuracy: 8.8028e-04 Epoch 17/30 71/71 [==============================] - 1s 14ms/step - loss: 0.0011 - accuracy: 8.8028e-04 Epoch 18/30 71/71 [==============================] - 1s 14ms/step - loss: 8.6142e-04 - accuracy: 8.8028e-04 Epoch 19/30 71/71 [==============================] - 1s 14ms/step - loss: 7.6914e-04 - accuracy: 8.8028e-04 Epoch 20/30 71/71 [==============================] - 1s 14ms/step - loss: 8.8667e-04 - accuracy: 8.8028e-04 Epoch 21/30 71/71 [==============================] - 1s 14ms/step - loss: 6.5316e-04 - accuracy: 8.8028e-04 Epoch 22/30 71/71 [==============================] - 1s 14ms/step - loss: 6.6321e-04 - accuracy: 8.8028e-04 Epoch 23/30 71/71 [==============================] - 1s 14ms/step - loss: 6.9309e-04 - accuracy: 8.8028e-04 Epoch 24/30 71/71 [==============================] - 1s 14ms/step - loss: 6.6656e-04 - accuracy: 8.8028e-04 Epoch 25/30 71/71 [==============================] - 1s 13ms/step - loss: 7.9177e-04 - accuracy: 8.8028e-04 Epoch 26/30 71/71 [==============================] - 1s 13ms/step - loss: 7.3700e-04 - accuracy: 8.8028e-04 Epoch 27/30 71/71 [==============================] - 1s 13ms/step - loss: 7.8668e-04 - accuracy: 8.8028e-04 Epoch 28/30 71/71 [==============================] - 1s 13ms/step - loss: 5.7617e-04 - accuracy: 8.8028e-04 Epoch 29/30 71/71 [==============================] - 1s 13ms/step - loss: 5.7106e-04 - accuracy: 8.8028e-04 Epoch 30/30 71/71 [==============================] - 1s 13ms/step - loss: 6.8287e-04 - accuracy: 8.8028e-04 <keras.callbacks.History at 0x7f655020a450>
テスト及び予測結果の可視化を行う
test_gen = DataGenerator(test, batch_size, n_steps, input_size, output_size) predicts = model.predict_generator(test_gen) fixed_predicts = scaler.inverse_transform(predicts) fixed_test = scaler.inverse_transform(test) # テストデータに対する予測を可視化 mod = (len(fixed_test) - n_steps) % batch_size xx = np.arange(len(fixed_predicts)) plt.figure(figsize=(18, 9)) plt.plot(xx, fixed_test[n_steps:-mod], label='true') plt.plot(xx, fixed_predicts, label='prediction') plt.legend()