TensorFlowで電力使用量予測 with Keras

# はじめに

これまでSciKit-learnを使って電力使用量を予測する例を何度か公開してきましたが、今回はTensorFlowを使って似たようなことをするサンプルを作ってみました。

参考記事：[機械学習で電力需要を予測してみる パート2](https://qiita.com/mix_dvd/items/1f96f5202614dbea93e0)

# 環境構築

## テスト環境

- MacBook Pro Mid 2012
- Python 3.5.1 with Anaconda 4.1.0
- TensorFlow 1.0.1
- Keras 2.1.2

## Anacondaのインストール

以下のURLより、自分のPC環境に適合するものをダウンロードしてインストール

https://www.anaconda.com

## TensorFlowのインストール

以下のコマンドを実行

- CPU版

```sh:
$ pip install tensorflow
```

- GPU版

```sh:
$ pip install tensorflow-gpu
```

## Kerasのインストール

以下のコマンドを実行

```sh:
$ pip install keras
```

## Jupyterの起動

以下のコマンドを実行

```sh:
$ jupyter notebook
```

新しいファイルを作成して、以下のコードを実行

# データ取得

## 電力データ

以下のURLより中国電力にて公開されている電力使用量データをダウンロード
http://www.energia.co.jp/jukyuu/download.html

※2016年度と2017年度のデータを取得

## 気象データ

中国電力管内にある主要都市として、岡山と広島をとりあげることとし、岡山と広島の気象データをダウンロード
※ 予測精度を向上させるために、他の主要都市の気温データを追加しても良いと思います

以下のURLより気温データをダウンロード
http://www.data.jma.go.jp/gmd/risk/obsdl/index.php

※1時間おきの気温データを電力データと同じ期間にあわせて取得

# データ読込

## ライブラリ読込

```py3:
import pandas as pd

%matplotlib inline
```

## 電力データ

```py3:
df_kw = pd.read_csv("juyo-2016.csv",encoding="Shift_JIS",skiprows=1)
df_kw["MW"] = df_kw["実績(万kW)"] * 10

df_kw["DATETIME"] = df_kw.index.map(lambda _: pd.to_datetime(df_kw.DATE[_] + " " + df_kw.TIME[_]))

# 可視化
df_kw["MW"].plot(figsize=(15,4))
```

![001.png](https://qiita-image-store.s3.amazonaws.com/0/114240/a5587181-4938-014c-6d32-f29dbf038a8f.png)

## 気象データ

```py3:
def read_temp(filename):
    df_temp = pd.read_csv(filename,encoding="Shift_JIS",skiprows=4)
    df_temp.columns = ["DATETIME","TEMP","品質情報","均質番号"]
    df_temp.DATETIME = df_temp.DATETIME.map(lambda _: pd.to_datetime(_))
    return df_temp

# 岡山
df_temp_okym = read_temp("data_okayama-2016.csv")
df_temp_okym.rename(columns = {'TEMP':'TEMP_okym'}, inplace=True)

# 広島
df_temp_hrsm = read_temp("data_hiroshima-2016.csv")
df_temp_hrsm.rename(columns = {'TEMP':'TEMP_hrsm'}, inplace=True)

# 可視化
df_temp_okym.TEMP_okym.plot(figsize=(15,4))
df_temp_hrsm.TEMP_hrsm.plot(figsize=(15,4))
```

[岡山の気温データ]
![002.png](https://qiita-image-store.s3.amazonaws.com/0/114240/7b26ea2c-2cba-7654-a9b2-da13823891e3.png)

[広島の気温データ]
![003.png](https://qiita-image-store.s3.amazonaws.com/0/114240/1c9a38f9-4317-1d9d-e78a-4353b0005ed4.png)

# データ加工

## 電力と気温のデータを結合

```py3:
# データの複製
df = df_kw.copy()
# 岡山の気温を結合
df = df.merge(df_temp_okym,how="inner", on="DATETIME")
# 広島の気温を結合
df = df.merge(df_temp_hrsm,how="inner", on="DATETIME")

# 月、週、時間の値を個別に取得
df["MONTH"] = df.DATETIME.map(lambda _: _.month)
df["WEEK"] = df.DATETIME.map(lambda _: _.weekday())
df["HOUR"] = df.DATETIME.map(lambda _: _.hour)
```

## 欠損データの削除

```py3:
# null値の削除
df = df.dropna()
```

## One-hotエンコーディング

```py3:
cols = ["MONTH","WEEK","HOUR"]
for col in cols:
    df = df.join(pd.get_dummies(df[col], prefix=col))
```

# 学習

```py3:
x_cols = ["TEMP_okym","TEMP_hrsm"] + df.columns.tolist()[14:]

X = df[x_cols]
y = df["MW"]

# ラベル付きデータをトレーニングセット (X_train, y_train)とテストセット (X_test,y_test)に分割
from sklearn import model_selection

X_train, X_test, y_train, y_test = model_selection.train_test_split(X, y, test_size=.2, random_state=42)

# 正規化
from sklearn.preprocessing import StandardScaler

scaler = StandardScaler()
scaler.fit(X_train)

X_train = scaler.transform(X_train)
X_test = scaler.transform(X_test)

# Keras読込
from keras.models import Sequential
from keras.layers import Dense
from keras.wrappers.scikit_learn import KerasRegressor

# モデル生成用関数
def reg_model():
    
    reg = Sequential()
    
    reg.add(Dense(10, input_dim=len(x_cols), activation='relu'))
    reg.add(Dense(16, activation='relu'))
    reg.add(Dense(1))

    reg.compile(loss='mean_squared_error', optimizer='adam', metrics=['accuracy'])
    reg.summary()
    
    return reg

# モデルの取得
model = KerasRegressor(build_fn=reg_model, epochs=200, batch_size=16, verbose=0)

# 学習
model.fit(X_train, y_train)

# スコア（参考値）
model.score(X_test, y_test)
```

# 検証

```py3:
from sklearn.metrics import mean_squared_error

# 予測値の取得
y_pred = model.predict(X_test)

# 二乗平方根で誤差を算出
mse = mean_squared_error(y_test, y_pred)
print("KERAS REG RMSE : %.2f" % (mse ** 0.5))
print("KERAS REG SCORE: %3.2f" % (1.0 - mse ** 0.5 / y_test.mean()))

# 可視化
pd.DataFrame({"pred":y_pred, "act":y_test})[:100].reset_index(drop=True).plot(figsize=(15,4))
```

誤差の出力は「KERAS REG RMSE : 349.74」で、予測結果のグラフは以下のとおり。

![004.png](https://qiita-image-store.s3.amazonaws.com/0/114240/4ff9a7c9-25ff-79d2-fc2d-2d20650cc844.png)

...なんだかあってそう(^-^)

# 予測

2017年度データを予測し、確認する。

```py3:
# 電力データを読込
df_kw = pd.read_csv("juyo-2017.csv",encoding="Shift_JIS",skiprows=1)

# 岡山のデータを読込
df_temp_okym = read_temp("data_okayama-2017.csv")
df_temp_okym.rename(columns = {'TEMP':'TEMP_okym'}, inplace=True)

# 広島のデータを読込
df_temp_hrsm = read_temp("data_hiroshima-2017.csv")
df_temp_hrsm.rename(columns = {'TEMP':'TEMP_hrsm'}, inplace=True)

# データ加工
df_kw.columns = ["DATE","TIME","万kW"]
df_kw["MW"] = df_kw["万kW"] * 10
df_kw["DATETIME"] = df_kw.index.map(lambda _: pd.to_datetime(df_kw.DATE[_] + " " + df_kw.TIME[_]))

# データ結合
df = df_kw.copy()
df = df.merge(df_temp_okym,how="inner", on="DATETIME")
df = df.merge(df_temp_hrsm,how="inner", on="DATETIME")

# 追加データ作成
df["MONTH"] = df.DATETIME.map(lambda _: _.month)
df["WEEK"] = df.DATETIME.map(lambda _: _.weekday())
df["HOUR"] = df.DATETIME.map(lambda _: _.hour)

# 欠損データ削除
df = df.dropna()

# One-hotエンコーディング
cols = ["MONTH","WEEK","HOUR"]
for col in cols:
    df = df.join(pd.get_dummies(df[col], prefix=col))

# テストデータの不足分を補完
for col in x_cols[2:]:
    if not col in df.columns:
        df[col] = 0

# 7月の電力使用量を予測
df_pred = df[df.MONTH==7]

# 予測用データ
X = df_pred[x_cols].as_matrix().astype('float64')
y = df_pred["MW"].as_matrix().astype('int').flatten()

# 正規化
X = scaler.transform(X)

# 予測値の取得
y_pred = model.predict(X)

# 二乗平方根で誤差を算出
mse = mean_squared_error(y, y_pred)
print("KERAS REG RMSE : %.2f" % (mse ** 0.5))

# 可視化
pd.DataFrame({"pred":y_pred, "act":y}, index=df_pred.index).plot(figsize=(15,4),ylim=(4000,12000))
```

誤差の出力は「KERAS REG RMSE : 368.87」で、予測結果のグラフは以下のとおり。

![005.png](https://qiita-image-store.s3.amazonaws.com/0/114240/87ca5023-dcdb-0509-7964-516280c08624.png)

良いかも(^-^)

# 参考資料

http://blueomega.jp/20171221_predict_kw_with_temp_by_tensorflow.html