DeepLearningで時系列予測 その1

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

前回の記事ではKerasを動かすための環境構築の手順を紹介しました。
この記事ではDeepLearningで時系列予測の第一弾としてAIにsin波を学習させます。

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

jupyter Notebookを使う

DeepLearningではデータを理解することが非常に重要です。
jupyter Notebookを使うと簡単にグラフ作成等ができるため、データの可視化が容易になります。
前回の記事で作成したVirtualenvの開発環境「venv」の中で下記のコマンドを実行します。

(venv) $ jupyter notebook

これでjupyter Notebookが起動し、ブラウザでアクセスするためのURLが表示されます。

ゲストOS上のjupyterをホストOSのブラウザから見るためのSSH転送設定

jupyter NotebookをVMware上の仮想環境で実行しているため、このままではホストのブラウザからアクセスすることはできません。
そこで、SSHターミナルのSSH転送機能を使います。

設定方法は使っているSSHターミナルソフトによりますが、TeraTermの場合下記のように設定します。

設定項目内容
ローカルのポート任意 例)2222
リモート側ホスト127.0.01
リモート側ポート8888
SSH転送の設定

上記設定の場合、ブラウザのURLに下記のように入力すると、jupyter Notebookに接続できます。
http://127.0.0.1:2222/?token=(ターミナル上に表示されたトークン) 

Sin波を作る

DeepLearningを始めるためには、まずデータが必要です。
最も簡単に作れそうな時系列データということで、sin波を作ってみましょう。

下記のコードをjupyter Notebook上で実行するとsin波が描画されます。

import numpy as np
import pandas as pd
import plotly.offline as offline
import plotly.graph_objs as go
offline.init_notebook_mode()
x = np.linspace(0, 15, 1001)
wave = np.sin(x)
wavedf = pd.DataFrame(wave, columns=['Wave'])
wave = go.Scatter(
    x = x,
    y = wave,
    mode = 'lines',
    name = 'lines',
    yaxis = 'y2'
)
offline.iplot([wave], filename='scatter-mode', image="png")
上記コードで生成されるsin波

TimeseriesGeneratorでsin波を時系列データにする

時系列データを予測するためにはデータを時系列データに変換しなければなりません。
過去10個のデータを見て11個目を予測するイメージです。

TimeseriesGeneratorはこのデータ変換を容易に行うことができます。

from keras.preprocessing.sequence import TimeseriesGenerator
n_timesteps = 10
seq = np.arange(20)
generator = TimeseriesGenerator(seq, seq, length=10, batch_size=1)
print(seq)
print(generator[0])
print(generator[1])
print(generator[9])

実行結果は以下のようになります。

[ 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19]
(array([[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]]), array([10]))
(array([[ 1,  2,  3,  4,  5,  6,  7,  8,  9, 10]]), array([11]))
(array([[ 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]]), array([19]))

dataは学習させるデータを、targetにはdataと同じ長さのデータを設定します。
今回は同一のデータを与えることで、t-9,t-8,t-7...tのデータからt+1を予測できるようにします。
TimeseriesGeneratorのlengthに長さを与えると、連続値をその長さのデータを時系列データに変換できます。

sin波を学習させるモデルを作る

DeepLearningは生物の脳にあるニューロンを模したネットワークを構築し、
学習データと予測のずれを重みとして学習していきます。
このニューロンをどのように結合するのか、何個くらいのニューロンを用意するかで予測精度や学習速度が変わります。
今回は時系列予測でよく使われるLSTMを使います。

from keras.models import Sequential
from keras.layers import Dense
model = Sequential()
model.add(LSTM(100, input_shape=(n_timesteps, 1)))
model.add(Dense(1))
model.summary()

コードを実行すると下記のような出力になり、これが今回作ったモデルの概要です。

Model: "sequential_1"
_________________________________________________________________
Layer (type)                 Output Shape              Param #
=================================================================
lstm_10 (LSTM)               (None, 100)               40800
_________________________________________________________________
dense_15 (Dense)             (None, 1)                 101
=================================================================
Total params: 40,901
Trainable params: 40,901
Non-trainable params: 0
_________________________________________________________________

sin波をDeepLearningで学習させる

さて、実際にsin波をTimeseriesGeneratorで時系列データに変換したものを、
上記のモデルで学習させてみましょう。

import numpy as np
import pandas as pd
import plotly.offline as offline
import plotly.graph_objs as go
offline.init_notebook_mode()
x = np.linspace(0, 15, 1001)
wave = np.sin(x)
# LSTMの入力は3次元のため(バッチサイズ, 時系列データ数, 特徴数)特徴数に1次元使うように変換する
wave = wave.reshape(-1, 1)
n_timesteps = 50
generator = TimeseriesGenerator(wave, wave, 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'])
history = model.fit(generator, epochs=10, verbose=2, shuffle=False)
Model: "sequential_2"
_________________________________________________________________
Layer (type)                 Output Shape              Param #
=================================================================
lstm_10 (LSTM)               (None, 100)               40800
_________________________________________________________________
dense_15 (Dense)             (None, 1)                 101
=================================================================
Total params: 40,901
Trainable params: 40,901
Non-trainable params: 0
_________________________________________________________________
Epoch 1/5
951/951 - 19s - loss: 0.0017 - mae: 0.0122
Epoch 2/5
951/951 - 19s - loss: 3.8856e-04 - mae: 0.0065
Epoch 3/5
951/951 - 20s - loss: 9.7383e-06 - mae: 0.0019
Epoch 4/5
951/951 - 19s - loss: 1.7384e-05 - mae: 0.0022
Epoch 5/5
951/951 - 19s - loss: 5.0634e-04 - mae: 0.0087

学習を5ステップ実行し、学習が進むにつれてlossとmaeが減少しています。
maeはMeanAbsoluteErrorの略で、実際の値とAIが予測した結果の差の絶対値の平均値です。
つまり、小さくなればなるほど、予測の精度が向上しているといえます。

モデルを評価する

せっかくなので、jyputer Notebook上で学習の履歴をプロットしてみましょう。
上記の学習後に下記のコードを実行すると、epochごとのlossとmaeを描画できます。

import matplotlib.pyplot as plt
mae = history.history['mae']
loss = history.history['loss']
epochs = range(len(mae))
plt.plot(epochs, mae, 'b' ,label = 'training mae')
plt.title('Training mae')
plt.legend()
plt.figure()
plt.plot(epochs, loss, 'b' ,label = 'training loss')
plt.title('Training loss')
plt.legend()
plt.show()
学習が進むとmaeとlossが減少する

まとめ

今回はsin波を作成し、シンプルなモデルを作成することで、AIにsin波を予測させることができました。
データを与えるだけでsin波がどんなものかを学習できるって、凄いことだと思います。

次回の記事ではDeepLearningを学習するうえで大切なテクニック等を交えて解説していきたいと思います。

Follow me!