🐈

Act 15. Pythonで線形回帰を試す

2024/11/13に公開

はじめに

Act 01. AIで外国為替を自動売買するまでの道のりをベースに学習を進めて行く。

前回は線形回帰の概要について学習した。
なので今回は実際にpythonで線形回帰分析をしてみようと思う。

データセットが必要になるため、今回はpythonで提供されているカリフォルニア住宅価格のデータセットを使用する。

線形回帰分析

まずは必要なライブラリのインストールを行う。

pip install scikit-learn

これで準備ができたため、コードを書いていこうと思う。
まずはデータセットを確認してみる。

Act15.py
from sklearn.datasets import fetch_california_housing

california_housing = fetch_california_housing()
print(california_housing.keys())

出力結果は以下の通り。

dict_keys(['data', 'target', 'frame', 'target_names', 'feature_names', 'DESCR'])

データセットのキーについて

それぞれのキーについて紹介する。

  • data: 特徴量のデータ(X
    ※特徴量とはモデルに入力するデータのこと
  • target: 予測対象となるターゲットのデータ(y)(今回は住宅の中央値価格)
  • frame: Pandas DataFrame 形式のデータでdatatargetの情報を結合したもの。
    frameは必ずしも存在するわけではなくNoneになることもある
  • target_names: ターゲットの名前(今回は住宅価格の中央値
  • feature_names: 特徴量の名前(カラム名)のリスト
  • DESCR: データセットの説明文。データセットの詳細や由来についての情報を含んでいる。

pandasを使ってデータを出力してみる。
以前学んだから行けるよなぁ!?

Act15.py
from sklearn.datasets import fetch_california_housing
import pandas as pd

california_housing = fetch_california_housing()

df = pd.DataFrame(california_housing.data)
df.columns = california_housing.feature_names
print(df[:10])

出力はこんな感じ。
カラムとデータ10行分が出力されているので問題なし。

   MedInc  HouseAge  AveRooms  AveBedrms  Population  AveOccup  Latitude  Longitude
0  8.3252      41.0  6.984127   1.023810       322.0  2.555556     37.88    -122.23
1  8.3014      21.0  6.238137   0.971880      2401.0  2.109842     37.86    -122.22
2  7.2574      52.0  8.288136   1.073446       496.0  2.802260     37.85    -122.24
3  5.6431      52.0  5.817352   1.073059       558.0  2.547945     37.85    -122.25
4  3.8462      52.0  6.281853   1.081081       565.0  2.181467     37.85    -122.25
5  4.0368      52.0  4.761658   1.103627       413.0  2.139896     37.85    -122.25
6  3.6591      52.0  4.931907   0.951362      1094.0  2.128405     37.84    -122.25
7  3.1200      52.0  4.797527   1.061824      1157.0  1.788253     37.84    -122.25
8  2.0804      42.0  4.294118   1.117647      1206.0  2.026891     37.84    -122.26
9  3.6912      52.0  4.970588   0.990196      1551.0  2.172269     37.84    -122.25

今更だけど各特徴量について書いておく。

  • MedInc: median income(地域の世帯所得の中央値)
  • HouseAge: house age(家の年齢:築年数)
  • AveRooms: average rooms(住宅1軒あたりの平均部屋数)
  • AveBedrms: average bed rooms(住宅1軒あたりの平均寝室数)
  • Population: population(人口)
  • AveOccup: average occupancy(住宅1軒あたりの平均人数)
  • Latitude: latitude(緯度)
  • Longitude: longitude(経度)
  • MedHouseVal: median house value(中央値住宅価格、ターゲットで使用)

ついでにターゲットも追加してあげようかな。

Act15.py
from sklearn.datasets import fetch_california_housing
import pandas as pd

california_housing = fetch_california_housing()

df = pd.DataFrame(california_housing.data)
df.columns = california_housing.feature_names
df["target"] = california_housing.target
print(df[:10])

出力はこんな感じに変化した。
pandasの使い方も何となく覚えてるしいい感じ!

   MedInc  HouseAge  AveRooms  AveBedrms  Population  AveOccup  Latitude  Longitude  target
0  8.3252      41.0  6.984127   1.023810       322.0  2.555556     37.88    -122.23   4.526
1  8.3014      21.0  6.238137   0.971880      2401.0  2.109842     37.86    -122.22   3.585
2  7.2574      52.0  8.288136   1.073446       496.0  2.802260     37.85    -122.24   3.521
3  5.6431      52.0  5.817352   1.073059       558.0  2.547945     37.85    -122.25   3.413
4  3.8462      52.0  6.281853   1.081081       565.0  2.181467     37.85    -122.25   3.422
5  4.0368      52.0  4.761658   1.103627       413.0  2.139896     37.85    -122.25   2.697
6  3.6591      52.0  4.931907   0.951362      1094.0  2.128405     37.84    -122.25   2.992
7  3.1200      52.0  4.797527   1.061824      1157.0  1.788253     37.84    -122.25   2.414
8  2.0804      42.0  4.294118   1.117647      1206.0  2.026891     37.84    -122.26   2.267
9  3.6912      52.0  4.970588   0.990196      1551.0  2.172269     37.84    -122.25   2.611

良い感じとか思ったけど一つ疑問が…。
前回学んだ感じだと、線形回帰の特徴量(入力の数)は1つだったぞ!?8個もあるの…?ってとこ。

その疑問に関しては「特徴量(入力の数)が複数あるんだが?」を見てね。
問題ない人は次に進もう。

各特徴量を表に出力してみる

とりあえず8個の特徴量(入力データ)が存在し、多変量回帰なる分析方法があると分かった。
せっかくなので以前学習したmatplotlibを使って、すべての特徴量を表に出力してみようと思う。

今回はsubplotsというメソッドを使用して、1つのウィンドウに8個の表を出力してみる。

Act15.py
from sklearn.datasets import fetch_california_housing
import matplotlib.pyplot as plt

# カリフォルニア住宅データをロード
california_housing = fetch_california_housing()
X = california_housing.data  # 特徴量(MedInc, HouseAge, AveRooms など)
y = california_housing.target  # ターゲット(住宅の価格)
feature_names = california_housing.feature_names  # 特徴量名

# サブプロットを作成 (2行4列で、8つの特徴量に対応)
fig, axes = plt.subplots(2, 4, figsize=(36, 18))  # figsizeで全体のサイズを調整

# 各サブプロットにプロットを追加
for i, ax in enumerate(axes.flat):  # axes.flatで2次元配列を1次元に変換
    ax.scatter(X[:, i], y)  # 各特徴量とターゲットの散布図
    ax.set_xlabel(feature_names[i], fontsize=12)  # 特徴量名をx軸ラベルに
    ax.set_ylabel('House Value (Target)', fontsize=12)  # y軸ラベル
    ax.set_title(f'{feature_names[i]} vs House Value', fontsize=14)  # タイトル

出力は以下の通り。
良い感じに出力されていそうだが、データの内容ってこれであっているのか…?
全然関連性のようなものが見えてこない(笑)

まあ、自分には良く分からないデータだということが分かったため、次に進もうと思う。

Pythonでの実装

ここから実装を行っていく。
とはいっても全然わからないため、ChatGTPさんに聞いた内容を元に進める。

先ほどのmatplotlibのコードはすべてコメントアウトした状態で進めて行く。

まずは結論から。
以下のコードを実行することで線形回帰分析ができる。

Act15.py
from sklearn.datasets import fetch_california_housing
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
import matplotlib.pyplot as plt

# カリフォルニア住宅データをロード
california_housing = fetch_california_housing()
X = california_housing.data  # 特徴量(MedInc, HouseAge, AveRooms など)
y = california_housing.target  # ターゲット(住宅の価格)

# データを訓練用とテスト用に分ける
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# 線形回帰モデルを作成
model = LinearRegression()

# モデルを訓練
model.fit(X_train, y_train)

# 予測を行う
y_pred = model.predict(X_test)

# 結果を表示
plt.scatter(y_test, y_pred)
plt.xlabel('True Values')
plt.ylabel('Predictions')
plt.title('California Housing - Predictions vs Actual')
plt.show()

出力は以下の通り。

これだけ見せられても、で?って感じだよね。
ということで調べた内容を一つずつ書いていこうと思う。

訓練データについて

まず初めに以下の記述について。
これは与えられたデータセットを訓練データとテストデータに分割するメソッドを呼び出している。

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

返り値は4つになるため、それぞれを適切な変数に代入している。

  • X_train: 訓練用の特徴量(入力)データ
  • X_test: 訓練用のターゲット(出力)データ
  • y_train: テスト用の特徴量(入力)データ
  • y_test: テスト用のターゲット(出力)データ

train_test_split()メソッドの引数について。
第一引数と第二引数は見ればわかると思うので割愛する。

第三引数のtest_size=0.2だが、これはテストデータの量を全体の20%に指定している。
例えば、Xyのデータ量が10000の場合は、訓練用のデータに8000、テスト用のデータに2000残しておく感じ。

第四引数のrandom_state=42だが、これは乱数シードを指定しており、これによりデータの分割が再現可能になる。分かりにくいと思うので簡単に説明する。

例えば以下のようなデータが存在したとする。
X = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
y = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100]

これをtrain_test_splitで分割した場合、シャッフルされた後、たとえば次のように分割されたとする
※test_size=0.2, random_state=42の場合とする

X_train = [4, 9, 2, 7, 1, 10, 6, 5]
X_test = [8, 3]
y_train = [40, 90, 20, 80, 10, 100, 60, 50]
y_test = [70, 30]

このシャッフルの法則?をrandom_stateで固定してあげることで、毎回このシャッフル結果になるらしい。
利点としては、何度実行しても同じ結果になるため、色々試したい場合や、他人にデータセットとrandom_stateの値を伝えることで同じ分析結果が得られたりするところらしい。

なるほどなー、便利。

モデルの訓練

次にモデルの訓練について。
前回の記事で線形回帰の仕組みを記事にしたが、勾配降下法とか最小二乗法とか、正直色々面倒だよね…。

そんな面倒な個所を勝手に計算してくれる学習モデルなるものがpythonでは提供されている。
それがLinearRegressionというクラス。

Act15.py
# 線形回帰モデルを作成
model = LinearRegression()

# モデルを訓練
model.fit(X_train, y_train)

やっていることは簡単で、クラスをインスタンス化して、その変数に対して訓練用のデータを渡してあげるだけ。
訓練用のデータはfit()メソッドで渡してあげる。

そうすると後はよろしくやってくれるらしい。便利…。
ここまででmodelに対して訓練用のデータを使って訓練が完了した。

後はこのモデルの精度を確かめてあげる必要がある。

テスト

先ほど訓練用のデータとテスト用のデータに分割したため、テスト用のデータを使って予測を行う。

Act15.py
# 予測を行う
y_pred = model.predict(X_test)

y_predというのは、yPredict、つまりターゲットの予測を表す。
model.predict(X_test)で先ほど学習したモデルに対して特徴量のデータを渡すことで、学習した結果をもとに予測値を導き出す。

結果の出力

以下のコードで結果を出力している。
ここの内容に関しては説明不要だと思う。

plt.scatter(y_test, y_pred)
plt.xlabel('True Values')
plt.ylabel('Predictions')
plt.title('California Housing - Predictions vs Actual')
plt.show()

自分が1つ疑問に思った個所を記載しておく。
plt.scatter(y_test, y_pred)ここについて。

以前学習した感じだと、plt.scatter(X軸, Y軸)だった。
ただ、今回は両方ともY軸のデータを渡していないか…?と思った。

これは何をしているかというと、テスト用の結果(実際の値)とテスト用の特徴量を使って求められたターゲット(予想した値)を比較している。

実際の値 = 予測した値となっていれば、精度が完璧なモデルが完成したということ。
つまり45度の直線になっていたら完璧だということが分かる。

今回の結果を見た感想としては、いいのか悪いのか分からない…。
目で見た感想ではなく、数値として結果を表すことが出来ればそれが一番良い。

様々な確認方法

先ほどの結果を見ただけでは正直良く分からない。
ということで色々な方法でモデルの学習精度を調べてみようと思う。

プロットする

まずは一つ目のmatplotlibを使う方法。

二つのターゲットを散布図で描画する

こんなコードを書いてみた。

Act15.py
from sklearn.datasets import fetch_california_housing
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
import matplotlib.pyplot as plt
import numpy as np

# カリフォルニア住宅データをロード
california_housing = fetch_california_housing()
X = california_housing.data  # 特徴量(MedInc, HouseAge, AveRooms など)
y = california_housing.target  # ターゲット(住宅の価格)

# データを訓練用とテスト用に分ける
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# 線形回帰モデルを作成
model = LinearRegression()

# モデルを訓練
model.fit(X_train, y_train)

# 予測を行う
y_pred = model.predict(X_test)

# 結果を表示
fig, axes = plt.subplots(1, 2, figsize=(16, 8))
axes[0].scatter(np.arange(len(y_pred)), y_pred, alpha=0.2, color="blue")
axes[1].scatter(np.arange(len(y_test)), y_test, alpha=0.2, color="red")
plt.show()

X軸はデータの個数で、y軸は出力の内容。
似たような出力になっていれば良いなーと思って書いてみた。

出力はこんな感じ。
青色が予測値で赤色が実際の値。これ見た感じだと、何となくは関連してそう…?って感じ。

実際の値と予測値の散布図を描画

次のパターンも紹介してみる。
これは実際の値の線、つまり理想とする出力結果をプロットしている。

Act15.py
from sklearn.datasets import fetch_california_housing
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
import matplotlib.pyplot as plt

# カリフォルニア住宅データをロード
california_housing = fetch_california_housing()
X = california_housing.data  # 特徴量(MedInc, HouseAge, AveRooms など)
y = california_housing.target  # ターゲット(住宅の価格)

# データを訓練用とテスト用に分ける
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# 線形回帰モデルを作成
model = LinearRegression()

# モデルを訓練
model.fit(X_train, y_train)

# 予測を行う
y_pred = model.predict(X_test)

# 結果を表示
plt.scatter(y_test, y_pred, color="blue", alpha=0.3)
plt.plot([y_test.min(), y_test.max()], [y_test.min(), y_test.max()], color="red")
plt.show()

plt.plot([y_test.min(), y_test.max()], [y_test.min(), y_test.max()], color="red")の個所について。

  • y_test.min()はテストセットの目的変数(実際の住宅価格)の最小値を返す
  • y_test.max()はテストセットの目的変数の最大値を返す
    これらは、y_testの範囲を定義するために使用する。つまり、実際の価格がどの範囲に収まるかを示す。

出力はこんな感じ。
全てが範囲に収まっているわけではないが、何となくいい感じに見えるぞ!?

回帰係数(重み)を確認する

次にモデルから回帰係数(重み)を参照する方法。
今回のデータセットでは特徴量が8つあることは確認済み。

では、それぞれの特徴量に対してどれだけの重みがかかっているのかを確認したい。
そんな時は学習済みモデルのcoef_を参照すればいい。

実際のコードはこんな感じ。

Act15.py
from sklearn.datasets import fetch_california_housing
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
import matplotlib.pyplot as plt

# カリフォルニア住宅データをロード
california_housing = fetch_california_housing()
X = california_housing.data  # 特徴量(MedInc, HouseAge, AveRooms など)
y = california_housing.target  # ターゲット(住宅の価格)

# データを訓練用とテスト用に分ける
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# 線形回帰モデルを作成
model = LinearRegression()

# モデルを訓練
model.fit(X_train, y_train)

# 予測を行う
y_pred = model.predict(X_test)

# 結果を表示
plt.figure(figsize=(10, 6))
plt.barh(california_housing.feature_names, model.coef_)
plt.xlabel("Coefficient value")
plt.show()

こんな感じでそれぞれの特徴量に対する重みを確認することが出来る。
Population(人口)に対してほどんど重みが掛かっていない、つまり重要視されていないことが分かる。

目で見て判断するのはざっとこんな感じかな?
新しいのを学んだら以降の記事で書いていこうと思う。

平均二乗誤差(MSE)

次に平均二乗誤差について。
この前、最小二乗法を学んだばかりなのに似たような言葉が召喚された…。

まあ、最小二乗法のおかげで何となくどんなものか予想がつく。

平均二乗誤差とは簡単に言うと、予測の「ズレ」を数値的に表した指標である。
「ズレ」とは、実際の値と予測値の差(誤差)のことで、MSEはその誤差を二乗して平均を取ることで、どれだけ予測が誤っているかを表す。

つまり、MSEの値が0に近ければ近いだけ予測の精度が高いと言える。
簡単な例を書いてみたから、ここまでの説明で良く分からなかったら読んでほしい。

pythonでMSEを求める方法はめちゃくちゃ簡単。

Act15.py
from sklearn.datasets import fetch_california_housing
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error
import matplotlib.pyplot as plt

# カリフォルニア住宅データをロード
california_housing = fetch_california_housing()
X = california_housing.data  # 特徴量(MedInc, HouseAge, AveRooms など)
y = california_housing.target  # ターゲット(住宅の価格)

# データを訓練用とテスト用に分ける
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# 線形回帰モデルを作成
model = LinearRegression()

# モデルを訓練
model.fit(X_train, y_train)

# 予測を行う
y_pred = model.predict(X_test)

# 平均二乗誤差を計算
mse = mean_squared_error(y_test, y_pred)
print(f"MSE: {mse}")

from sklearn.metrics import mean_squared_errorのインポート文を追加し、mean_squared_errorメソッドを使うだけで簡単に求められる。
引数には実際の値と予測した値を渡すだけ。

出力はこんな感じ。
これがいいのか悪いのかって分からなくないか…?

MSE: 0.5558915986952422

決定係数(R²)

次に決定係数について。

決定係数(R²)は、回帰モデルがどれくらいデータの変動を「説明できているか」を示す指標。
「説明出来ているか」と聞くと良く分からないが、簡単に言うと、モデルがどれだけうまくデータを予測しているかを測るもの。

もし仮に、予測したデータが実際のデータと一致している時、決定係数は1となり、逆に全く当てはまらない場合は決定係数が0となる。1に近いほどいいってことだね。

簡単な例を書いてみたから、ここまでの説明で良く分からなかったら読んでほしい。

pythonで決定係数を求める方法はめちゃくちゃ簡単。

Act15.py
from sklearn.datasets import fetch_california_housing
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import r2_score

# カリフォルニア住宅データをロード
california_housing = fetch_california_housing()
X = california_housing.data  # 特徴量(MedInc, HouseAge, AveRooms など)
y = california_housing.target  # ターゲット(住宅の価格)

# データを訓練用とテスト用に分ける
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# 線形回帰モデルを作成
model = LinearRegression()

# モデルを訓練
model.fit(X_train, y_train)

# 予測を行う
y_pred = model.predict(X_test)

# 決定係数を計算
r2 = r2_score(y_test, y_pred)
print(f"R²: {r2}")

from sklearn.metrics import r2_scoreのインポート文を追加し、r2_scoreメソッドを使うだけで簡単に求められる。
引数には実際の値と予測した値を渡すだけ。

出力はこんな感じ。
約56%が予測通りって考えれば、まあ何となくは予測出来てるって判断してもいいのかも?

MSE: 0.5558915986952422

一応これでカリフォルニアの住宅価格データセットを線形回帰で分析することが出来た!
中々長かったが、わからなかったんだから仕方がない。

正直明日になったら記憶から抜けそうだから、出勤の時間に見直すようにする。

疑問

特徴量(入力の数)が複数あるんだが?

ChatGTPさんに「特徴量が複数あるんだけど、線形回帰って特徴量は一つじゃないの!?」と質問してみた。

結論から、複数の特徴量を扱う線形回帰は、多変量回帰(Multiple Linear Regression) と呼ばれるらしい。
そして、一つの特徴量を扱う線形回帰を単回帰と呼ぶらしい。

なるほど、今まで学習してきたのは単回帰だったのね。

単回帰(1つの特徴量を扱う線形回帰)

まず、単回帰(Simple Linear Regression) を復習する。
単回帰は1つの特徴量(独立変数と言うらしい)と1つのターゲット(従属変数と言うらしい)の関係をモデル化する。

例えば部屋数を特徴量にして、住宅価格(ターゲット)を予測するモデルを作るなど。
その関係を公式に変換すると以下の通り。

y = w_1 x_1 + b

中学の頃に習ったy = ax + bのようなもんか。
ちなみにそれぞれは以下のような意味。

  • y はターゲット(住宅価格)
  • x_1 は特徴量(部屋数)
  • w_1 は重み(回帰係数)
  • b はバイアス(切片)

例えば、y = 2x + 5 の場合、部屋数が2だと住宅価格は9となる。
部屋数がかなり重要な項目で重みが大きくなり y = 10x + 5 のような場合、部屋数が2でも住宅価格は25となる。

つまり重みはかなり重要というわけか。

多変量回帰(複数の特徴量を使う線形回帰)

一方、カリフォルニア住宅データセットでは特徴量が8つ存在する。(例えば、MedInc, HouseAge, AveRooms など)
この場合、線形回帰は多変量回帰(Multiple Linear Regression) となる。

多変量回帰では、複数の特徴量(入力変数) を使って、1つのターゲット(出力)を予測する。
数式で表すと以下の通り。

y = w_1 x_1 + w_2 x_2 + w_3 x_3 + \dots + w_8 x_8 + b

ちなみにそれぞれは以下のような意味。

  • y はターゲット(住宅価格)
  • x_1, x_2, \dots, x_8 は特徴量(収入、住宅年齢、部屋数、位置情報など)
  • w_1, w_2, \dots, w_8 はそれぞれの特徴量に対応する重み(回帰係数)
  • b はバイアス(切片)

このように、8つの特徴量それぞれに対応する重み(w_1, w_2, \dots, w_8)を学習して、ターゲット(住宅価格)を予測するモデルを作る。

なるほど!結構理解できた気がする!!面白い!

数学的なイメージ

多変量線形回帰では、特徴量が複数あるため、各特徴量の重要度(回帰係数もしくは重み)を求める。
それぞれの特徴量がどれだけターゲットに影響を与えるかを学習する。

例えば、次のように解釈できる。

  • w_1 が大きい場合、MedInc(地域の収入)が住宅価格に与える影響が大きいということ。
  • w_2 が小さい場合、HouseAge(住宅の年齢)は価格にあまり影響を与えないということ。

オッケーオッケー!
ある程度は理解できたため先ほどの続きに戻ることにする。

平均二乗誤差を分かりやすい例で説明してみた

お金を使ったやり取りを例にする。ここでは、実際の金額(目標の金額)と予測した金額(あなたが予想した金額)との「ズレ」を計算していく。

シナリオ

友達にお金を貸した後、返済額を予測することにした。しかし、予測した金額(あなたの予測)は毎回少しずつズレていた。ここでは、実際に返済された金額と予測した金額との誤差を求める。

例えば、あなたが予測した返済額は次の通り

  • 1回目: 1000円
  • 2回目: 1500円
  • 3回目: 2000円

そして、実際に返済された金額(実際の金額)は次の通り

  • 1回目: 1200円
  • 2回目: 1400円
  • 3回目: 1800円

1. 各回の誤差(ズレ)の計算

まず、実際に返済された金額(y_i)とあなたが予測した金額(\hat{y}_i)の「ズレ」を求める。このズレが誤差となる。

  • 1回目の誤差: 1200 - 1000 = 200
  • 2回目の誤差: 1400 - 1500 = -100
  • 3回目の誤差: 1800 - 2000 = -200

ここで、2回目と3回目の誤差は負の値(マイナスのズレ)となる。つまり、予測額を実際よりも多く予測してしまったため、実際に返済された金額よりも少し多く返済額を見積もっていた。

2. 誤差を二乗する

誤差をそのまま使うのではなく、誤差を二乗して大きさを比較する。これによって、正の誤差も負の誤差も同じように扱われることになる。
最小二乗法の時と同じような理屈。

  • 1回目の誤差の二乗: 200^2 = 40000
  • 2回目の誤差の二乗: (-100)^2 = 10000
  • 3回目の誤差の二乗: (-200)^2 = 40000

3. 平均二乗誤差(MSE)の計算

二乗した誤差を平均する。

MSE = \frac{40000 + 10000 + 40000}{3} = \frac{90000}{3} = 30000

この 30000 が、予測モデルの「平均二乗誤差(MSE)」になる。

MSEの解釈

  • MSEが小さい場合: 予測値と実際の値がほぼ一致していることを意味する。誤差が小さく、予測が良いと言える。
  • MSEが大きい場合: 予測値と実際の値に大きなズレがあることを意味する。予測が不正確であることを示す。

このように、MSEを使うことで、どれだけ予測が誤っているのかを数値的に把握できる。
今回の場合だと、MSEが100とかだったらかなり精度が高い予測というイメージかな?

決定係数を分かりやすい例で説明してみた

まず初めに、決定係数(R²)の数式は以下の通りとなる。

R^2 = 1 - \frac{\sum_{i=1}^{n}(y_i - \hat{y}_i)^2}{\sum_{i=1}^{n}(y_i - \overline{y})^2}
  • y_i : 実際の値(観測値)
  • \hat{y}_i : 予測値
  • \overline{y} : 実際の値の平均(実測値の平均)
  • n : データの数

いつもの通り、全く意味が分からない!!!
ということで例として気温を予測するモデルを作った前提で書いていく。

5日間の天気を予測した値と実際の値を表にしてみたので、このデータをもとに決定係数を求めていく。

日付 実際の気温(℃) 予測したの気温(℃)
1日目 30 28
2日目 32 33
3日目 29 31
4日目 34 35
5日目 33 32

分子部分:残差平方和(RSS)

分子部分は残差平方和 (RSS) と呼び、「実際の値」と「予測した値」の差を2乗して、それを全て足し合わせたもの。

\text{RSS} = \sum_{i=1}^{n}(y_i - \hat{y}_i)^2

2乗する理由はいつもの通り、負の値をなくすため。
\sum_{i=1}^{n} は、iを1~n番目まで順に処理していくみたいな意味。
y_i は実際の値で \hat{y}_i は予測値なので、そんなに難しいことはしていない。

誤差を2乗したものを表に追加してみた。

日付 実際の気温(℃) 予測したの気温(℃) 誤差(実際の値 - 予測した値) 誤差の2乗
1日目 30 28 2 4
2日目 32 33 -1 1
3日目 29 31 -2 4
4日目 34 35 -1 1
5日目 33 32 1 1

残差平方和(RSS)は、誤差の2乗の合計となるため以下の通り。

\text{RSS} = 4 + 1 + 4 + 1 + 1 = 11

分母部分:全平方和(TSS)

分母部分は全平方和 (TSS) と呼び、「実際の値」と「実際の値の平均値」の差を2乗して、それを全て足し合わせたもの。

\text{TSS} = \sum_{i=1}^{n}(y_i - \overline{y})^2

y_i 実際の値で \overline{y} は実際の値の平均であるため、こっちもそんなに難しいことはしていない。

実際の気温の平均値は以下の通り。

\overline{y} = \frac{30 + 32 + 29 + 34 + 33}{5} = \frac{158}{5} = 31.6
日付 実際の気温(℃) 実際の気温と平均の差(実際 - 平均) 差の二乗
1日目 30 30 - 31.6 = -1.6 2.56
2日目 32 32 - 31.6 = 0.4 0.16
3日目 29 29 - 31.6 = -2.6 6.76
4日目 34 34 - 31.6 = 2.4 5.76
5日目 33 33 - 31.6 = 1.4 1.96

全体平方和(TSS)は、差の2乗の合計となるため以下の通り。

\text{TSS} = 2.56 + 0.16 + 6.76 + 5.76 + 1.96 = 17.2

これで主役はそろったので最後の計算

決定係数(R²)の計算

式に当てはめると以下の通り。

R^2 = 1 - \frac{\text{RSS(残差平方和)}}{\text{TSS(全平方和)}} = 1 - \frac{11}{17.2} = 1 - 0.639 = 0.361

R^2 = 0.361 ということは、この予測方法では実際の気温の変動の 36.1% しか説明できていないということになる。
残りの 63.9% の変動は、予測ではうまく説明できていないということになる。

おさらいだが、決定係数(R²)は、回帰モデルがどれくらいデータの変動を「説明できているか」を示す指標だった。

ということで、天気の回帰モデルは36.1%しか説明が出来ていない精度の低いモデルだということがわかる。

なるほどなるほど!

図も書いてみた

実際の値と予測した値の誤差

実際の値と平均値のバラつき

平均値からのバラつきに対して、予測の誤差が大きいから決定係数が36.1%になったってことか。
データ数が少ないから何とも言えないけど、もっと多いデータ数だったら変わったのかもね。

面白いね。

さいごに

分からない単語だらけで正直大変だった。

なんだかやっとスタートラインに立った気がするから、これからもっと頑張るぞー!

次回はロジスティック回帰について学習してみようと思う。

ではまた

Discussion