はじめに
本稿では機械学習の基礎の基礎として線形回帰について、Pythonのサンプルコードを併せて学んでいきます。
scikit-learnのデータセットであるカリフォルニアの住宅価格データセットから目的変数である住宅価格を予測します。
用語の説明
説明変数
目的変数を説明する変数のことです。例えば、「部屋数」「築年数」「緯度経度」から「住宅価格」を求める場合、この「部屋数」「築年数」「緯度経度」が説明変数となります。
目的変数
説明変数から予測された変数のことです。例えば、「部屋数」「築年数」「緯度経度」から「住宅価格」を求める場合、この「住宅価格」が目的変数となります。
回帰
予測する値が数値など連続する値のときのことです。例えば、「住宅価格」などは連続する値を取ることができるので回帰となります。
分類
予測する値がYes,Noのようなデータが属するクラスである場合のことです。カリフォルニアの住宅価格データセットには入っていませんが、例えば仮に、「ユニットバスかセパレートか」といった項目があれば分類となります。
線形回帰
さて、いよいよ機械学習の最も基本的な手法の1つである線形回帰について見ていきます。線形回帰とは回帰分析の一種で、目的変数の予測値yを説明変数xを用いて
y = a_1x_1 + a_2x_2 + \cdots + a_nx_n
の形で表すことによって予測する手法です。
まずは、データセットの確認です。
import pandas as pd
from sklearn.datasets import fetch_california_housing
# カリフォルニアの住宅価格データセットの読み込み
california = fetch_california_housing()
# データフレームを表示
df = pd.DataFrame(california.data, columns=california.feature_names)
df['Price'] = california.target
print(df)
MedInc | HouseAge | AveRooms | AveBedrms | Population | AveOccup | Latitude | Longitude | Price | |
---|---|---|---|---|---|---|---|---|---|
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 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
20638 | 1.8672 | 18.0 | 5.329513 | 1.171920 | 741.0 | 2.123209 | 39.43 | -121.32 | 0.781 |
20639 | 2.3886 | 16.0 | 5.254717 | 1.162264 | 1387.0 | 2.616981 | 39.37 | -121.24 | 0.771 |
データは上図のようなモノとなっています。それでは、回帰分析をやってみましょう。
まずは、このデータの分布を見てみましょう。
# データの分布を確認
axes = df.hist(bins=48, figsize=(14,10))
住宅価格の5付近に異常な数のデータがあることが確認できます。住宅価格が丸められているとするとモデル作成に悪影響を及ぼしてしまいます。なので、まず、こういったデータを除去する必要があります。
# 価格の最大値未満のデータのみ取得
df = df[df['Price'] < df['Price'].max()]
次に、説明変数xと目的変数yにデータを分けます。
# 説明変数
x = df.drop(['Price'], axis=1)
# 目的変数
y = df['Price']
次に、訓練データとテストデータを分けます。
こちらはscikit-learnのtrain_test_splitを使って実装します。
また、後の処理で目的変数の標準化を施すために、目的変数を二次元配列に変換します。
from sklearn.model_selection import train_test_split
# random_state=42で訓練データとテストデータを作成
x_training, x_test, y_training, y_test = train_test_split(x, y, test_size=0.1, random_state=42)
# y_training, y_testを二次元配列に変換
y_training = y_training.values.reshape(-1,1)
y_test = y_test.values.reshape(-1,1)
次に、各データを標準化します。標準化とはデータの平均を0、分散を1にすることで範囲の異なるそれぞれの変数を同様に扱うことが可能になります。こちらは、scikit-learnのStandardScalerで実装します。
from sklearn.preprocessing import StandardScaler
# 標準化
scaler_x = StandardScaler()
x_training = scaler_x.fit_transform(x_training)
x_test = scaler_x.transform(x_test)
scaler_y = StandardScaler()
y_training = scaler_y.fit_transform(y_training)
y_test = scaler_y.transform(y_test)
さて、いよいよ学習です。
今回は、標準化を施しているため、fit_intercept=Falseを指定します。
from sklearn.linear_model import LinearRegression
# 線形回帰実行
model = LinearRegression(fit_intercept=False)
model.fit(x_training, y_training)
これで学習は完了です。
結果の確認ですが、横軸を予測値、縦軸を実測値とした散布図を書き、正の相関を確認できれば「予測精度が良い」と判断できそうです。
また、定量化するためにスコア RMSE を計算します。今回、標準化により分散1となっているため、RMSEの値は1より十分に小さいことが求められます。
それではRMSEを計算し、散布図を書いてみます。
import matplotlib.pyplot as plt
from sklearn.metrics import mean_squared_error
#RMSEを計算
rmse = mean_squared_error(y_test,model.predict(x_test), squared=False)
# 結果確認
plt.figure(figsize=(5,5))
plt.xlim(-3, 4)
plt.ylim(-3, 4)
plt.plot(model.predict(x_test), y_test, 'o')
plt.title('RMSE: {:.3f}'.format(rmse))
plt.xlabel('Predict')
plt.ylabel('Actual')
plt.grid()
plt.show()
散布図とRMSEは次のようになりました。
一部、外れている箇所もありますが、おおむね正の相関となりRMSE=0.670であることを確認できました。
今回は行いませんが、より良い精度を出すためにはより良いアルゴリズムを用いたりより良いデータの前処理を施したりする必要があります。
以下、コードまとめです。
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.preprocessing import StandardScaler
from sklearn.datasets import fetch_california_housing
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error
# カリフォルニアの住宅価格データセットの読み込み
california = fetch_california_housing()
# データフレームを表示
df = pd.DataFrame(california.data, columns=california.feature_names)
df['Price'] = california.target
print(df)
# データの分布を確認
axes = df.hist(bins=48, figsize=(14,10))
# 価格の最大値未満のデータのみ取得
df = df[df['Price'] < df['Price'].max()]
# 説明変数
x = df.drop(['Price'], axis=1)
# 目的変数
y = df['Price']
# random_state=42で訓練セットを作成
x_training, x_test, y_training, y_test = train_test_split(x, y, test_size=0.1, random_state=42)
# y_training, y_testを二次元配列に変換
y_training = y_training.values.reshape(-1,1)
y_test = y_test.values.reshape(-1,1)
# 標準化
scaler_x = StandardScaler()
x_training = scaler_x.fit_transform(x_training)
x_test = scaler_x.transform(x_test)
scaler_y = StandardScaler()
y_training = scaler_y.fit_transform(y_training)
y_test = scaler_y.transform(y_test)
# 線形回帰実行
model = LinearRegression(fit_intercept=False)
model.fit(x_training,y_training)
#予測精度の確認
rmse = mean_squared_error(y_test,model.predict(x_test), squared=False)
# 結果確認
plt.figure(figsize=(5,5))
plt.xlim(-3, 4)
plt.ylim(-3, 4)
plt.plot(model.predict(x_test), y_test, 'o')
plt.title('RMSE: {:.3f}'.format(rmse))
plt.xlabel('Predict')
plt.ylabel('Actual')
plt.grid()
plt.show()