tensorflowを使って気温を予測してみた

こんにちは。最近気象データを用いて、発電所の発電量を予測できるのかということを調査していました。調査中に得た知見(必要なデータの取得や環境構築、予測手順)についてまとめました。

目次

  • 概要
  • はじめに
  • キーワード
  • 構成内容
  • 今回使用するデータ
  • データ取得先
  • そもそもgrib2ファイルとは
  • 動作環境(grib2ファイルから気温を取得するための環境)
  • grib2ファイルから気温を取得する
  • 動作環境(tensorflowを実行するための環境)
  • tensorflowを用いてlstmネットワークを構築し、予測する
  • 考察、まとめ

概要

気象庁が提供している気象データを用いて、将来の気温をtensorflowを用いて予測しました。使用したデータと予測結果は下記の通りになりました。
・使用したデータについて
 1時間毎の気温データを、教師データ用に1年間、評価データ用に1ヶ月
・予測結果
 教師データから予測モデルを作成し、未来1ヶ月を予測した結果、下記のような高い精度で予測できました。
  全てのデータは3%以内の誤差(※1絶対温度換算)
  90%のデータは3%以内の誤差、9%のデータは、10%以内の誤差、1%のデータは、20%以内の誤差(※1摂氏換算)

 結果をグラフ化したものが下記になります。
※1 絶対温度とは、原子・分子の熱運動がほとんどなくなる温度を0K(単位:ケルビン)とする温度のことで、絶対温度T(K)と摂氏温度t(℃ )は、T=t+273(K)という関係が成り立つ。

未来1ヶ月の実データと予測データ

 このブログでは予測するまでに必要なデータの取得や環境構築、予測手順についてまとめました。

はじめに

1.tensorflowとは
 tensorflowとはGoogleが開発している、機械学習に用いられるソフトウェアライブラリです。詳しい使い方やチュートリアルはこちらを参考にしてください。

2.予測方法について
 tensorflow内で、long short term memory(lstm)というリカレントニューラルネットワーク(RNN)のモデルを使って予測を行なっています。RNNとは、脳内にある神経細胞のつながりを数式的なモデルで表現したものです。lstmはRNNのモデルの中の一つで、長期間の情報を学習することが可能になっています。
予測を行う上で、モデルの詳細を理解しなくても実装することは可能ですが、概要程度は理解している方が、記事の後半に書いてあるソースコードを理解しやすくなるので、時間のある人は下記記事を参考にしてください。
ニューラルネットワークの基礎知識 – 1
13. ニューラルネットワークの基礎
リカレントニューラルネットワーク_RNN (Vol.17)
わかるLSTM ~ 最近の動向と共に
RNNとLSTMを理解する

キーワード

  • python
  • grib2
  • tensorflow
  • long short time memory(lstm)
  • docker

構成内容

本記事では、下記環境を前提に検証を行っています。
OS:macOS Sierra 10.12.6
Docker Desktop:19.03.02
Python:3.5.2(docker内のpythonのバージョン)

今回使用するデータ

 気象庁が気象データをgrib2というファイル形式で提供しています。今回は下記のファイルを使用します。

データ取得先

今回は検証のため、こちらからデータを取得してきます。企業活動で頻繁にデータを利用する方は、※気象業務支援センターからデータを直接購入してください。

※気象業務支援センターでは教育研究機関向けにデータを提供しています.企業活動等のためにデータを頻繁に必要とされる方は,気象業務支援センターからデータを直接購入し,データ提供スキーム全体の維持発展にご協力ください.
http://database.rish.kyoto-u.ac.jp/arch/jmadata/

そもそもgrib2ファイルとは

 grib2とは、世界気象機関(WMO)が定める形式で書かれたファイルで、気象データを扱うために世界中で使用されています。grib2ファイルには、地表面を格子状に分割した各地点の気象データが含まれています。
 grib2について詳しく知りたい方は、こちらを参照してください。

動作環境(grib2ファイルから気温を取得するための環境)

 grib2ファイルを読み込むためにpygribを使います。環境はdockerで準備しました。
(dockerはこちらからインストールしました。Download for Macボタンを押して、dmgファイルをダウンロードして、インストールしてください。設定は全てデフォルトでOKです。)

FROM ubuntu:latest

ENV DEBIAN_FRONTEND=noninteractive

# パッケージのインストールとアップデート
RUN apt-get update && apt-get -y upgrade
RUN apt-get -y install build-essential
RUN apt-get -y install git vim curl wget
RUN apt-get -y install zlib1g-dev \
libssl-dev \
libreadline-dev \
libyaml-dev \
libxml2-dev \
libxslt-dev \
libncurses5-dev \
libncursesw5-dev 

# pyenv のインストール
RUN git clone git://github.com/yyuu/pyenv.git /root/.pyenv
RUN git clone https://github.com/yyuu/pyenv-pip-rehash.git /root/.pyenv/plugins/pyenv-pip-rehash
ENV PYENV_ROOT /root/.pyenv
ENV PATH $PYENV_ROOT/bin:$PATH
RUN echo 'eval "$(pyenv init -)"' >> .bashrc

# anaconda のインストール
ENV ANACONDA_VER 4.1.1
ENV LD_LIBRARY_PATH=/lib/x86_64-linux-gnu:$PYENV_ROOT/versions/anaconda3-$ANACONDA_VER/lib
RUN pyenv install anaconda3-$ANACONDA_VER
RUN pyenv global anaconda3-$ANACONDA_VER
ENV PATH $PYENV_ROOT/versions/anaconda3-$ANACONDA_VER/bin:$PATH

# ライブラリのアップデート
RUN conda update -y conda
RUN pip install --upgrade pip
RUN conda install -c conda-forge pygrib=2.0.2

RUN conda install -c conda-forge/label/gcc7 jpeg

RUN pip install numpy --upgrade
RUN pip install pandas --upgrade

RUN mkdir /temp

上記のDockerfileをbuildします

docker build -t pygrib_ubuntu .

データの取得

データ取得するためのソースコードは下記です。

# -*- coding: utf-8 -*-
from datetime import timedelta
import pygrib
import pandas as pd
import sys
import numpy as np
import os

# 取得したい場所の緯度経度
latitude = 34.123456
longitude = 134.123456

# ファイル内の時刻はUTCなので、JSTに変換
time_diff = timedelta(hours=9)

def _read(srcfn):
    src = pygrib.open(srcfn)
    return src

def _close(src):
    src.close()

def create_csv(parameter_name, file_name):
    selected_item = gpv_file.select(parameterName = parameter_name )

    df = pd.DataFrame({
        "validDate" : [item.validDate + time_diff for item in selected_item],
        file_name : [
            item.data(
                lat1 = latitude - 0.025,
                lat2 = latitude + 0.025,
                lon1 = longitude - 0.03125,
                lon2 = longitude + 0.03125,
            )[0][0][0] for item in selected_item
        ]
    })

    df.to_csv( file_name + '/' + os.path.basename(sys.argv[1]) + '_' + file_name + '.csv', index=False, columns=["validDate", file_name])

# ファイル読み込み    
gpv_file = _read(sys.argv[1])

# CSVファイル作成
create_csv("Temperature","temperature")

 下記コマンドを実行して、任意の時間帯の気温を取得します。
(Z__C_RJTD_20190701000000_MSM_GPV_Rjp_Lsurf_FH00-15_grib2.binはgrib2ファイルのファイル名)

# ローカル上
docker run -it -v /path/to/somepath:/temp pygrib_ubuntu
# docker上
python /temp/get_temperature.py Z__C_RJTD_20190701000000_MSM_GPV_Rjp_Lsurf_FH00-15_grib2.bin

 上記の実行例では、2019/7/1 9:00~2019/7/2 1:00までのデータ、つまり、16時間分のデータがcsvファイルで取得できます。データの単位はケルビン(K)であり、℃ではない点に注意してください。これを学習に使用したい分だけ、取得します。今回の予測には学習用に1年分を、評価用に1ヶ月分を使用しています。

動作環境(tensorflowを実行するための環境)

次に、tensorflowを使用するための環境をdocker上に構築します。

FROM tensorflow/tensorflow:latest-py3

RUN apt-get update && apt-get -y upgrade
RUN pip install pandas --upgrade
RUN pip install matplotlib --upgrade
RUN pip install keras --upgrade
RUN pip install tensorflow --upgrade
RUN pip install sklearn --upgrade
RUN mkdir /temp

上記のDockderfileをbuildします

docker build -t tensor_flow_ubuntu .

予測するためのソースコードは下記です。使用できるモデルはいくつかあるのですが、今回はlong short time memory(lstm)というモデルで予測を行いました。lstmは、時系列データで学習できるようにするネットワークで、今回の予測に適しているので採用しました。

import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.pyplot as plt
import math
from keras.models import Sequential
from keras.layers import Dense, LSTM
from sklearn.preprocessing import MinMaxScaler
from sklearn.metrics import mean_squared_error

plt.switch_backend('agg')
dataframe = pd.read_csv('learn_temperature.csv', usecols=[1], engine='python')
dataset = dataframe.values
dataset = dataset.astype('float32')
plt.plot(dataset)
 
# 乱数を固定する
np.random.seed(7)
 
# データを正規化する
scaler = MinMaxScaler(feature_range=(0, 1))
dataset = scaler.fit_transform(dataset)

# 教師データと予測データに分割する
train_size = 8775
# train_size = int(len(dataset) * 0.7)
test_size = len(dataset) - train_size
train, test = dataset[0:train_size,:], dataset[train_size:len(dataset),:]

# 配列データを行列データに変換する
def create_dataset(dataset, maxlen):
    #dataX, dataY = [], []
    dataX = []
    dataY = []
    for i in range(len(dataset)-maxlen-1):
        # 時刻t、気温x(t)とすると、x(t),x(t+1)...x(t-maxlen)を入力とする
        a = dataset[i:(i+maxlen), 0]
        dataX.append(a)
        # x(t+maxlen)を出力とする
        dataY.append(dataset[i + maxlen, 0])
    return np.array(dataX), np.array(dataY)
 
# 行列データを作成
maxlen = 24
trainX, trainY = create_dataset(train, maxlen)
testX, testY = create_dataset(test, maxlen)
 
# 行列を[samples, time steps, features]という形に変換する
trainX = np.reshape(trainX, (trainX.shape[0], 1, trainX.shape[1]))
testX = np.reshape(testX, (testX.shape[0], 1, testX.shape[1]))

# LSTMネットワークを構築する
model = Sequential()
model.add(LSTM(4, input_shape=(1, maxlen)))
model.add(Dense(1))
model.compile(loss='mean_squared_error', optimizer='adam')
model.fit(trainX, trainY, epochs=10, batch_size=1, verbose=2)

# 予測する
trainPredict = model.predict(trainX)
testPredict = model.predict(testX)

# 正規化したデータを基に戻す
trainPredict = scaler.inverse_transform(trainPredict)
trainY = scaler.inverse_transform([trainY])
testPredict = scaler.inverse_transform(testPredict)
testY = scaler.inverse_transform([testY])

# グラフ化するために、教師データをずらす
trainPredictPlot = np.empty_like(dataset)
trainPredictPlot[:, :] = np.nan
trainPredictPlot[maxlen:len(trainPredict)+maxlen, :] = trainPredict

# グラフ化するために、予測データをずらす
testPredictPlot = np.empty_like(dataset)
testPredictPlot[:, :] = np.nan
print("len(trainPredict),len(dataset)")
print(len(trainPredict),len(dataset))
testPredictPlot[len(trainPredict)+(maxlen*2)+1:len(dataset)-1, :] = testPredict

# データセットと予測値をプロットする
plt.plot(scaler.inverse_transform(dataset), color ="g", label = "row")
plt.plot(trainPredictPlot,color="b", label="trainpredict")
plt.plot(testPredictPlot,color="m", label="testpredict")
 
# グラフを保存
plt.legend()
plt.savefig('result.png')

np.savetxt('test_predict_plot.csv',testPredictPlot,delimiter=',')

下記コマンドを実行して、予測を行います。

# ローカル
docker run -it -v /path/to/somepath:/temp tensor_flow_ubuntu
# docker上
python /temp/predict_temperature.py

 学習に使用したデータ(learn_temperature.csv)と、出力結果(test_predict_plot.csv)は下記です。test_predict_plot.csvは、学習期間の値(1~8789行目)はnanで、8800行目からが予測値になっています。

実際の値と予測値をグラフ化したものが下記です。

未来1ヶ月の実データと予測データ

予測精度については下記の通りとなりました。
 1.全てのデータは3%以内の誤差(ケルビン換算)
 2.90%のデータは3%以内の誤差、9%のデータは、10%以内の誤差、1%のデータは、20%以内の誤差(摂氏換算)

考察、まとめ

 今回はtensorflowを使って気温の予測を行いました。一般的にtensorflowのメリット、デメリットは下記と言われています。
メリット
・利用者が多いので、調べた時に情報を得やすい
デメリット
・計算グラフ構築後の変更が不可能
このくらいの予測では計算グラフを変更することはしなかったので、特に大きなデメリットは感じませんでした。tensorflowを使ってよかったと思います。
 次に、精度についてですが、学習期間のデータを増やす、学習回数を増やす等で精度を上げることは可能です。ただし、昨今温暖化が進んでいる影響で、学習期間のデータを増やしすぎると精度が逆に下がる等の傾向が見られるかもしれないので、調査してみるのも良いかもしれません。
 さらに今後は、このモデルを使って、別の気象データについても予測がうまくいくのか検証していきたいと思います。