【Tensorflow2】Kerasで単純なRNN(リカレントニューラルネットワーク)を試す

tensorflow

今回はRNN(リカレントニューラルネットワーク)を試してみようと思います。

Kerasとは

Kerasは、ニューラルネットワークを非常にシンプルに構築できるライブラリです。

https://keras.io/ja/

TensorFlow等で書くとかなり長くなってしまうコードがKerasを使うことでシンプルなコードとなります。

ちなみにKerasは下でTensorFlow等が動いています。

RNN(リカレントニューラルネットワーク)とは

RNNとは再帰型ニューラルネットワークとも呼ばれ、
時系列の関係がある入力にも対応できるニューラルネットワークです。

以下はイメージ図、

図からわかるとおり、
現在の入力\({\bf x}\)1つ前の出力\({\bf h}\)を用いることで、入力間の関係性も学習させようというアプローチです。

ちなみに図の右のようにすることをアンロールするといいます。
図の中の\(h_t\)は記憶セルセルと呼び、1つ前の状態の内容(内部状態)を保存します。
\(h_t\)は数式で表現すると次のようになります。

$$\begin{aligned}
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等を試していこうと思います。

参考文献

  1. 直感 Deep Learning ―Python×Kerasでアイデアを形にするレシピ
タイトルとURLをコピーしました