KerasでLSTMを学習する手順を整理してみた

概要

以下の記事でChainerを使った場合のLSTMを学習する手順を整理しましたが、本記事ではKerasを使った場合について同様に整理してみました。結論から言うと、単純にLSTMを使いたい、と言うニーズであればKerasの方がかなりお手軽でした。

ChainerでLSTMを学習する手順を整理してみた


kerasを使ってLSTMを使うまでの流れ

まずはtensorflowとkerasをインストールします。

pip install tensorflow
pip install keras

次にkerasのLSTMに投げ込むデータセットを作成します。おそらくここが唯一分かりにくい部分ですので、絵を書いてみました。左側が元データ、右側がkerasのLSTMが必要とするデータの形式です。kerasのLSTMでは、左の列から右の列に向けてデータをLSTMに投入していき、一番右の列が出力として得られる仕様になっているようです(return_sequences=Falseとした場合)。従って、元データを下の絵のように変換した後、学習時はfitに一番右の列を除くデータを説明変数、一番右の列を目的変数として与えて使うことになります。

以下が、sin波とcos波を例とした場合のサンプルコードです。timestepsという変数が列数を表しています(上の絵で5になっているもの)。lstm_data_xが上絵右の色が薄い部分(説明変数)、lstm_data_yが同色が濃い部分(目的変数)になります。多分、パラメータを変えれば他のデータでも動作すると思います。

def sincos(T=100):
   x = np.arange(0, 2*T+1)
   return np.concatenate([np.sin(2.0 * np.pi * x / T).reshape(-1, 1), np.cos(2.0 * np.pi * x / T).reshape(-1, 1)], axis=1)

if __name__ == "__main__":

   #データセット作り
   data = sincos()

   timesteps = 10
   data_dim = data.shape[1]

   lstm_data = []
   index_data = []

   for i in range(timesteps):

       length = data[i:-1].shape[0] // timesteps
       lstm_data.append(data[i:i+length*timesteps].reshape(length, timesteps, data_dim))
       index_data.append(np.arange(i, i+(length*timesteps), timesteps))

   lstm_data = np.concatenate(lstm_data, axis=0)
   index_data = np.concatenate(index_data, axis=0)
   lstm_data = lstm_data[pd.Series(index_data).sort_values().index]

   lstm_data_x = lstm_data[:, :-1, :]
   lstm_data_y = lstm_data[:, -1, :]

ここから先は簡単で、モデルの定義、学習、予測、と進みます。

   #モデル定義
   model = Sequential()
   model.add(LSTM(hidden, input_shape=(timesteps-1, data_dim), stateful=False, return_sequences=False))
#    model.add(BatchNormalization())
   model.add(Dense(data_dim))
   model.compile(loss="mean_squared_error", optimizer='adam')

   #学習
   model.fit(lstm_data_x, lstm_data_y,
             batch_size=32,
             epochs=20,
             validation_split=0.1,
             )

   #保存と読み込み
   model.save("sincos_model.h5")
   load_model = load_model("sincos_model.h5")

   #予測
   lstm_data_y_predict = model.predict(lstm_data_x)

ひと手間かかりますが、再帰的な予測をする使い方もちゃんとできます。

    #再帰予測
   lstm_data_future = pd.DataFrame(index=range(300), columns=['sin', 'cos'], data=0)
   lstm_data_future.iloc[:timesteps-1, :] = lstm_data_x[-1, :, :]

   for i in lstm_data_future.index[timesteps-1:]:

       x = lstm_data_future.iloc[i-timesteps+1:i, :].values.reshape(1, timesteps-1, -1)
       y = model.predict(x)
       lstm_data_future.iloc[[i], :] = y

まとめ

LSTMを使うという視点で考えると、Chainerに比べて遥かに簡単という印象です。用途に応じて使い分けていかないといけないですね。

ソースコード

以下にソースコード全文を載せておきます。

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

from sklearn.datasets import load_iris

from keras.models import Sequential, load_model
from keras.layers.core import Dense, Activation
from keras.layers import BatchNormalization
from keras.layers.recurrent import LSTM
from keras.utils import np_utils
from keras.optimizers import Adam

def sin(T=100):
   x = np.arange(0, 2*T+1)
   return np.sin(2.0 * np.pi * x / T).reshape(-1, 1)

def sincos(T=100):
   x = np.arange(0, 2*T+1)
   return np.concatenate([np.sin(2.0 * np.pi * x / T).reshape(-1, 1), np.cos(2.0 * np.pi * x / T).reshape(-1, 1)], axis=1)

if __name__ == "__main__":

   #データセット作り
   data = sincos()

   timesteps = 10
   hidden = 100
   data_dim = data.shape[1]

   lstm_data = []
   index_data = []

   for i in range(timesteps):

       length = data[i:-1].shape[0] // timesteps
       lstm_data.append(data[i:i+length*timesteps].reshape(length, timesteps, data_dim))
       index_data.append(np.arange(i, i+(length*timesteps), timesteps))

   lstm_data = np.concatenate(lstm_data, axis=0)
   index_data = np.concatenate(index_data, axis=0)
   lstm_data = lstm_data[pd.Series(index_data).sort_values().index]

   lstm_data_x = lstm_data[:, :-1, :]
   lstm_data_y = lstm_data[:, -1, :]

   #モデル定義
   model = Sequential()
   model.add(LSTM(hidden, input_shape=(timesteps-1, data_dim), stateful=False, return_sequences=False))
#    model.add(BatchNormalization())
   model.add(Dense(data_dim))
   model.compile(loss="mean_squared_error", optimizer='adam')

   #学習
   model.fit(lstm_data_x, lstm_data_y,
             batch_size=32,
             epochs=20,
             validation_split=0.1,
             )

   #保存と読み込み
   model.save("sincos_model.h5")
   load_model = load_model("sincos_model.h5")

   #予測
   lstm_data_y_predict = model.predict(lstm_data_x)

   plt.figure()
   plt.title('sin')
   plt.plot(lstm_data_y[:, 0], lw=2)
   plt.plot(lstm_data_y_predict[:, 0], '--', lw=2)
   plt.figure()
   plt.title('cos')
   plt.plot(lstm_data_y[:, 1], lw=2)
   plt.plot(lstm_data_y_predict[:, 1], '--', lw=2)

    #再帰予測
   lstm_data_future = pd.DataFrame(index=range(300), columns=['sin', 'cos'], data=0)
   lstm_data_future.iloc[:timesteps-1, :] = lstm_data_x[-1, :, :]

   for i in lstm_data_future.index[timesteps-1:]:

       x = lstm_data_future.iloc[i-timesteps+1:i, :].values.reshape(1, timesteps-1, -1)
       y = model.predict(x)
       lstm_data_future.iloc[[i], :] = y

   plt.figure()
   lstm_data_future.iloc[timesteps:].plot(title='future') 

2 thoughts on “KerasでLSTMを学習する手順を整理してみた

  1. k.m

    ありがとうございます。
    Google先生にいくら聞いてもLSTMの使い方がわからなくて出力をbinaryにするという
    DNN的な間抜けな使い方をしていたのですが、この記事でよく解りました。
    数年前にpythonでデータ分析をしてmt4で取引をするという面倒な方法でシステムトレードに
    チャレンジして挫折し、oandaAPIを使うと全てpythonで完結することに気づいて
    再チャレンジしているところです。

    Reply
    1. Rosyuku Post author

      お役に立ててよかったです。私の周りでは、LSTMはディープラーニングで時系列分析をしたいという方が最初にトライしている場合が多く、そのようなほぼすべての方が苦戦されている印象があります。ロジックもかなりややこしいですよね。私もトレードに使っていきたいという思いがあったので、oandaAPI、勉強させていただきます。

      Reply

k.m へ返信する コメントをキャンセル

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

CAPTCHA