暑い日々が続くのでgluonTS(LSTM)を用いた気温予測を試みる

## この暑さはいつまで続くのか。

過去の気温から未来の予測が出来ないか。
気を紛らすために確率的時系列モデリングのためのGluonツールキットの[**GluonTS**](https://gluon-ts.mxnet.io/index.html)を試してみました。

### インストール
利用するためには、mxnet,gluonライブラリが必要です(この[記事](https://cpp-learning.com/mxnet_gluonts/)を参考にしました)。
anacondaなどでPythonを導入していると、pipですぐインストールできます。

~~~
$pip install -U pip
$pip install mxnet
$pip install gluonts
~~~

pip自体が古いと、この後のソースコードを実行する際にエラーが出る場合があるので、pip自体のアップデートをしておくとよいと思います。

### データ取得
気温データをcsvファイルで取得したいので、気象庁の[過去の気象データ・ダウンロードサイト](https://www.data.jma.go.jp/gmd/risk/obsdl/index.php)からデータを取得することにしました。

例として佐賀県佐賀市のの2018/8/14~2020/8/14の2年分のデータをダウンロードしました。
年度推移を覚え込ませるために、なんとなく2年分のデータを取得しました。

~~~
#jupyter notebook上で動作確認を実施
import pandas as pd
import datetime
%matplotlib inline
 
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np

#まずは佐賀県の気温データの読み込み
tempera_data = pd.read_excel("saga_weather_data_20200815.xlsx") #※加工しやすいようにexcelファイルに修正
tempera_data["time"] = pd.to_datetime(tempera_data['年月日'])
~~~

現状こんな感じのデータです。
![temperature_list.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/684711/9b8f7c9b-69b1-f90d-5d73-d370cf3efb4e.png)

このDataFrameの"time","平均気温(℃)"列を用いていきます。

~~~
analysis_data = tempera_data[["time", '平均気温(℃)']]
~~~

どんなデータか可視化してみます。

~~~
plt.figure(figsize=(15, 5))
plt.plot(analysis_data["time"], analysis_data["平均気温(℃)"])
plt.grid(True)
plt.show()
~~~

![temperature.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/684711/7d36b701-33ee-744e-19a2-b909588c2555.png)

直近の7日分を予測するために、学習：1日目~725日目、評価：726日目～とすることにしました。
以下の図でいうところのオレンジ色の部分です。

~~~
tmp_time = np.arange(0, len(analysis_data))
analysis_data["re_time"] = tmp_time #時間情報を変換
plt.figure(figsize=(15, 5))
 
# 推論したい領域（網掛け部分）
plt.axvspan(725,len(tmp_time),color="orange")
 
plt.plot(tmp_time, analysis_data["平均気温(℃)"])
plt.xlabel("[day]")
plt.grid(True)
plt.show()
~~~

![temperature_ver2.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/684711/7a1f5ba2-b650-6655-ba50-ae618b1d98f3.png)

### 学習と評価
学習と評価のデータを定義します。

~~~
from gluonts.dataset.common import ListDataset

#make_train
predict_length = 7
training_data = ListDataset(
    [{"start": analysis_data["time"].values[0], "target": analysis_data.iloc[:len(tmp_time)-predict_length, 1]}],
    freq = "24H")

#make_test
test_data = ListDataset(
    [{"start": analysis_data["time"].values[0], "target": analysis_data.iloc[:len(tmp_time), 1]}],
    freq = "24H")
~~~

学習をするため、estimatorを定義します。
パラメータは参考記事や[公式チュートリアル](https://gluon-ts.mxnet.io/examples/basic_forecasting_tutorial/tutorial.html)などを参考に設定しました。

~~~
from gluonts.model.simple_feedforward import SimpleFeedForwardEstimator
from gluonts.trainer import Trainer
 
estimator = SimpleFeedForwardEstimator(freq="24H",
                            context_length=20,
                            prediction_length=10,
                            trainer=Trainer(epochs=300,
                                            batch_size=32,
                                            learning_rate=0.001))
 
predictor = estimator.train(training_data=training_data)
~~~

どのような推論結果になるのか、可視化してみます。

~~~
from gluonts.dataset.util import to_pandas
 
for test_entry, forecast in zip(test_data, predictor.predict(test_data)):
    plt.figure(figsize=(15, 5))
    to_pandas(test_entry).plot(linewidth=2)
    forecast.plot(color='g', prediction_intervals=[50.0, 90.0])
 
plt.legend(["observations", "median prediction", "90% confidence interval", "50% confidence interval"], 
          loc='lower left')
plt.grid(which='both')
~~~

![result.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/684711/cc89e276-75f7-1fcd-cc88-0e95966d283c.png)

バラつきが大きいものの、なんとな～く予測が出来ていそうです。

### 今後
* もっと精度よく推論するため、GluonTSの使い方を色々試してみたい（コメント等でアドバイス頂けると嬉しいです）。

## 参考記事

ありがとうございました！

* MXNetとLSTMで時系列データ予測 -入門から実践まで- : https://cpp-learning.com/mxnet_gluonts/
  →解説が丁寧な上、敵が波動拳を撃つタイミングを予測することにチャレンジしている記事です。
* Gluon Quick Start Tutorial :https://gluon-ts.mxnet.io/examples/basic_forecasting_tutorial/tutorial.html
  →図示する部分で参考になりました。