DeepLearningで時系列予測 その2

Metclomerのシステム屋、escgreenです。

前回の記事ではKerasで時系列予測の練習としてsin波を学習させました。
この記事では単純なsin波ではなく、より複雑な波を学習させてみます。

私はデータサイエンティストではなく、普通のエンジニアです!
そのため、的外れなことをしたり、間違うこともあるかもしれません。
ですが、DeepLearning初心者にもわかりやすく記事を書いていきたいと思います。

時系列予測を学ぶモチベーション

AIを始める際にmnistから入る人は多いです。
mnistは0~9が描かれた手書きの画像を入力として、0~9の正解ラベルを予測するものです。
mnistを実践するとAIの素晴らしさと可能性を感じることができるのですが、
次に何をやろうかと悩んでしまう人が多いのではないでしょうか。(私がそうでした)

私は時系列予測は応用できることが多いと考えています。
ビジネスでは、受注数量、物量予測などが、
工学ではジャイロセンサーの数値から姿勢推定など、
金融関係では株価や為替の予測など・・。
今後、これらの実践的な取り組みも記事にしていきたいと考えています。

sin波にひと手間加えて複雑な波を作る

前回sin波を作りましたが、sin波は単純なので学習も容易そうでした。
今回はひと手間加えることで、DeepLearningがどのようなデータに対応できるのかを試してみたいと思います。

sin波を組み合わせる

複数のsin波を組み合わせる方法は、numpyの配列を足し合わせるだけです。

import numpy as np
# x軸を作る
x = np.linspace(0, 15, 1001)
# 基本となるsin波
wave1 = np.sin(x)
wave2 = np.sin(x + np.pi) / 4
wave3 = np.sin(x * 8) / 8
# sin波の組み合わせ
w12 = wave1 + wave2
w23 = wave2 + wave3
w123 = wave1 + wave2 + wave3

実際の波形を見てみないとイメージできないと思うのでjupyter Notebook上で描画してみます。
まずは基本のsin波です。

import plotly.offline as offline
import plotly.graph_objs as go
offline.init_notebook_mode()
wave1p = go.Scatter(
    x = x,
    y = wave1,
    mode = 'lines',
    name = 'wave1',
    yaxis = 'y2'
)
wave2p = go.Scatter(
    x = x,
    y = wave2,
    mode = 'lines',
    name = 'wave2',
    yaxis = 'y2'
)
wave3p = go.Scatter(
    x = x,
    y = wave3,
    mode = 'lines',
    name = 'wave3',
    yaxis = 'y2'
)
offline.iplot([wave1p, wave2p, wave3p], filename='scatter-mode', image="png")

上記コードを実行すると、基本のsin波が描画されます。

組み合わせの基本となるsin波

次にsin波を組み合わせた複合波を描画します。

w12p = go.Scatter(
    x = x,
    y = w12,
    mode = 'lines',
    name = 'w12',
    yaxis = 'y2'
)
w23p = go.Scatter(
    x = x,
    y = w23,
    mode = 'lines',
    name = 'w23',
    yaxis = 'y2'
)
w123p = go.Scatter(
    x = x,
    y = w123,
    mode = 'lines',
    name = 'w123',
    yaxis = 'y2'
)
offline.iplot([w12p, w23p, w123p], filename='scatter-mode2', image="png")
基本のsin波を組み合わせてできた波

複合波をプロットしてみると興味深いことがわかります。
w12はwave1とwave2の複合波です。wave1とwave2は逆位相になっているので、sin波の振幅が減っているだけです。
w23はwave2とwave3とwave3の複合波です。振幅の小さいwave2に周波数の高いwave3がノイズのように乗っています。
w123はwave1~wave3の複合波です。それぞれの特徴が合わさった波になっています。

それぞれの波をモデルに学習させる

モデルは時系列データに適したLSTMを使います。
前回、単純なsin波を学習したものと同様のものです。

from keras.preprocessing.sequence import TimeseriesGenerator
from keras.models import Sequential
from keras.layers import Dense, LSTM
n_timesteps = 50
epoch = 10
# LSTMの入力に合わせるために変換する
w12 = w12.reshape(-1,1)
w23 = w23.reshape(-1,1)
w123 = w123.reshape(-1,1)
generator12 = TimeseriesGenerator(w12, w12, length=n_timesteps, batch_size=1)
generator23 = TimeseriesGenerator(w23, w23, length=n_timesteps, batch_size=1)
generator123 = TimeseriesGenerator(w123, w123, length=n_timesteps, batch_size=1)
model = Sequential()
model.add(LSTM(100, input_shape=(n_timesteps, 1)))
model.add(Dense(1))
model.summary()
model.compile(loss='mean_squared_error', optimizer='adam', metrics=['mae'])
history12 = model.fit(generator12, epochs=epoch, verbose=0, shuffle=False)
model.compile(loss='mean_squared_error', optimizer='adam', metrics=['mae'])
history23 = model.fit(generator23, epochs=epoch, verbose=0, shuffle=False)
model.compile(loss='mean_squared_error', optimizer='adam', metrics=['mae'])
history123 = model.fit(generator123, epochs=epoch, verbose=0, shuffle=False)

さらに訓練の履歴をプロットしてみましょう。

import matplotlib.pyplot as plt
epochs = range(epoch)
plt.plot(epochs, history12.history['mae'], 'b' ,label = 'w12 mae', color="blue")
plt.plot(epochs, history23.history['mae'], 'b' ,label = 'w23 mae', color="red")
plt.plot(epochs, history123.history['mae'], 'b' ,label = 'w123 mae', color="green")
plt.title('Training mae')
plt.legend()
plt.figure()
plt.plot(epochs, history12.history['loss'], 'b' ,label = 'w12 loss', color="blue")
plt.plot(epochs, history23.history['loss'], 'b' ,label = 'w23 loss', color="red")
plt.plot(epochs, history123.history['loss'], 'b' ,label = 'w123 loss', color="green")
plt.title('Training loss')
plt.legend()
plt.show()
訓練の結果(mae, loss)

結果を見ると、w123は学習に苦戦しているようにみえますが10epoch目にはlossが減少してきています。
単純なsin波に近いw12に比べ、w23、w123は予測と実際の値との誤差が大きいことがわかります。

モデルに時系列データを予測させる

モデルに時系列データを学習させることができたので、次は実際に予測させてみましょう。
maeやlossを見るだけだと、実際にどのような予測が行われているかはわかりません。

下記のコードを実行すると、実際のデータとモデルが予測したデータがjupyter Notebook上に描画されます。

predict12 = model.predict(generator12,batch_size=1)
# LSTM用に変換した配列をもとに戻す
w12 = w12.reshape(1001)
# 学習データに使った時系列の長さが50不足するので、0で埋めておく
p12 = np.hstack((np.zeros(50), predict12.reshape(951)))
w12p = go.Scatter(
    x = x,
    y = w12,
    mode = 'lines',
    name = 'w12',
    yaxis = 'y2'
)
p12p = go.Scatter(
    x = x,
    y = p12,
    mode = 'lines',
    name = 'p12',
    yaxis = 'y2'
)
offline.iplot([w12p, p12p], filename='diff', image="png")

青線が実際の値で、赤線が予測値になります。
最初のほうがずれているのは、sin波を時系列データに変換したときに欠損した値を0埋めしているためです。
他の箇所は、大枠ではほぼ予測できていると言えそうです。

描画するコードはw12と同様のためw23, w123はプロット結果のみを載せます。

w23の予測結果
w123の予測結果

まとめ

前回に続きDeepLearningを使ってsin波を予測しました。
今回はsin波を足し合わせた複合波で、前回に比べると複雑な形状でしたがAIはこれを学習し、予測することができました。

Follow me!