はじめに
単回帰分析の理論とPythonでの実装について簡単な解説によって、説明していく。 (※あくまで個人の見解であるため、内容に不備がある可能性もございます。)目次
1. 単回帰分析とは? 2. 説明変数に最も当てはまりの良い1次関数を見つける 3. Pythonでの実装 4. まとめ1. 単回帰分析とは?
単回帰分析とは、1種類のデータから最も当てはまりの良い関数を見つけ、その関数に未知データを適用することで予測を行う手法である。 では、単回帰分析の「回帰」とは何を示しているだろうか。 <回帰> 回帰とは、未知データからある数値を予測することを指す。 (例:身長、体重) ここで、機械学習では「回帰」という問題のほかに、「分類」という問題もある。 <分類> 分類とは、未知データからあるクラスに分類すること指す。 主に、0か1の分類は2値分類と呼び、複数のクラスに分類することを多クラス分類と呼ぶ。 (例 : 花の種類、商品を買うか買わないか) 今回は単回帰分析であるため、回帰問題である。よって、ある数値を予測することを目的とする。2. 説明変数に最も当てはまりの良い1次関数を見つける
そもそも説明変数とは、予測したい値(目的変数)を求めるためのデータのことである。 単回帰分析を行うにあたって、説明変数に最も当てはまりの良い1次関数を見つけることで、最適な予測値を導き出したい。ではどうやってその関数を見つけ出すのだろうか。最小二乗法
最小二乗法とは、1次関数をf(x)とした時、 $$ \sum^{n}_{i=1}\lbrace y_i-f(x) \rbrace^{2} $$ が最小となるようなf(x)の値を求める方法である。(n : データの数, $y_i$ : 実際のデータ) それではどのようにして最小二乗法を行うのだろうか。 今回求めたい関数は1次関数であるから、 $$ f(x)=ax+b $$ としておく。(a:傾き, b:切片)[手順] ① xとyの平均値を求める。 平均値の求め方としては、 $$ \bar{x}=\frac{\sum^{n}_ix_i}{N} $$
である。 ( $x_i$:xのデータ, $\bar{x}$:xの平均値 , N:データの個数)
yの平均値も同じようにして求める。
② xの分散を求める。
分散とは、データのばらつき具合を示す数値である。式は以下である。
$$
V_x=\frac{1}{N}\sum^{n}_{i=1}(x_i-\bar{x})^{2}
$$
③ xとyの共分散を求める。
$$
V_x {}_y = \frac{1}{N}\sum^{n}_i(x_i-\bar{x})(y_i-\bar{y})
$$
④ 共分散をxの分散で割って、f(x)の傾きaを得る。
$$
a=\frac{V_x {}_y}{V_x}
$$
⑤ 切片bの値を求める。
$y=ax+b$より、
$b=y-ax$
であるから、切片bは、xとyのデータと、傾きaの値を用いて求めることができる。
こうして得られた傾きaとbを$y=ax+b$の式に適用することで、最適な関数f(x)を求めることができる。
3. Pythonでの実装
ここまでで学習した単回帰分析を実際にPythonで実装してみる。# 今回使うライブラリの読み込み
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression
今回は、説明変数を数学の点数、目的変数を理科の点数として予測を行なっていく。
訓練用データ↓
math | science |
---|---|
6 | 13 |
15 | 22 |
19 | 30 |
20 | 32 |
34 | 40 |
42 | 40 |
46 | 52 |
59 | 64 |
62 | 64 |
78 | 72 |
79 | 80 |
81 | 87 |
95 | 98 |
テスト用データ↓
math | science |
---|---|
5 | 14 |
10 | 17 |
22 | 30 |
36 | 44 |
48 | 55 |
64 | 64 |
67 | 65 |
72 | 78 |
77 | 80 |
86 | 88 |
97 | 96 |
# データの読み込み
train_df = pd.read_csv('train_data.csv')
test_df = pd.read_csv('test_data.csv')
# 説明変数と目的変数に分ける
train_x = train_df['math']
train_x = np.array(train_x).reshape(-1, 1)
train_y = train_df['science']
test_x = test_df['math']
test_x = np.array(test_x).reshape(-1, 1)
test_y = test_df['science']
# 訓練用データの形状
print("説明変数: ", train_x.shape)
print("目的変数: ", train_y.shape)
'''
実行結果
説明変数: (13, 1)
目的変数: (13,)
'''
実際に予測を行なっていく。scikit-learnのLinearRegressionを使って、モデルを構築する。
# モデルの構築
reg = LinearRegression()
# 学習
reg.fit(train_x, train_y)
'''
実行結果
LinearRegression()
'''
reg.coef_で1次関数の傾き、reg.intercept_で1次関数の切片を出力する。
print("傾き:a={}".format(reg.coef_))
print("切片:b={}".format(reg.intercept_))
'''
実行結果
傾き:a=[0.89738222]
切片:b=9.481915874431337
'''
上のコードで得られた傾きと切片の値を使って、$y=ax+b$のグラフを描画する。
# グラフを描画するための関数を定義する
def simple_linear_regression(x):
y = []
for i in x:
y.append(reg.coef_*i + reg.intercept_)
fig, ax1 = plt.subplots(1,1,figsize=(10,8))
ax2 = ax1.twinx()
ax1.plot(x, y, color='orange')
ax2.scatter(train_x, train_y)
ax1.set_xlabel("score of math", fontsize=18)
ax1.set_ylabel("score of science", fontsize=18)
plt.show()
# グラフの描画
x = range(0, 100)
simple_linear_regression(x)
実行結果は以下のようになる。
それでは、このモデルの評価を行なっていく。reg.score(x, y)とすることで、データの決定係数$R^{2}$を出力することができる。
決定係数とは、最大値が1であり、値が1に近いほどモデルが与えられたデータに当てはまっていることを示す。
式は以下のようになる。($\widehat{y_i}$:予測値)
$$
R^2=1-\frac{\sum_{i=1}^{n}(y_i-\widehat{y_i})^2}{\sum_{i=1}^{n}(y_i-\bar{y_i})^2}
$$
実際にreg.scoreを使って出力してみる。
# 訓練用データの評価
print("訓練用データに対する決定係数: {:.3f}".format(reg.score(train_x, train_y)))
# テスト用データの評価
print("テスト用データに対する決定係数: {:.3f}".format(reg.score(test_x, test_y)))
'''
実行結果
訓練用データに対する決定係数: 0.978
テスト用データに対する決定係数: 0.992
'''
どちらのデータに対しても決定係数が1に近いため、精度の高いモデルであることが分かる。