このノート全体にわたって、共通して使用するパッケージ、関数やデータなどをここで呼び出して、すぐに使える状態にする。
%matplotlib inline
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
このノートでは scikit-learn パッケージに保存されているボストン住宅価格データセットを使って、回帰分析を行う例を示す。このデータセットは、各区画における住宅の価格、犯罪率、非小売業の割合、全額固定資産税率などを集計したデータとなっています。このページではこのデータセット使って、犯罪率、非小売業の割合、全額固定資産税率などの特徴量(説明変数)を使って、住宅の価格(目的変数)を予測する回帰分析を行う例を示す。
import sklearn.datasets
boston = sklearn.datasets.load_boston()
X = boston.data
y = boston.target
print(X.shape)
boston_df = pd.DataFrame(np.concatenate([y.reshape(y.shape[0], 1), X], axis=1),
columns=['y' if i == 0 else 'X' + str(i) for i in range(X.shape[1] + 1)])
sns.pairplot(boston_df)
sns.pairplot(boston_df, y_vars=['y'], x_vars=boston_df.columns[1:])
scikit-learn で回帰分析(単回帰・重回帰)を行うには、線形モデルモジュール sklearn.linear_model
中の LinearRegression
関数を使用する。データの分布を眺めて、6 番目の特徴量と 13 番目の特徴量が、目的変数との相関が高そうであるから、まず、これらの特徴量で単回帰モデルを作成してみる。
import sklearn.model_selection
import sklearn.metrics
import sklearn.linear_model
X_subset = X[:, [5]]
X_train, X_test, y_train, y_test =\
sklearn.model_selection.train_test_split(X_subset, y, test_size=0.3, random_state=2020)
reg = sklearn.linear_model.LinearRegression()
reg.fit(X_train, y_train)
y_pred = reg.predict(X_test)
教師ラベルを縦軸に、予測値を横軸に散布図で可視化して、モデルのテスト結果を確認してみる。グラフをみると、だいたい予測できているが、一部のサンプルで、実際の値よりも高く予測されているのがわかる。
plt.scatter(y_pred, y_test)
plt.plot([0, 50], [0, 50])
plt.grid()
plt.ylabel('true')
plt.xlabel('predicted')
plt.show()
教師ラベル(真の値)と予測値の差を残差とよび、各サンプルの残差をプロットした図を残差プロットという。上のモデルの予測結果を利用して残差プロットを描いてみる。残差プロットをみると、全体的に、予測した値は、実際の値よりも大きくなっているのを確認できる。
plt.scatter(X_test, y_test - y_pred)
plt.ylabel('residuals')
plt.xlabel('X')
plt.show()
この残差から MSE を計算して、モデル良さを測る指標を計算する。
mse = sklearn.metrics.mean_squared_error(y_test, y_pred)
print(mse)
次に 13 番目の特徴量だけを使って、住宅価格を予測してみる。
import sklearn.model_selection
import sklearn.metrics
import sklearn.linear_model
import sklearn.pipeline
X_subset = X[:, [12]]
X_train, X_test, y_train, y_test =\
sklearn.model_selection.train_test_split(X_subset, y, test_size=0.3, random_state=2020)
reg = sklearn.linear_model.LinearRegression()
reg.fit(X_train, y_train)
y_pred = reg.predict(X_test)
mse = sklearn.metrics.mean_squared_error(y_test, y_pred)
print(mse)
MSE をみると一つ前のモデルよりも良いモデルができるこを確認できる。上と同様に、残差プロットを描いてみる。この残差プロットを見ても、全体的に、予測値が実際の値よりも大きくなる傾向が見られる。
plt.scatter(X_test, y_test - y_pred)
plt.ylabel('residuals')
plt.xlabel('X')
plt.show()
次に、この 13 番目の特徴量を対数化してからもう一度モデルを構築してみる。MSE をみると、対数化することで、MSE が大きく改善しているのが確認できる。また、残差プロットをみると、X の値が小さ時に誤差が大きいことがわかる。一つの特徴量でばんばるにはこれで限界かもしれない。
import sklearn.model_selection
import sklearn.metrics
import sklearn.linear_model
import sklearn.pipeline
X_subset = np.log10(X[:, [12]])
X_train, X_test, y_train, y_test =\
sklearn.model_selection.train_test_split(X_subset, y, test_size=0.3, random_state=2020)
reg = sklearn.linear_model.LinearRegression()
reg.fit(X_train, y_train)
y_pred = reg.predict(X_test)
mse = sklearn.metrics.mean_squared_error(y_test, y_pred)
print(mse)
plt.scatter(X_test, y_test - y_pred)
plt.ylabel('residuals')
plt.xlabel('X')
plt.show()
同じ関数で単回帰・重回帰モデルも構築することは可能である。重回帰分析の場合も、コードを変更せずに、特徴量を大きな行列として与えればよい。もちろん、特徴量が多いほど、バリアンスの大きいモデル(複雑なモデル)が構築される傾向が見えるので、予測誤差が小さくなっていきます。また、重回帰分析のとき、各変数間のスケールが異なるので、スケールを揃えるために標準化を行う必要がある。そのため、これ以降パイプラインを使ってモデルを構築していくことにする。
import sklearn.model_selection
import sklearn.metrics
import sklearn.linear_model
import sklearn.pipeline
X_train, X_test, y_train, y_test =\
sklearn.model_selection.train_test_split(X, y, test_size=0.3, random_state=2020)
pipe = sklearn.pipeline.Pipeline([
('scaler', sklearn.preprocessing.StandardScaler()),
('reg', sklearn.linear_model.LinearRegression())
])
pipe.fit(X_train, y_train)
y_pred = pipe.predict(X_test)
mse = sklearn.metrics.mean_squared_error(y_test, y_pred)
print(mse)
13 番目の特徴量を使うだけで MSE が 29.222 であるのに対して、特徴量を 13 個全部使っての MSE が 23.44 であり、それほど改善しているように見えない。この回帰モデルの各係数は、次のようにして取得することができる。
回帰モデル中の各パラメーター(各係数)は、pipe.intercept_
および pipe.coef_
からアクセスすることができる。
print(pipe['reg'].intercept_)
print(pipe['reg'].coef_)
残差プロットを描いて、残差の分布を確認してみる。特徴量全部使っても、予測値が、真の値よりもやや高くなっているのがわかる。おそらく、学習データの中に外れ値的なものが存在して、それに影響されていると思われる。
plt.scatter(np.arange(0, len(y_test)), y_test - y_pred)
plt.ylabel('residuals')
plt.xlabel('test sample id')
plt.show()
LASSO を使用すると、変数選択を自動的に行ってくれる。例えば、次のように 13 特徴量全体を使ってモデルを構築することからスタートして、LASSO 実行して、その係数を見てみる。
import sklearn.model_selection
import sklearn.metrics
import sklearn.linear_model
import sklearn.pipeline
X_train, X_test, y_train, y_test =\
sklearn.model_selection.train_test_split(X, y, test_size=0.3, random_state=2020)
pipe = sklearn.pipeline.Pipeline([
('scaler', sklearn.preprocessing.StandardScaler()),
('reg', sklearn.linear_model.Lasso(alpha=0.01))
])
pipe.fit(X_train, y_train)
y_pred = pipe.predict(X_test)
mse = sklearn.metrics.mean_squared_error(y_test, y_pred)
print(mse)
print(pipe['reg'].coef_)
print(pipe['reg'].intercept_)
import sklearn.model_selection
import sklearn.metrics
import sklearn.linear_model
import sklearn.pipeline
X_train, X_test, y_train, y_test =\
sklearn.model_selection.train_test_split(X, y, test_size=0.3, random_state=2020)
pipe = sklearn.pipeline.Pipeline([
('scaler', sklearn.preprocessing.StandardScaler()),
('reg', sklearn.linear_model.Lasso(alpha=1))
])
pipe.fit(X_train, y_train)
y_pred = pipe.predict(X_test)
mse = sklearn.metrics.mean_squared_error(y_test, y_pred)
print(mse)
print(pipe['reg'].coef_)
print(pipe['reg'].intercept_)
LASSO のハイパーパラメーターとして alpha
がある。この alpha
を動かして、最小 MSE を与える alpha
を求めて、そのときのモデルの性能を評価してみよう。ハイパーパラメーターを探す時は、5-fold 交差検証を使用してみるとよい。
import sklearn.model_selection
import sklearn.metrics
import sklearn.linear_model
import sklearn.pipeline
X_train, X_test, y_train, y_test =\
sklearn.model_selection.train_test_split(X, y, test_size=0.4, random_state=2020)
pipe = sklearn.pipeline.Pipeline([
('scaler', sklearn.preprocessing.StandardScaler()),
('reg', sklearn.linear_model.Lasso())
])
parameters = {
'reg__alpha': 10**np.linspace(-5, 2, 10)
}
# GrideSearchCV で scoring='mse' を指定できないので、
# (R)MSE を計算する関数を自分で定義して GrideSearchCV に代入することにする
def calc_rmse(predict, actual):
return np.sqrt(sklearn.metrics.mean_squared_error(predict, actual))
rmse_score = sklearn.metrics.make_scorer(calc_rmse, greater_is_better = False)
gs = sklearn.model_selection.GridSearchCV(pipe, parameters, scoring=rmse_score, cv=10)
gs.fit(X_train, y_train)
best_pipe = gs.best_estimator_
print(best_pipe['reg'].coef_)
y_pred = best_pipe.predict(X_test)
mse = sklearn.metrics.mean_squared_error(y_test, y_pred)
print(mse)
次に、5-fold 交差検証を用いて、最適なハイパーパラメーターで elastic net 回帰モデルを構築し、その性能を評価してみよう。
import sklearn.model_selection
import sklearn.metrics
import sklearn.linear_model
import sklearn.pipeline
X_train, X_test, y_train, y_test =\
sklearn.model_selection.train_test_split(X, y, test_size=0.4, random_state=2020)
pipe = sklearn.pipeline.Pipeline([
('scaler', sklearn.preprocessing.StandardScaler()),
('reg', sklearn.linear_model.ElasticNet())
])
parameters = {
'reg__alpha': 10**np.linspace(-5, 2, 10),
'reg__l1_ratio': [0.1, 0.2, 0.3, 0.4, 0.5],
}
def calc_rmse(predict, actual):
return np.sqrt(sklearn.metrics.mean_squared_error(predict, actual))
rmse_score = sklearn.metrics.make_scorer(calc_rmse, greater_is_better = False)
gs = sklearn.model_selection.GridSearchCV(pipe, parameters, scoring=rmse_score, cv=10)
gs.fit(X_train, y_train)
best_pipe = gs.best_estimator_
print(best_pipe['reg'].coef_)
y_pred = best_pipe.predict(X_test)
mse = sklearn.metrics.mean_squared_error(y_test, y_pred)
print(mse)
ロバスト推定アルゴリズムの一つ RANSAC は RANSACRegressor
で実行することができる。この推定では、様々なハイパーパラメーターを設定していく必要がある。以下の例は、すべてのハイパーパラメーターをデフォルトのままでモデルを構築している。
import sklearn.model_selection
import sklearn.metrics
import sklearn.linear_model
import sklearn.pipeline
X_train, X_test, y_train, y_test =\
sklearn.model_selection.train_test_split(X, y, test_size=0.3, random_state=2020)
pipe = sklearn.pipeline.Pipeline([
('scaler', sklearn.preprocessing.StandardScaler()),
('reg', sklearn.linear_model.RANSACRegressor(random_state=2020))
])
pipe.fit(X_train, y_train)
y_pred = pipe.predict(X_test)
mse = sklearn.metrics.mean_squared_error(y_test, y_pred)
print(mse)
scikit-learn に実装されているロバスト推定アルゴリズムを調べてみると、スライドで紹介したRANSAC 以外のも、HuberRegressor
および HuberRegressor
がある。これらのアルゴリズムの詳細は Google 検索で。
import sklearn.model_selection
import sklearn.metrics
import sklearn.linear_model
import sklearn.pipeline
X_train, X_test, y_train, y_test =\
sklearn.model_selection.train_test_split(X, y, test_size=0.3, random_state=2020)
pipe = sklearn.pipeline.Pipeline([
('scaler', sklearn.preprocessing.StandardScaler()),
('reg', sklearn.linear_model.HuberRegressor())
])
pipe.fit(X_train, y_train)
y_pred = pipe.predict(X_test)
mse = sklearn.metrics.mean_squared_error(y_test, y_pred)
print(mse)