今回はRNN(リカレントニューラルネットワーク)を試してみようと思います。
Kerasとは
Kerasは、ニューラルネットワークを非常にシンプルに構築できるライブラリです。
TensorFlow等で書くとかなり長くなってしまうコードがKerasを使うことでシンプルなコードとなります。
ちなみにKerasは下でTensorFlow等が動いています。
RNN(リカレントニューラルネットワーク)とは
RNNとは再帰型ニューラルネットワークとも呼ばれ、
時系列の関係がある入力にも対応できるニューラルネットワークです。
以下はイメージ図、
図からわかるとおり、
現在の入力\({\bf x}\)1つ前の出力\({\bf h}\)を用いることで、入力間の関係性も学習させようというアプローチです。
ちなみに図の右のようにすることをアンロールするといいます。
図の中の\(h_t\)は記憶セルやセルと呼び、1つ前の状態の内容(内部状態)を保存します。
\(h_t\)は数式で表現すると次のようになります。
h_t = f(h_{t-1}, x_t)
\end{aligned}$$
KearsでRNNを動かしてみる
RNNのサンプルとしてよく用いられるsin波を使ってKerasのRNNを試してみます。
具体的には以下の図のような感じで、
現在から少し前までを見て、次の値を予測するようなものです。
ソースコードは以下のgithubにも上げておきます
https://github.com/tocom242242/aifx_blog_codes/blob/master/nn_tf2/rnn/rnn_sin.ipynb
学習・検証用データの作成
numpyを使ってsin波を作成します。
import numpy as np import matplotlib.pyplot as plt x = np.linspace(-3*np.pi,3*np.pi,1000) y = np.sin(x) plt.plot(x, y) # 確認。別にプロットしなくていい
RNN用のデータに加工していきます。
input_len = 5 # 入力の長さ X, Y = [], [] for i, _ in enumerate(x): if (i+input_len+1 >= len(x)): break X.append(y[i:i+input_len]) Y.append(y[i+input_len+1])
input_lenが入力の長さです。
データとして、i番目からinput_lenまでを入力データ、i+input_len+1を出力データになるようにデータを加工します。
学習と検証用にデータを分けます。
今回は単純に左から8割を学習用、残りの2割を評価用とします。
つまり、以下のような感じです。
実装しましょう。
split_index = int(len(X)*0.8) train_x = X[:split_index] train_y = Y[:split_index] test_x = X[split_index:] test_y = Y[split_index:]
次に学習するためにデータを成形しておきます。
train_x = np.array(train_x).reshape(len(train_x),-1,1) test_x = np.array(test_x).reshape(len(test_x), -1, 1) train_y = np.array(train_y).reshape(-1,1) test_y = np.array(test_y).reshape(-1,1)
これで、学習用、検証用のデータができました。
モデルの構築・学習・評価
まずモデルの構築をしていきます。
from keras.layers import Dense, Activation, SimpleRNN from keras.models import Sequential input_shape = (len(train_x[0]), 1) model = Sequential() model.add(SimpleRNN(100, return_sequences=False, input_shape=input_shape)) model.add(Dense(1)) model.add(Activation("linear")) model.compile(loss="mse", optimizer="adam")
今回は最もシンプルなRNNを用いるのでSimpleRNN層を用います。
第一引数の100はSimpleRNN層の中の素子数です。
今回は系列を返すのでないので、return_sequences=Falseとしています。
出力は次のステップの値であり、回帰問題となりますので、出力は1です。
まず学習前のモデルで予測してみます。学習前ですので、うまく予測できないはずです。
plt.plot(np.arange(len(test_y)), test_y, label="sin") predict_y = model.predict(test_x) plt.plot(np.arange(len(test_y)), predict_y, label="predict(before)") plt.legend() plt.show()
予想通り正しく予測できていません。
では、学習させていきましょう。
hist = model.fit(train_x, train_y, batch_size=32, epochs=100, verbose=0) history = hist.history plt.plot(hist.epoch, history["loss"], label="loss")
上記のコードを実行すると以下のように学習過程を出力します。
グラフからわかるように、誤差を小さくできているようです。
さらに検証用データでモデルの評価を行います。
score = model.evaluate(test_x, test_y) print(score) #=>8.943095873296466e-05
誤差を小さくできていますね。
具体的にどのように予測しているかを見てみましょう
plt.plot(np.arange(len(test_y)), test_y, label="sin") predict_y = model.predict(test_x) plt.plot(np.arange(len(test_y)), predict_y, label="predict") plt.legend() plt.show()
青が正解データ、オレンジが学習したモデルによる予測値になります。
かなりきれいに予測できています。
ソースコードの全体像
from keras.models import Sequential from keras.layers import Dense, Activation, SimpleRNN import numpy as np import matplotlib.pyplot as plt x = np.linspace(-3*np.pi, 3*np.pi, 1000) y = np.sin(x) input_len = 5 X, Y = [], [] for i, _ in enumerate(x): if (i+input_len+1 >= len(x)): break X.append(y[i:i+input_len]) Y.append(y[i+input_len+1]) split_index = int(len(X)*0.8) train_x = X[:split_index] train_y = Y[:split_index] test_x = X[split_index:] test_y = Y[split_index:] train_x = np.array(train_x) train_x = train_x.reshape(train_x.shape[0], -1, 1) test_x = np.array(test_x) test_x = test_x.reshape(test_x.shape[0], -1, 1) input_shape = (len(train_x[0]), 1) model = Sequential() model.add(SimpleRNN(100, return_sequences=False, input_shape=input_shape)) model.add(Dense(1)) model.add(Activation("linear")) model.compile(loss="mse", optimizer="adam") hist = model.fit(train_x, train_y, batch_size=32, epochs=100, verbose=0) history = hist.history plt.plot(hist.epoch, history["loss"], label="loss") score = model.evaluate(test_x, test_y) print(score) plt.plot(np.arange(len(test_y)), test_y, label="sin") predict_y = model.predict(test_x) plt.plot(np.arange(len(test_y)), predict_y, label="predict") plt.legend() plt.show()
終わりに
今回はKerasを使って最もシンプルなRNNを試してみました。
次はLSTM等を試していこうと思います。