GRUによる需要電力予測

#1. はじめに
こんにちは,機械学習を学び始めて約3ヶ月ちょい,少しずつ理解が深まってきたように思えます.
今回は,今まで学んだ事を使って電力需要の予測を行いたいと思います.
具体的には,時系列データを扱える深層学習モデルの一種であるGRUを用いて予測をおこないたいと思います.

#2. 開発環境
>OS : Windows 10
>python の実行環境 : Google colaboratory

Google colaboratory を用いると環境構築を行わずに開発が可能です.また、GoogleからGPU提供されています(時間制限あり)

#3.	時系列データの予測
時系列データとは,一体どんなものがあるでしょうか？
時系列データとは,時間の経過と共に観察されたデータのことで,例えば株価や,気温,電力消費量等があります. 
時系列予測は,様々な分野で活躍しています. 例えば,ビジネスの場面においては, 価格戦略や販促戦略等の意思決定の過程で使用されることがあります.  時系列データを予測する方法論は,色々とあります. 古典的な方法であるARIMAモデル,状態空間モデル等や,後述する深層学習によって予測する方法があります. 深層学習で時系列データを扱える構造を持つものとしてRecurrent neural network (RNN)があります. 深層学習による予測には,以下のようなメリット[1]があります.

>① 深層学習による予測モデルでは,古典的な方法と比較して,より一般的な表現が可能. 
   層を重ねることによって得られる豊かな表現力によって、より柔軟に適用可能なモデルが構築できます.
>②長期依存性を持つ時系列に対するモデル化が可能.
>③ モデル構築の際に、時系列の性質に対する事前知識が不要. 
 深層学習においては,周期の長さやトレンドの大きさ等はデータから自動的に学習されます.
 
次にRNNの仕組みについて軽く触れて,そしてRNNの問題点とそれの改善策であるLSTMとGRUについて紹介します.

[1]: Deep learningは時系列予測でも最強なのか?より引用
https://www.sas.com/content/dam/SAS/documents/marketing-whitepapers-ebooks/sas-whitepapers/ja/viya-recurrent-neural-network.pdf

#4. Recurrent neural network (RNN)
##4.1 RNNとは
RNNはニューラルネットワークに時系列データを扱う構造を取り込んだものです. 
単純なRNN(Vanilla RNN)の構造は, 図1に示すようになっています.
![RNN.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/730627/2ce59eb1-2f04-d43e-640c-e41e573ad0de.png)
　　　　　　　　　　　　　　　　図1 　vanilla RNN

このようにRNNは, 前時刻の値を入力とする自己ループを持ちます. これにより時間的順序を持つデータを学習できるようになっています. しかし,この単純なRNNはいくつかの問題を抱えています.

##4.2 RNNの問題点
図1の単純なRNNにおいては,信号が再帰される度に同じ重みwが何度もかかるために,信号や勾配が消失,爆発する問題が起き,うまく学習が進まないという問題がおこります. この問題に対する解決策はいくつかありますが,ここではLSTMとGRUを紹介します.

#5. LSTM (Long-short-term-memory)とは
　先ほどの単純なRNNでは,勾配が消失,爆発してしまうことからうまく学習できないといった問題がありました. LSTM (図2)[2]では,CECと3つのゲート(入力ゲート,出力ゲート,忘却ゲート)を導入して,問題を改善しています. 
　CECの働きにより,誤差はその場で留まり続けることになります. これにより過去を遡っても誤差を逆伝搬することが可能となりました. しかし,CEC(cell)により入力重み衝突,出力重み衝突といった問題が発生します. この問題を改善するために導入されたのが入力ゲートと出力ゲートです. また, CECの値が必要なくなった時点で過去の情報を忘れることが必要になります,そのために導入されたのが忘却ゲートです. Peepholesは,後に追加された機能なので割愛します.
![LSTM.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/730627/0c448a5f-478a-6191-0d99-4c5612aabb14.png)
　　　　　　　　　　　　　　　　　　図2　LSTMの構成

[2]: LSTM: A Search Space Odyssey, Klaus Greff, Rupesh K. Srivastava, Jan Koutn´ ık, Bas R. Steunebrink, Jürgen Schmidhuber,TRANSACTIONS ON NEURAL NETWORKS AND LEARNING SYSTEMSより引用

#6. GRU (gated recurrent unit)とは
　GRUは,LSTMの代替となる方法です. LSTMは,CEC,入力ゲート,出力ゲート,忘却ゲートで構成されていましたが,GRUは更新ゲートとリセットゲートで構成されます.
役割的には,更新ゲートが入力・出力ゲートに近い働きをし,忘却ゲートがリセットゲートに近い働きを担います.
　GRUのメリットとして,LSTMと比較してパラメーター数が少ないので計算時間が抑えられるといった点があります. 性能としてはLSTMと同程度と報告されています.

#7. 使用したモジュール
今回は、先ほど挙げたGRUを用いて予測を行っていきたいと思います。

``````.python
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
import math
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import Activation
from keras.layers import GRU
from keras.layers import Dropout
from sklearn.preprocessing import MinMaxScaler
from sklearn.metrics import mean_squared_error
```

#8. 使用したデータとその前処理
##●使用したデータ
今回はKaggleのオープンデータ(https://www.kaggle.com/manualrg/spanish-electricity-market-demand-gen-price) を使用しました.
　日毎の需要電力のデータをデータフレーム形式で読み込んでいます. そして,元の形式だと時間情報まで含まれているので日付までの表示に変更しています.
　そして,2014年～2017年までのデータを訓練用,2018年のデータをテスト用として分割しています.

```.python
data = pd.read_csv("/content/drive/My Drive/data/spain_energy_market.csv", sep=",", parse_dates=["datetime"])
data = data[data["name"]=="Demanda programada PBF total"]#.set_index("datetime")
data["date"] = data["datetime"].dt.date
data.set_index("date", inplace=True)
data = data[["value"]]
data = data.asfreq("D")
data = data.rename(columns={"value": "energy"})
train_series = data.loc["2014":"2017"].values
test_series  = data.loc["2018"].values
```
データをplot表示すると,このような波形が得られます.
![energy_raw.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/730627/686fcab1-a277-9c88-5c8b-88a7ee6517e7.png)


##●データの加工
次に訓練データでの分布を元に,それぞれのデータセットの正規化を行っていきます.

```.python

scaler = MinMaxScaler(feature_range=(0, 1))
scaler_train = scaler.fit(train_series)
train = scaler_train.transform(train_series)
test = scaler_train.transform(test_series)
```

次に,今回は20点のデータを使用して1期先のデータを予測することを行うので,そのために直近の20点(予測する点から見て後方20点)とそのラベルデータ(1期先のデータ)とに分割を行っていきます.

```.python
def create_dataset(dataset, look_back):
    data_X, data_Y = [], []
    for i in range(look_back, len(dataset)):
        data_X.append(dataset[i-look_back:i, 0])
        data_Y.append(dataset[i, 0])
    return np.array(data_X), np.array(data_Y)
```

最後にGRUモデルに加えるためにデータの整形を行います.

```.python
train_X = train_X.reshape(train_X.shape[0], train_X.shape[1], 1)
test_X = test_X.reshape(test_X.shape[0], test_X.shape[1], 1)
```

#9. モデルの定義
今回は機械学習ライブラリの「Keras」を利用してGRUモデル構築を行います.
モデルの構築は以下のようになっています.

```.python
# GRUモデルの作成
model = Sequential()
model.add(GRU(40, return_sequences=False, batch_input_shape=(None, look_back, 1)))
model.add(Dropout(0.25))
model.add(Dense(1))
model.add(Activation("linear"))
model.compile(loss='mean_squared_error', optimizer='adam')
```

#10. データの学習と予測結果

##●学習と予測
モデルの学習は、model.fit()で行えます.
ここでエポック(epoch)=20と設定しているため,20回の反復処理が行われ学習が行われます.

```.python
history = model.fit(train_X, train_Y, epochs=20, batch_size=1, verbose=1)
```

そして,モデルでの予測は,model.predict()で行えます.
これにテストデータを入力として渡すことで,1期先の予測が行われます.

```.python
train_predict = model.predict(train_X)
test_predict = model.predict(test_X)
```

##●予測結果
学習時の損失は以下のような結果になりました.
損失関数の値は小さく抑えられることが出来たと思われます.
![loss.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/730627/2ea1bd0b-0495-90d0-4c54-581684fd1afb.png)
次に,テストデータを用いて1期先予測を行った結果は,以下のようになりました.
![forecast_testdata.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/730627/ef6d2383-58c6-701f-98f7-9f5b3d4e7f5f.png)
大まかな流れはあってそうですけど,ところどころ大きく外れた結果となってしまいました.



#11. まとめ
今回は,RNNモデルの亜種であるGRUモデルを用いて電力需要の予測をおこないました.
その結果,良い結果は得られませんでした.ハイパーパラメーターなどを再度設定しなおして学習すると良い結果が得られるかもしれませんが前処理の工程でいろいろ改善すべき点がありそうでした. また,ネットワークのアーキテクチャの改善も考えていきたいと思います。具体的には,GRUにおいてデータを双方向に学習するBidirectional GRUにて予測モデルを構築していこうと思います。