作成: 日大生産工 MA 大前
これは、大前研究室の卒業研究テーマの学習資料として作成されたものです。他のゼミ生がこのページを参考にして卒業研究を行う場合は、必ず事前に相談してください。異なる研究室なのに、同じテーマが卒業研究概要集に並んでいると、問題になるかもしれません。
まず、分析に使用するデータを取得します。以下のコードを実行してください。
import numpy as np
import matplotlib.pyplot as plt
from sklearn.neural_network import MLPRegressor
import scipy.stats as st
!wget http://int-info.com/PyLearn/Grad_4/data_for_study/tokyo_elec_dat_2020.csv
dat = np.loadtxt("tokyo_elec_dat_2020.csv", delimiter=",", skiprows=1, dtype=str)
day_all = dat[:, 0]
y_all = dat[:, 1].astype(float)
x_all = dat[:, 2:].astype(float)
def MinMaxStand_Xtrain_Xtest(fX_train, fX_test):
# trainデータについて、各特徴量の最大値・最小値取得
MaxVal, MinVal = [], []
for i in range(0, len(fX_train[0, :])):
MaxVal.append(np.max(fX_train[:,i]))
MinVal.append(np.min(fX_train[:,i]))
# trainデータの規格化
newX_train = np.zeros(np.shape(fX_train))
for i in range(0, len(fX_train[:,0])):
for j in range(0, len(fX_train[0, :])):
newX_train[i, j] = (fX_train[i, j] - MinVal[j]) / (MaxVal[j] - MinVal[j])
# testデータの規格化
newX_test = np.zeros(np.shape(fX_test))
for i in range(0, len(fX_test[:,0])):
for j in range(0, len(fX_test[0, :])):
newX_test[i, j] = (fX_test[i, j] - MinVal[j]) / (MaxVal[j] - MinVal[j])
# 規格化trainデータ、規格化testデータ、train最大値、train最小値を返却
return newX_train, newX_test, MaxVal, MinVal
東京都の電力使用量(正確には、東京電力パワーグリッドが発電し、消費された電力)は、y_allに格納されています。1行1行が1日1日を表し、2020年は閏年でしたらか、366個のデータで構成されます。単位は GW/day です。G(ギガ)は、10の9乗を意味します。試しにこのデータの中身を見てみます。
split = 30
x_num = np.arange(0, len(day_all), split)
x_str = []
for i in range(0, len(day_all), split):
x_str.append(day_all[i])
plt.plot(y_all) # <- y_all(電力使用量をプロット)
plt.ylabel("[GW/day]")
plt.xticks(x_num, x_str, rotation=90)
plt.grid()
plt.show()
真夏と冬に、電力使用量が高いことがわかりました。ある程度直感的な結果かと思います。
続いて、電力使用量を予測するための情報である、気象データに注目します。気象データはx_allに格納されており、
となります。こちらも、試しに平均気温をプロットしてみます。
plt.plot(x_all[:, 0]) # <- 0列目に平均気温があるので、列の指定を0にする
plt.ylabel("Temperature [°C]")
plt.xticks(x_num, x_str, rotation=90)
plt.grid()
plt.show()
夏の気温は高く、冬の気温が低いことがわかりました。こちらも直感的に問題ない結果かと思います。余裕があれば、気温以外も見てみることをお勧めします。
続いて、データセットを2つ(教師データ、テストデータ)に分割します。それぞれ、以下の意味を持ちます。
人工知能とは、学習に使用しなかった、つまり、初めてみるデータに正解できることが重要です。そのため、教師データとテストデータを独立させることが必要です。なお今回は、2020年1月1日〜8月31日を教師データ、2020年9月1日〜12月31日をテストデータとします。
sp = 244 # 0〜243日を教師データ、244 〜 最後までをテストデータとする基準日
x_train = x_all[:sp, :]
x_test = x_all[sp:, :]
x_train_s, x_test_s, xmax, xmin = MinMaxStand_Xtrain_Xtest(x_train, x_test)
ymin = np.min(y_all[:sp])
ymax = np.max(y_all[:sp])
y_train = (y_all[:sp] - ymin)/(ymax - ymin)
y_test = (y_all[sp:] - ymin)/(ymax - ymin)
day_train = day_all[:sp]
day_test = day_all[sp:]
split = 15
x_num_train = np.arange(0, len(day_train), split)
x_str_train = []
for i in range(0, len(day_train), split):
x_str_train.append(day_train[i])
x_num_test = np.arange(0, len(day_test), split)
x_str_test = []
for i in range(0, len(day_test), split):
x_str_test.append(day_test[i])
print("### 教師データ: ", day_train[0], " 〜 ", day_train[-1])
print("### テストデータ: ", day_test[0], " 〜 ", day_test[-1])
続いて、ニューラルネットワークについて簡単に説明します。今回構築するニューラルネットワークは、5個の情報(気温、風速、降水量、日照時間、湿度)を与えると、電力使用量を教えてくれる、という構造をしています(つまり、入力側が5次元、出力側が1次元となります)。
ニューラルネットワークを構築する際、重要なパラメータとして、学習回数と学習係数があります。すごくざっくりと説明すると、
という意味となります。一つの例として、
でニューラルネットワークを構築してみます。
NNreg = MLPRegressor(
hidden_layer_sizes=(3, ),
max_iter= 2000, # 学習回数を変更するには、ここの数字を変更すること
learning_rate_init=0.001, # 学習係数を変更するには、ここの数字を変更すること
random_state=0, # 乱数シード
tol=10**-20,
verbose=False)
NNreg.fit(x_train_s, y_train)
これで学習が終わりました(学習回数が多いと、時間がかかるので注意してください)。もし、モデルをよりよくしたい場合は、3, 4行目の学習回数と学習係数を任意の値に変更してみてください。学習回数は5000や8000、学習係数は0.01、0.0001などのように、キリが良い値をおすすめします。
続いて、どのような予測結果になったのか確認してみます。学習に利用した教師データの結果を表示します。
# 教師データ
y_train_pred = NNreg.predict(x_train_s)
gt_train = (y_train)*(ymax-ymin) + ymin
pred_train = (y_train_pred)*(ymax-ymin) + ymin
plt.plot(gt_train, linestyle="-.", label="Ground truth")
plt.plot(pred_train, label="Prediction")
plt.title("Training data")
plt.ylabel("[GW/day]")
plt.xticks(x_num_train, x_str_train, rotation=90)
plt.grid()
plt.legend()
plt.show()
横軸が日にち、縦軸が電力使用量となります。線が2つあり、それぞれ以下を意味します。
重なっていることが望ましい結果となります。夏と冬ごろは性能が良いようですが、春はちょっと微妙な感じです。このずれを定量化するために、絶対誤差を算出してみます(絶対誤差とは、1日ごとの誤差に、絶対値をつけ、それを平均したものです。したがって、1日だいたいどれくらい電力使用料がずれるのかを意味します)。
err_train = np.mean(np.abs(gt_train - pred_train))
print("絶対誤差(教師データ): ", np.round(err_train, 2), "[GW/day]")
1日あたり、平均で60GWほどズレがあるようです。
教師データは、ニューラルネットワークが学習のために使用したデータでしかありません。実際は、初めてみるデータに当てられるということが重要になります。そのためここでは、テストデータで性能評価を行います。
# テストデータ
y_test_pred = NNreg.predict(x_test_s)
gt_test = (y_test)*(ymax-ymin) + ymin
pred_test = (y_test_pred)*(ymax-ymin) + ymin
plt.plot(gt_test, linestyle="-.", label="Ground truth")
plt.plot(pred_test, label="Prediction")
plt.title("Test data")
plt.ylabel("[GW/day]")
plt.xticks(x_num_test, x_str_test, rotation=90)
plt.grid()
plt.legend()
plt.show()
テストデータは先ほど記載したように、9月〜12月となります。こちらも割と当たっているでしょうか? 定量評価のために、絶対誤差を算出してみます。
err_test = np.mean(np.abs(gt_test - pred_test))
print("絶対誤差(テストデータ): ", np.round(err_test, 2), "[GW/day]")
初めてみるデータに対しても、60GWくらいのズレに収まるようです。このように、何らかの自動判定モデルの性能評価を行う場合は、構築に関与していないデータの性能を示してあげる必要があります。
続いて、構築したニューラルネットワークを使用して、「どんな日だと、どれくらいみんな電力を使うのか?」という問いの答えを得てみます。今回は、暑くもなく寒くもない、風も強くなく、天気が良い日を想定してみます。いわゆる、普通の日でしょうか。
x0 = 25 # 平均気温(度)/ 5〜30度(東京なので)
x1 = 3 # 平均風速(m/s)/ 強風 4〜6くらい、普通の風2〜3くらい、ほとんど風がないならば1〜2
x2 = 0 # 降水量(mm)/大雨ならば60〜120くらい、小雨ならば30〜10くらい、晴れならば0
x3 = 10 # 日照時間(h)/ 晴天ならば10時間くらい 〜 1日中曇りor雨ならば0時間
x4 = 40 # 平均湿度(%)/ 30〜100%
x = [x0, x1, x2, x3, x4]
for i in range(len(x)):
x[i] = (x[i] - xmin[i])/(xmax[i] - xmin[i])
res = NNreg.predict([x])
res = res * (ymax-ymin) + ymin
print("電力使用量の予測値: ", np.round(res[0] ,2), "[GW/day]")
普通の日の電力使用量は、720GWほどという結果になりました。先に見せた電力使用量のグラフを見る限り、あまり電気を使っていない日と言えそうです。x0 〜 x4 に任意の値を入れることで、シミュレーションしたい状況を設定することができます。なお、
などのような、現実的にはあり得ない条件で試すことに意味はありませんので、注意してください(予測自体は行われます)。
1章 はじめに:(文章量の比率: 30%)
2章 入出力の関係:(文章量の比率: 20%)
3章 ニューラルネットワークの実験:(文章量の比率: 30%)
3.1節 概要:
3.2節 結果と考察:
4章 おわりに:(文章量の比率: 20%)
参考文献: 参考にした資料を、2〜3件記載してください。以下、書き方の例です。
本文中で引用する場合は「大前らはXXXを実施している [1]。また、〜」のように、どこで引用したのか明白にしてください。
本項で使用しているデータは、以下から取得したものです。
東京電力パワーグリッド, 過去の気象データ・ダウンロード:
気象庁, 過去の気象データ・ダウンロード(東京都)