作成: 日大生産工 MA 大前
これは、大前研究室の卒業研究テーマの学習資料として作成されたものです。他のゼミ生がこのページを参考にして卒業研究を行う場合は、必ず事前に相談してください。異なる研究室なのに、同じテーマが卒業研究概要集に並んでいると、問題になるかもしれません。
おまじないとして、以下を実行しておいてください。
import numpy as np
from sklearn.datasets import load_boston
import matplotlib.pyplot as plt
import scipy.stats as st
from sklearn.neural_network import MLPRegressor
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
まずは、今回分析に使うデータセットをロードします。これは、ボストン住宅価格に関するデータセットで、どのような地域だと、住宅価格が安いのか、高いのかなど、分析することができます。
Boston_dataset = load_boston(return_X_y=False)
Boston_dataset.keys() # key一覧取得
Boston_datasetの中には、['data', 'target', 'feature_names', 'DESCR', 'filename'] というデータがあることがわかりました。
まずは、targetとラベルづけられたデータを取り出し、BD_Yに代入してみます。
BD_Y = Boston_dataset["target"]
print("BD_Yのデータサイズ: ", np.shape(BD_Y))
print("・54番目: ", BD_Y[54])
print("・80番目: ", BD_Y[80])
targetというデータは、506個の数字の集まりであることがわかりました。また、54番目のデータは18.9、80番目のデータは28であることがわかりました。これは、
ということを意味しています。イメージとしては、
となります。
続いて、dataとラベルづけられたデータを取り出してみます。
BD_X = Boston_dataset["data"]
print("BD_Xのデータサイズ: ", np.shape(BD_X))
取り出したデータは、506行、13列のサイズを有していることがわかりました。
です。つまり、縦506、横13サイズの変数ということになります。506とは、ボストンを細かく分けたそれぞれの区画のどれかを意味していることは、想像しやすいと思います。それ一つ一つに、13個の情報が付与されていることになります。この情報は、以下の並びで保存されています。
print(Boston_dataset["feature_names"])
13個の英語が出てきました。これだけみてもよくわかりませんので、以下にまとめておきます。
ざっくり書いています。詳しく知りたい場合は、調べてみてください。
例えば、BD_X[54. 3]と記載すると、区画54の、3番目の情報(チャールズ川に接しているかどうか)が取り出されます。
print("区画54はチャールズ川に接しているか: ", BD_X[54, 3])
print("区画54の犯罪の起こりやすさ: ", BD_X[54, 0])
print("\n区画142はチャールズ川に接しているか: ", BD_X[142, 3])
print("区画142の犯罪の起こりやすさ: ", BD_X[142, 0])
このように、
などの情報が見て取れます。 ここからわかるように、BD_Xには、506区画に対する13個の情報がまとめられています。
機械学習の分野では、このようなデータを特徴量と呼びます。 13個の情報は、506区画の特徴を表現するため、このような呼び方をするわけです。
続いて、BD_XからBD_Yを予測する、つまり、「犯罪の発生のしやすさなど13個の情報」から「住宅価格」を予測するモデルの構築について考えます。この場合、506個分すべてのデータで予測するモデルを構築しようとすると、精度評価を行うことができないという問題が生じます。予測モデルを作る大きな目的は、正解を知っているデータを予測することではなく、これから新たに発生する、未知のデータを予測することとなります。したがって、予測モデルを作った後の精度評価は、モデルの構築に使っていないデータを使用して行うことが重要となります。
このため、
に分割します。BD_XとBD_Yがあり、それが教師データ、テストデータに分けられるわけですから、合計4つのデータが出来上がります。
今回は、506個のうち80%を教師データ、20%をテストデータに振り分けてみます。
# 506の数値の中で、80%に位置する数字を取得(506 * (8/10) = 404.80 = 404)
split_th = int(len(Boston_dataset["target"])*(8/10))
# 予測対象側(住宅価格)のデータセット分割
Y_train = BD_Y[:split_th]
Y_test = BD_Y[split_th:]
# 特徴量側(犯罪の発生のしやすさなど)のデータセット分割
temp_X_train = BD_X[:split_th, :]
temp_X_test = BD_X[split_th:, :]
# 特徴量を、0〜1範囲に規格化
nX_train, nX_test, MinV, MaxV = MinMaxStand_Xtrain_Xtest(temp_X_train, temp_X_test)
# サイズチェック
print("教師データ(住宅価格)のサイズ: ", np.shape(Y_train))
print("教師データ(特徴量)のサイズ: ", np.shape(nX_train))
print("テストデータ(住宅価格)のサイズ: ", np.shape(Y_test))
print("テストデータ(特徴量)のサイズ: ", np.shape(nX_test))
506件分のデータが、教師データは404件分、テストデータは102件分として分割されたことがわかりました。
ここでは、予測モデルの構築手法の一つである、ニューラルネットワークについて簡単に説明します。今回構築するニューラルネットワークは、13個の情報を与えると、住宅価格を教えてくれる、という構造をしています(つまり、入力側が13次元、出力側が1次元となります)。
ニューラルネットワークを構築する際、重要なパラメータとして、学習回数と学習係数があります。すごくざっくりと説明すると、
という意味となります。一つの例として、
でニューラルネットワークを定義してみます。
# ↓ ★学習回数と学習係数を、課題に応じて自分の好きな値に変更
MT = 10000 # 学習回数
LR = 0.001 # 学習係数
# 以下は、変更しないことを推奨
NNreg = MLPRegressor(
hidden_layer_sizes=(5, ), #中間層
max_iter = MT, # 学習回数
learning_rate_init = LR, # 学習係数
random_state = 0,
verbose=False
)
これでモデルの定義が終わりました。これを学習させるには、以下のように記載します。
NNreg.fit(nX_train, Y_train)
# 学習回数が多いと、1分間程度、時間がかかる場合があります。
fitに与えている2つの変数は、
ですから、教師データのみを使って、13個の情報から、住宅価格を予測するような学習をしてください、という意味になります。頑張って学習をしているので、特に学習回数が多い場合は、少し時間がかかることがあります。プログラムの連打などをせずに、終わるまで待ってください。
続いて、構築したニューラルネットワークの予測誤差を推定してみます。データは、教師データを利用します。教師データの誤差を下げるような学習をした訳ですので、ある程度、誤差が低いことが想定されます。
教師データは404件でした。このうち、3件分の予測値と正解値を見てみます。
pred_Y_train = NNreg.predict(nX_train) # 教師データ(nX_train)を代入した場合の予測値を取得
for i in range(3):
print(i, "件目の住宅: ")
print("__予測値: ", np.round(pred_Y_train[i], 2))
print("__正解値: ", Y_train[i])
temp_err = np.abs(Y_train[i] - np.round(pred_Y_train[i], 2))
print("__誤差: ", np.round(temp_err, 2))
print("")
正解と予測が、似ていることがわかります。3件分だと全体的な評価にならないので、教師データすべての平均誤差を求めてみましょう。
Err_train = np.mean(np.abs(Y_train - pred_Y_train))
print("教師データの平均誤差: ", np.round(Err_train, 2))
これを1000倍したものがUSDですから、だいたい4000ドル程度(40万円くらい?)の誤差があることがわかります。住宅価格の40万円程度のズレですから、そこまで悪くもない気がします。
続いて、もっと素朴に教師データの誤差を検証してみます。404件分のデータの予測値と正解値にがある訳で、それが一致している方が望ましいことになります。すなわち、404件分のデータについて、横軸を正解値、縦軸を予測値として散布図を描いたとき、対角線上にデータが集まると、良い予測と判断することができます。これを確認してみましょう。
plt.scatter(x=Y_train, y=pred_Y_train)
plt.ylabel("Prediction") # 予測値
plt.xlabel("Ground truth") # 正解値
plt.title("Training data")
plt.grid(True)
plt.show()
train_cor = st.pearsonr(Y_train, pred_Y_train)
print("(教師データ)予測-実測の相関係数: ", np.round(train_cor[0], 3))
横軸(Ground truth)が正解値、縦軸(Prediction)がニューラルネットワークの予測値です。右肩上がりの対角線上に集まっているということは、高価な住宅を高価と予測できていること、安価な住宅を安価と予測できていることになります。
また、対角成分に集中しているかどうかチェックするため、相関係数も出しています。 相関が1に近いほど、うまく予測できていることになります。 また、相関係数が0に近い、あるいはマイナスの場合、右肩上がりではないわけですから、全然ダメということになります。 今回は0.815ですからとても性能が良いことを意味します。 → 精度評価は、誤差(実測と予測の引き算)でもできますし、相関係数でもできます。好きな方を使いましょう。
総合的に見て、教師データに関しては、誤差が少ないと解釈できます。
さて、教師データの誤差は少ないことがわかりました。しかしそれは喜んでいいことではありません。ニューラルネットワークは、教師データの誤差を下げることを目的とした学習をしている訳ですから、これが低いのは、いわば当たり前のことにすぎません。
本来、うまく予測できていることが望ましいのは、教師データに存在しない未知のデータに対してとなります。 今回は、学習に使用していないテストデータがありますから、それを利用して誤差測定をしてみます。
まずは、テストデータ3件分についてみてみます。
pred_Y_test = NNreg.predict(nX_test) # テストデータ(nX_test)を代入した場合の予測値を取得
for i in range(3):
print(i, "件目の住宅: ")
print("__予測値: ", np.round(pred_Y_test[i], 2))
print("__正解値: ", Y_test[i])
temp_err = np.abs(Y_test[i] - np.round(pred_Y_test[i], 2))
print("__誤差: ", np.round(temp_err, 2))
print("")
0件目の誤差はやや大きく、1, 2件目の誤差は少なくなりました。
これだけではうまく解釈できないので、テストデータすべての平均誤差を算出してみます。
Err_test = np.mean(np.abs(Y_test - pred_Y_test))
print("テストデータの平均誤差: ", np.round(Err_test, 2))
テストデータの誤差も教師データと同じくらいになりました。
最後に、テストデータにおいても、散布図を書いて、全体の傾向を確認してみましょう。
plt.scatter(x=Y_test, y=pred_Y_test)
plt.ylabel("Prediction") # 予測値
plt.xlabel("Ground truth") # 正解値
plt.title("Test data")
plt.grid(True)
plt.show()
test_cor = st.pearsonr(Y_test, pred_Y_test)
print("(テストデータ)予測-実測の相関係数: ", np.round(test_cor[0], 3))
若干ふんわりしていますが、右肩上がりであることがわかります。ですので、安いものは安いと予測、高いものは高いと予測できていることがわかります。これは、学習に使っていないデータですから、未知のデータ(ニューラルネットワークが初めて見るデータ)でもある程度予測できることを示しています。
テストデータの相関係数は0.711ですから、教師データよりもわずかに低い結果となりました。初めて見るデータの予測ですから、教師データの制度よりも落ちることは、自然なことです。
1章 はじめに:(文章量の比率: 15%)
2章 特徴量と予測対象:(文章量の比率: 30%)
3章 ニューラルネットワークの実験:(文章量の比率: 40%)
3.1節 概要:
3.2節 結果と考察:
4章 おわりに:(文章量の比率: 15%)
参考文献:
参考にした資料を、2〜3件記載してください。以下、書き方の例です。
本文中で引用する場合は「大前らはXXXを実施している [1]。また、〜」のように、どこで引用したのか明白にしてください。