Google Colaboratory で試す: https://colab.research.google.com/gist/propella/1ada5171f9884dbf15a96290aa7369a3
以下の記事と同じ事をするだけです。さらに LinearRegression との比較もしてみます。
- 詳解ディープラーニング TensorFlow Keras による時系列データ処理 第五章 リカレントニューラルネットワーク
- 深層学習ライブラリKerasでRNNを使ってsin波予測
- RNNにsin波を学習させて予測してみた
- https://github.com/sunsided/tensorflow-lstm-sin
from sklearn.linear_model import LinearRegression
from sklearn.metrics import r2_score
from sklearn.model_selection import train_test_split
from tensorflow import keras
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import tensorflow as tf
データの作成
周期が 100 のノイズ入りサイン波を 200 個作ります。このなかで任意の 25 の連続した値から次の値を予測するというのをやります。
ノイズを乗せても予測される値はノイズの消えたきれいなカーブになりますよ、という事がしたいです。
period = 100
np.random.seed(2)
def sin_curve(length, ampl=0.05):
signal = np.sin(2 * np.pi * np.arange(0, length) / period)
noise = ampl * np.random.uniform(low=-1.0, high=1.0, size=length)
return signal + noise
source = sin_curve(200)
plt.plot(source)
plt.show()
ひと区間を 25 + 1 個として、先頭から区間をずらしながらデータを作ります。
chunk = 25
data = np.array([source[start: start + chunk + 1] for start in range(0, len(source) - chunk)])
print('Shape of the data:', data.shape)
print('First 3 data:\n', data[:3])
Shape of the data: (175, 26)
First 3 data:
[[-0.00640051 0.01538314 0.13029948 0.18091355 0.24072667 0.29205048
0.33858942 0.43770639 0.46171914 0.51250952 0.59989864 0.6403382
0.6480051 0.73032644 0.73895723 0.83755051 0.87972545 0.87573036
0.9394832 0.88774103 0.95158113 0.92511181 0.97509948 0.95176779
0.96074273 1.00967453]
[ 0.01538314 0.13029948 0.18091355 0.24072667 0.29205048 0.33858942
0.43770639 0.46171914 0.51250952 0.59989864 0.6403382 0.6480051
0.73032644 0.73895723 0.83755051 0.87972545 0.87573036 0.9394832
0.88774103 0.95158113 0.92511181 0.97509948 0.95176779 0.96074273
1.00967453 0.97062793]
[ 0.13029948 0.18091355 0.24072667 0.29205048 0.33858942 0.43770639
0.46171914 0.51250952 0.59989864 0.6403382 0.6480051 0.73032644
0.73895723 0.83755051 0.87972545 0.87573036 0.9394832 0.88774103
0.95158113 0.92511181 0.97509948 0.95176779 0.96074273 1.00967453
0.97062793 0.95280927]]
ある区間の先頭 25 個を予測に使う特徴データ (X) とし、最後を正解データ (y) とします。
X = data[:, :-1]
y = data[:, -1]
print('First 3 features (X):\n', X[:3])
print('First 3 labels (y):', y[:3])
First 3 features (X):
[[-0.00640051 0.01538314 0.13029948 0.18091355 0.24072667 0.29205048
0.33858942 0.43770639 0.46171914 0.51250952 0.59989864 0.6403382
0.6480051 0.73032644 0.73895723 0.83755051 0.87972545 0.87573036
0.9394832 0.88774103 0.95158113 0.92511181 0.97509948 0.95176779
0.96074273]
[ 0.01538314 0.13029948 0.18091355 0.24072667 0.29205048 0.33858942
0.43770639 0.46171914 0.51250952 0.59989864 0.6403382 0.6480051
0.73032644 0.73895723 0.83755051 0.87972545 0.87573036 0.9394832
0.88774103 0.95158113 0.92511181 0.97509948 0.95176779 0.96074273
1.00967453]
[ 0.13029948 0.18091355 0.24072667 0.29205048 0.33858942 0.43770639
0.46171914 0.51250952 0.59989864 0.6403382 0.6480051 0.73032644
0.73895723 0.83755051 0.87972545 0.87573036 0.9394832 0.88774103
0.95158113 0.92511181 0.97509948 0.95176779 0.96074273 1.00967453
0.97062793]]
First 3 labels (y): [1.00967453 0.97062793 0.95280927]
訓練データとテストデータに分けます。
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)
データの最初の区間分から残りの部分を予想してプロットする関数です。
def plot_predict(predict, start, length):
history = start
for i in range(length):
feature = history[-chunk:].reshape(1, -chunk)
pred = predict(feature)
history = np.append(history, pred)
plt.figure(figsize=(16,4))
plt.plot(np.sin(2 * np.pi * np.arange(0, len(start) + length) / period), '--', label='reference')
plt.plot(history, label='predict')
plt.legend()
plt.show()
scikit-learn の LinearRegression で予測
比較対象として、単純な線形回帰でやってみます。
def sklearn_linear():
reg = LinearRegression()
reg.fit(X_train, y_train) # 学習
def predict(features):
return reg.predict(features)
y_test_pred = predict(X_test)
print('r2_score: %.3f' % r2_score(y_test, y_test_pred)) # 検証
plot_predict(predict, source[:chunk], 300)
%time sklearn_linear()
r2_score: 0.998
CPU times: user 557 ms, sys: 74.8 ms, total: 632 ms
Wall time: 264 ms
滅茶苦茶良過ぎでは。。。
小さすぎて分かりづらいですが、グラフの前の方の赤いギザギザした部分が入力データです。ここは学習に使った物と同じです(本来なら新しく作り直すべきですね)。x = 25 以降のなめらかな赤いカーブは予測で得られた物です。学習データにノイズが乗っていたにも関わらず、ノイズが除去されて綺麗なカーブになっています。バックの青い点線は参考までに普通のサインカーブを描いた物です。予測がかなり正確である事が分かります。
TensorFlow の RNN で予測
RNN の一種 LSTM というのを使ってみます。
def tensorflow_lstm():
model = keras.Sequential([
keras.layers.LSTM(256, batch_input_shape=(None, chunk, 1)),
keras.layers.Dense(1)
])
model.compile(optimizer=tf.train.AdamOptimizer(),
loss='mean_squared_error',
metrics=['accuracy'])
model.summary()
model.fit(X_train.reshape(-1, chunk, 1), y_train, epochs=20)
def predict(features):
return model.predict(features.reshape(-1, chunk, 1))[:,0]
y_test_pred = predict(X_test)
print('r2_score: %.3f' % r2_score(y_test, y_test_pred))
plot_predict(predict, source[:chunk], 300)
%time tensorflow_lstm()
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
lstm (LSTM) (None, 256) 264192
_________________________________________________________________
dense (Dense) (None, 1) 257
=================================================================
Total params: 264,449
Trainable params: 264,449
Non-trainable params: 0
_________________________________________________________________
Epoch 1/20
131/131 [==============================] - 1s 7ms/step - loss: 0.3450 - acc: 0.0000e+00
Epoch 2/20
131/131 [==============================] - 0s 2ms/step - loss: 0.0709 - acc: 0.0000e+00
Epoch 3/20
131/131 [==============================] - 0s 2ms/step - loss: 0.0202 - acc: 0.0000e+00
Epoch 4/20
131/131 [==============================] - 0s 2ms/step - loss: 0.0075 - acc: 0.0000e+00
Epoch 5/20
131/131 [==============================] - 0s 2ms/step - loss: 0.0042 - acc: 0.0000e+00
Epoch 6/20
131/131 [==============================] - 0s 2ms/step - loss: 0.0046 - acc: 0.0000e+00
Epoch 7/20
131/131 [==============================] - 0s 2ms/step - loss: 0.0032 - acc: 0.0000e+00
Epoch 8/20
131/131 [==============================] - 0s 2ms/step - loss: 0.0020 - acc: 0.0000e+00
Epoch 9/20
131/131 [==============================] - 0s 2ms/step - loss: 0.0020 - acc: 0.0000e+00
Epoch 10/20
131/131 [==============================] - 0s 3ms/step - loss: 0.0014 - acc: 0.0000e+00
Epoch 11/20
131/131 [==============================] - 0s 2ms/step - loss: 0.0015 - acc: 0.0000e+00
Epoch 12/20
131/131 [==============================] - 0s 2ms/step - loss: 0.0013 - acc: 0.0000e+00
Epoch 13/20
131/131 [==============================] - 0s 2ms/step - loss: 0.0010 - acc: 0.0000e+00
Epoch 14/20
131/131 [==============================] - 0s 2ms/step - loss: 0.0013 - acc: 0.0000e+00
Epoch 15/20
131/131 [==============================] - 0s 2ms/step - loss: 0.0011 - acc: 0.0000e+00
Epoch 16/20
131/131 [==============================] - 0s 2ms/step - loss: 0.0011 - acc: 0.0000e+00
Epoch 17/20
131/131 [==============================] - 0s 2ms/step - loss: 0.0012 - acc: 0.0000e+00
Epoch 18/20
131/131 [==============================] - 0s 2ms/step - loss: 0.0014 - acc: 0.0000e+00
Epoch 19/20
131/131 [==============================] - 0s 2ms/step - loss: 0.0016 - acc: 0.0000e+00
Epoch 20/20
131/131 [==============================] - 0s 2ms/step - loss: 0.0016 - acc: 0.0000e+00
r2_score: 0.998
CPU times: user 33.9 s, sys: 8.18 s, total: 42.1 s
Wall time: 9.5 s
決定係数 r2_score だけ見ると似たような結果ですが、グラフを描いてみると線形回帰の方が断然良いです。
LinearRegression で Sin 波の予測が出来る理由
相関関係を見つける LinearRegression で何故このような周期的な振る舞いを検出出来るのか不思議です。予測する値が直近の値に強く影響されるのは当然なのですが、予測する値が上り坂の位置にいるのか下り坂の位置にいるのか何故分かるのだろう? 試しに予測器の偏回帰係数を見てみます。
def sklearn_linear_coef():
reg = LinearRegression()
reg.fit(X_train, y_train)
plt.plot(reg.coef_)
sklearn_linear_coef()
予想どうり直近のデータの影響が強く、昔のデータの影響が低いという事が分かります。これで LinearRegression が上り坂か下り坂かをどう判断するのかちょっと考えてみます。
本題からはずれますが、LinearRegression は説明変数に定数を掛けて足し合わせるだけなのに、綺麗なサインカーブが描けるのは非常に面白いなと思いました。
結論
サインカーブの予想には予想に反して RNN (LSTM) より LinearRegression の方が良いことが分かりました。 LinearRegression にこのような使い方が出来るとは驚きでした。
ちなみに色々試すと LinearRegression では過去データの数 chunk = 8 くらいでも完璧に予測しますが、LSTM では全然だめでした。