LSTMでモデルを作成した際、シンプルな方法で予測する範囲を増やしたい

機械学習

結論

RepeatVectorを利用するとシンプルに構築できました。

経緯

モデルを作成した際、予測範囲を増やす方法として下記の方法があります。
予測データを次の予測データの算出に使う方法となります。

  • ①検証データ1で予測データAを算出する
  • ②検証データ1の最初のデータを削除し、検証データ2を作成する
  • ③予測したデータAを検証データ2の最後に追加する
  • ④検証データ2で予測データBを算出する
  • ⑤検証データ2の最初のデータを削除し、検証データ3を作成する
  • ⑥予測したデータBを検証データ3の最後に追加する
  • ⑦検証データ3で予測データCを算出する

上記フローのコードを作成すること自体は可能ですが、もっとシンプルな方法で実現できないか。
調べたところ、RepeatVectorを使用するとシンプルに実現できることが判りました。

方法

7つのデータから、3つのデータを予測するモデルを作成してみました。
訓練データと教師データは下記の通りです。

コード

import numpy as np

# tensorflowのライブラリを読み込む
import tensorflow as tf

# 乱数シードを固定する
tf.random.set_seed(1234)

# kerasのライブラリを読み込む
from keras.models import Sequential
from keras.layers import Dense, LSTM, RepeatVector, TimeDistributed

# RMSEを計算するためにsklearnのライブラリを読み込む
from sklearn.metrics import r2_score, mean_squared_error

# 訓練データを用意する
x_train = np.array([
    [1, 4, 7, 10, 13, 16, 19],
    [2, 5, 8, 11, 14, 17, 20],
    [3, 6, 9, 12, 15, 18, 21],
    [4, 7, 10, 13, 16, 19, 22],
    [5, 8, 11, 14, 17, 20, 23],
    [6, 9, 12, 15, 18, 21, 24],
    [7, 10, 13, 16, 19, 22, 25],
    [8, 11, 14, 17, 20, 23, 26]])

# 教師データを用意する
y_train = np.array([
    [22, 25, 28],
    [23, 26, 29],
    [24, 27, 30],
    [25, 28, 31],
    [26, 29, 32],
    [27, 30, 33],
    [28, 31, 34],
    [29, 32, 35]])

# 訓練データと教師データをLSTMで読み込める形式に変換する
x_train = x_train.reshape((x_train.shape[0], x_train.shape[1], 1))
y_train = y_train.reshape((y_train.shape[0], y_train.shape[1], 1))

# モデルを定義する
model = Sequential()

# 隠れ層の数は、10とする
# 活性化関数はreluに設定する
# input_shapeは、1回に入力するデータ=1行分のデータ、となる
model.add(LSTM(10, activation='relu', input_shape=(x_train.shape[1], x_train.shape[2])))

# 予測数の指定は、RepeatVectorで設定する。1回あたりの教師データの数が予測数となる。
model.add(RepeatVector(y_train.shape[1]))

# RepeatVectorを設定しているので、return_sequences=TrueとTimeDistributedを設定する
model.add(LSTM(10, activation='relu', return_sequences=True))
model.add(TimeDistributed(Dense(1)))

# モデルの情報を表示する
model.summary()

# モデルを作成する
model.compile(optimizer='adam', loss='mean_squared_error')

# モデルを訓練する
model.fit(x_train, y_train, epochs=1000, verbose=1)

# 検証データを用意する
x_test = np.array([12, 15, 18, 21, 24, 27, 30])
x_test = x_test.reshape((1, x_test.shape[0], 1))

# 正解データを用意する
y_test = np.array([33, 36, 39])

# 検証データを用いて予測する
y_predict = model.predict(x_test)

# 予測結果を1次元化する
y_predict = np.ravel(y_predict)

# 予測データを表示する
print(f'予測結果: {y_predict}')

# モデルの精度を評価する
# R2、RMSEを計算する。R2は1.0に、RMSEは0.0に近いほど、モデルの精度は高い
r2_score = r2_score(y_test, y_predict)
rmse = np.sqrt(mean_squared_error(y_test, y_predict))

print(f'r2_score: {r2_score:.4f}')
print(f'rmse: {rmse:.4f}')

下記のデータで作成したモデルが想定通りに3つの予測データを算出するかを確認しました。

実行結果

予測結果: [32.80152  35.688915 38.342003]
r2_score: 0.9684
rmse: 0.4356

RepeatVectorとTimeDistributedについて

作成したコードのポイントとなるのは、RepeatVectorとTimeDistributedの設定となります。
該当するコードは下記となります。

# 予測数の指定は、RepeatVectorで設定する。1回あたりの教師データの数が予測数となる。
model.add(RepeatVector(y_train.shape[1]))

# RepeatVectorを設定しているので、return_sequences=TrueとTimeDistributedを設定する
model.add(LSTM(10, activation='relu', return_sequences=True))
model.add(TimeDistributed(Dense(1)))

RepeatVectorで予測数(1回あたりの教師データの数)を指定します。
TimeDistributedで出力する数を1にしています。

概念図(間違えているかもしれませんが)

コメント

  1. 入野 光博 より:

    始めてお便りを差し上げます。
    貴方の下記3文献を参考に株関係の予測をtryし、他の方法より感覚的に良い結果を算出できたように感じています。
    PythonでKerasのLSTMを用いて、株価の予測を試してみた #https://relaxing-living-life.com/128/
    PythonでKerasのLSTMを用いて、複数の情報を基に株価の予測を試してみた #https://relaxing-living-life.com/147/
    LSTMでモデルを作成した際、シンプルな方法で予測する範囲を増やしたい #https://relaxing-living-life.com/174/

    ただ、KerasでのLSMTへのデータの渡し方が疑問点として、引っ掛かりまして、教えていただければとメールさせていただいた次第です。

    あなたの渡し方は、
    文献、#Kerasで多変量LSTM #https://qiita.com/tizuo/items/b9af70e8cdc7fb69397f
    と同じく
    1.input_shapeには、変数個数とルックバック数が入ります
    2.データをkerasのLSTMで受け付けられている形式に変換します。[行数]>[変数数]>[カラム数(ルックバック数)]
    ですが、

    2.に関しては以下の文献

    TensorFlow 2.x での多変量LSTMとデータの前処理 #https://qiita.com/ell/items/34f069651b551709d127  
    にて

    訓練データのNumpy配列について、、LSTMを多変量データで学習させるとき、データは [サンプル数, ルックバック数, 変数数] という形式でないといけません(超重要!)。
    ここで、ルックバック数とは「過去何回分のデータを1つのデータとするか」を意味します。

    との記述があり、混乱している次第です。
    よろしくお願いします。
    2022.06.12 irino

タイトルとURLをコピーしました