入門者に向けてKerasを使ったRNN(Recurrentニューラルネットワーク)の初歩を解説します。RNNは時系列データの予測やNLP(自然言語処理)などに強く、使いどころが多い便利な手法です。
Google Colaboratoryを使っているのでローカルでの環境準備すらしていません。Google Colaboratoryについては「Google Colaboratory概要と使用手順(TensorFlowもGPUも使える)」の記事を参照ください。
以下のシリーズにしています。
- 【Keras入門(1)】単純なディープラーニングモデル定義
- 【Keras入門(2)】訓練モデル保存(KerasモデルとSavedModel)
- 【Keras入門(3)】TensorBoardで見える化
- 【Keras入門(4)】Kerasの評価関数(Metrics)
- 【Keras入門(5)】単純なRNNモデル定義 <- 本記事
- 【Keras入門(6)】単純なRNNモデル定義(最終出力のみ使用)
- 【Keras入門(7)】単純なSeq2Seqモデル定義
##使ったPythonパッケージ
Google Colaboratoryでインストール済の以下のパッケージとバージョンを使っています。KerasはTensorFlowに統合されているものを使っているので、ピュアなKerasは使っていません。Pythonは3.6です。
- tensorflow: 1.14.0
- Numpy: 1.16.4
- matplotlib: 3.0.3
処理概要
時系列データ予測として、正弦関数(sine)の値を予測します。
正弦関数は、こんなウネウネした値が変わっていくやつです。
以下の2種類の配列を作って、Kerasを使ったディープラーニングで予測モデルを作ります。
種類 | Shape | データ |
---|---|---|
説明変数(配列x_train) | 10 X 40 | xが-4.9から4.9までの0.2間隔の正弦関数の値 |
目的変数(配列y_train) | 10 X 40 | 説明変数の1ずれた値 |
言葉だとわかりにくいですが、表とグラフにするとこんな感じ。目的変数の次の値を予測しています(つまり時系列データ予測)。そして、2つ目のデータセットでは、1つ目のデータセットから1つずつずらしています。!
そして、訓練時のRNNへのデータの渡し方は下記のような形です。
でもって、予測時は下記のように予測結果を次の予測データ元へ渡します。モデルが予測した値を再度使うあたりが再帰(Recurrent)たる所以ですね。
処理プログラム
プログラム全体はGitHubを参照ください。
1. ライブラリインポート
今回はmatplotlibとnumpyとtensorflowに統合されているkerasを使います。ピュアなkerasでも問題なく、インポート元を変えるだけです。
import numpy as np
import matplotlib.pyplot as plt
# TensorFlowに統合されたKerasを使用
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, SimpleRNN
##2. 前処理
###2.1. 正弦関数配列作成
-4.9から4.9までの50要素の等差数列(0.2間隔)の配列(x_sin
)を作成し、対応する正弦関数の値の配列(y_sin
)も
作成します。
# -4.9から4.9までの50要素の等差数列(0.2間隔)
x_sin = np.linspace(-4.9, 4.9)
y_sin = np.sin(x_sin)
plt.plot(x_sin, y_sin)
plt.show()
###2.2. 説明変数(x_train
)と目的変数(y_train
)作成
RNNに流すための形に整形します。
NUM_RNN = 10 # 1時系列のデータ数
NUM_DATA = len(x_sin) - NUM_RNN # 今回は40(=50-10)
x = []
y = []
for i in range(NUM_DATA):
x.append(y_sin[i:i+NUM_RNN]) # 説明変数
y.append(y_sin[i+1:i+NUM_RNN+1]) # 正解データなので1ずらした値
else:
x_train = np.array(x).reshape(NUM_DATA, NUM_RNN, 1) # 入力を(サンプル数、時系列の数、入力層のニューロン数)にする
y_train = np.array(y).reshape(NUM_DATA, NUM_RNN, 1) # 説明変数(x_train)と同様のshape
3. モデル定義
記事「【Keras入門(1)】単純なディープラーニングモデル定義」のときと違い、simpleRNN
を使っています。実際にはLSTMやGRUなどを使うことが多いかと思いますが、今回はsimpleRNN
で十分な精度が出ます。また、LSTMやGRUを使う場合も呼び出し方はほとんど変わりません。
NUM_DIM = 8 # 中間層の次元数
model = Sequential()
# return_sequenceがTrueなので全RNN層が出力を返す(Falseだと最後のRNN層のみが出力を返す)
model.add(SimpleRNN(NUM_DIM, input_shape=(NUM_RNN, 1), return_sequences=True))
model.add(Dense(1, activation="linear")) #全結合層
model.compile(loss="mean_squared_error", optimizer="sgd")
model.summary()
summary
関数で以下のようなモデルサマリを出してくれます。
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
simple_rnn (SimpleRNN) (None, 10, 8) 80
_________________________________________________________________
dense (Dense) (None, 10, 1) 9
=================================================================
Total params: 89
Trainable params: 89
Non-trainable params: 0
_________________________________________________________________
##4. 訓練実行
fit関数を使って訓練実行です。20epoch程度でそこそこいい精度が出ます。
history = model.fit(x_train, y_train, epochs=20, batch_size=8)
# Lossをグラフ表示
loss = history.history['loss']
plt.plot(np.arange(len(loss)), loss) # np.arangeはlossの連番数列を生成(今回はepoch数の0から19)
plt.show()
5. テスト
最後にテストです。
5.1. テストデータ作成
訓練データの最初の10件をテストデータとします。
# x[0]は最初の入力(時系列10個の数)。reshape(-1)で一次元のベクトルにする。
x_test = x_train[0].reshape(-1)
##4.2. 訓練済モデルを使ったテストデータの答え合わせ
predict関数を使ってテストデータから予測値を出力し、さらに次の予測の元データとします。
# データ数(40回)ループ
for i in range(NUM_DATA):
y_pred = model.predict(x_test[-NUM_RNN:].reshape(1, NUM_RNN, 1)) # 直近データ(最後から10要素)を使って予測
x_test = np.append(x_test, y_pred[0][NUM_RNN-1][0]) # 出力結果をx_testに追加(n_rnn-1が10番目を意味している)
# 最初の10要素は完全に同じ
plt.plot(x_sin, y_sin, label="Training data")
plt.plot(x_sin, x_test, label="Predicted")
plt.legend()
plt.show()
予測値と訓練データを比較でグラフ表示してみます。-4.9から-2.9までの値は完全に同じです。それ以降は、微妙にずれてはいますが、それっぽい値が出ています。
簡単な変更でモデルを複雑に変更
simpleRNNではなく、実務上はLSTMを使うことが多いはずです。
その場合、モデルの層をこんな感じにするだけです。
GPU向けのCuDNNLSTMもあります(確かモデル間の互換性がなかったような気がします(未確認))。
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, LSTM
model.add(LSTM(NUM_DIM, input_shape=(NUM_RNN, 1), return_sequences=True))
GRUだとこう。GPU向けのCuDNNGRUもあります(確かモデル間の互換性がなかったような気がします(未確認))。
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, GRU
model.add(GRU(NUM_DIM, input_shape=(NUM_RNN, 1), return_sequences=True))
双方向にしたい場合はBidirectional
を使います。
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, simpleRNN, Bidirectional
model.add(Bidirectional(SimpleRNN(NUM_DIM, return_sequences=True), input_shape=(NUM_RNN, 1)))
複数層に重ねることもできます。
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, simpleRNN
model.add(SimpleRNN(NUM_DIM, input_shape=(NUM_RNN, 1), return_sequences=True))
model.add(SimpleRNN(NUM_DIM, input_shape=(NUM_RNN, 1), return_sequences=True))