Win10 KerasでLSTMによる時系列データ学習の練習

Pythonニューラルネットワークライブラリkerasを用いて,RNNの1つであるLSTM(Long Short-Term Memory)による時系列データの学習をさせてみました.
これまでPythonを利用する際はPandasを主に使用していたため,今回はnumpyのndarrayの取り扱いやkerasの学習データのフォーマットを調べながら参考記事と同様のモデル作成と学習・予測をしています.

本記事のコード全体は以下に置いてあります.
github.com

環境

  • Windows 10 (64bit)
  • Anaconda
  • python 3.6.8
  • keras 2.2.4
  • tensorflow 1.13.1

開発環境の用意

Anacondaをインストールし,Anaconda Promptから以下2つのコマンドを実行します.
conda install keras
conda install tensorflow
今回GPUは使用しませんでした.

入力データの作成

学習させる入力データとして,ノイズを含むsin波とcos波を作成します.

import numpy as np
import matplotlib.pyplot as plt

#sinとcosの波形を作る
cycle = 2 # 周期の数
period = 100 # 1周期の時間
x = np.arange(0, cycle*period)
sin = np.sin(2.0 * np.pi * x / period)
cos = np.cos(2.0 * np.pi * x / period)

# numpy のndarrayを横に連結
y = np.vstack((sin, cos))

# ノイズを乗せる
noise = np.random.uniform(-0.05, 0.05, size=y.shape)
y = y + noise

# 波形の確認
plt.plot(x, y[0])
plt.plot(x, y[1])

f:id:ohtayo:20190506151507p:plain

入力データの形式変更

生成したnumpy配列をkerasに入力するデータの形式となるように変更します.

input_length = 10 # 1入力データの長さを10個とする
X = np.zeros([len(x)-input_length, input_length-1, 2]) #(190, 9, 2)
Y = np.zeros([len(x)-input_length, 2]) #(190, 2)
for i in range(0, len(X)):
    X[i, :, :] = y[:,i:i+input_length-1].T
    Y[i, :] = y[:,i+input_length-1].T

LSTMモデルの作成

LSTMモデルを作成します.sinとcosの2つの波形を予測するので入力ユニットと出力ユニットはそれぞれ2ずつ,隠れLSTM層のユニットは100としました.

from keras.layers.recurrent import LSTM
from keras.models import Sequential
from keras.layers import Dense, Activation
from keras.optimizers import Adam

# モデルの作成
model = Sequential()

# LSTM層の追加,隠れユニット100,入力は10×2サイズ
num_in_units = 2
num_hidden_units = 100
model.add(
    LSTM(units=num_hidden_units, 
         input_shape=(input_length-1, num_in_units), 
         kernel_initializer="random_normal", 
         stateful=False, 
         return_sequences=False
    )
)

# 全結合出力層の追加,出力は2ユニット
num_out_units = 2
model.add(Dense(units=num_out_units, kernel_initializer="random_normal"))

# 活性化関数は線形関数
model.add(Activation('linear'))

# MSEを誤差とし,Adamで最適化する
model.compile(loss="mean_squared_error", optimizer=Adam(lr=0.01, beta_1=0.9, beta_2=0.999))

学習の実行

fit()で学習を実行します.今回はepochを20,バッチサイズを32としました.

model.fit(X, Y, epochs=20, batch_size=32, validation_split=0.1)

学習中は以下のようなメッセージが出力されます.

Train on 171 samples, validate on 19 samples
Epoch 1/20
171/171 [==============================] - 4s 23ms/step - loss: 0.2567 - val_loss: 0.0184
Epoch 2/20
171/171 [==============================] - 0s 316us/step - loss: 0.0496 - val_loss: 0.0168
(中略)
Epoch 20/20
171/171 [==============================] - 0s 292us/step - loss: 0.0012 - val_loss: 0.0011

最終的に1割の検証データで誤差(MSE)の値が0.0011まで低減できています.

データ予測の実行

学習データをそのまま使った予測を実行してみます.

Y_predict = model.predict(X)
plt.figure()
plt.plot(Y[:, 0])
plt.plot(Y_predict[:, 0], ".")
plt.figure()
plt.plot(Y[:, 1])
plt.plot(Y_predict[:, 1], ".")

f:id:ohtayo:20190506152019p:plain
sin波形の元データ(実線)と予測データ(点線)
f:id:ohtayo:20190506152039p:plain
cos波形の元データ(実線)と予測データ(点線)
元の波形がノイズを含んでいても,良い精度で元のsin, cos波形を推測できていることがわかります.

LSTMは時系列のデータ予測モデルであり,予測した時刻の出力データを入力としてさらにその先の時刻のデータを繰り返し予測して出力ことができます. 予測は以下のように出力値を含んだデータを入力としてpredict()を繰り返し実行すればできます.

Y_future = np.zeros([300, 2]) # 予測値を格納する配列を用意
Y_future[:input_length-1, :] = X[-1, :, :] # Xの最終のinput_lengthのデータを入力とする
for i in range(input_length-1,len(Y_future)):
    x_temp = Y_future[i-input_length+1:i, :].reshape(1, input_length-1, -1)
    y_temp = model.predict(x_temp)
    Y_future[i, :] = y_temp
plt.plot(Y_future[input_length:]) 

f:id:ohtayo:20190506152708p:plain

参考文献

WindowsでKerasを用いたDeep Learning開発環境を整備する
Keras Documentation
kerasでlstmを学習する手順を整理してみた
入門 Keras (7) 最終回:リカレントニューラルネットワークを体験する
深層学習ライブラリKerasでRNNを使ってsin波予測