ナード戦隊データマン

データサイエンスを用いて悪と戦うぞ

線形モデル: RidgeとLassoを調べてみる

正則化とは、過学習を防いだり、モデル不良を起こさないための手法です。普通の線形回帰では正則化が用いられませんが、Ridge回帰とLassoという手法では、それぞれL2とL1という正則化を行います。ここでは、それぞれの手法で係数がどう変化するのかと、汎化性能を調べてみましょう。

必要ライブラリのインポート

import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
import mglearn
from IPython.display import display

普通の線形回帰を調べる

データには、100の特徴量が用意されたbostonデータを用います。

from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split

X, y = mglearn.datasets.load_extended_boston()
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)
lr = LinearRegression().fit(X_train, y_train)
print("Train set score: {:.2f}".format(lr.score(X_train, y_train)))
print("Test set score: {:.2f}".format(lr.score(X_test, y_test)))
Train set score: 0.95
Test set score: 0.61
plt.plot(lr.coef_)
plt.xlabel("Coefficient index")
plt.ylabel("Coefficient magnitude")

f:id:mathgeekjp:20170831143517p:plain

上記を見ると、訓練データの精度が高い一方で、テストデータへの精度が低いので、過学習している可能性が考えられます。

Ridge回帰

Ridge回帰では、L2正則化を用いています。つまり、L2ノルム(係数のユークリッド長)に対してペナルティを与えます。alpha値を設定することにより、係数への制約を調整できます。

from sklearn.linear_model import Ridge

for alpha in [0.1, 1, 3, 7, 10]:
    ridge = Ridge(alpha=alpha).fit(X_train, y_train)
    print("\n alpha={}".format(str(alpha)))
    print("Train set score: {:.2f}".format(ridge.score(X_train, y_train)))
    print("Test set score: {:.2f}".format(ridge.score(X_test, y_test)))
    plt.plot(ridge.coef_,label="alpha:{}".format(str(alpha)))

plt.xlabel("Coefficient index")
plt.ylabel("Coefficient magnitude")
plt.legend()
alpha=0.1
Train set score: 0.93
Test set score: 0.77

 alpha=1
Train set score: 0.89
Test set score: 0.75

 alpha=3
Train set score: 0.85
Test set score: 0.71

 alpha=7
Train set score: 0.81
Test set score: 0.66

 alpha=10
Train set score: 0.79
Test set score: 0.64

f:id:mathgeekjp:20170831144312p:plain

alphaが大きいほど、係数の範囲が狭まっていることがわかります。

mglearn.plots.plot_ridge_n_samples()

f:id:mathgeekjp:20170831144521p:plain

サンプル数に対してどれだけの性能を出すかを調べてみると、Ridgeのほうがテストセットに対して性能が高いことがわかります。しかし、データ数がある一定量を超えると、訓練データへの性能が落ちていることがわかります。これは、「データが多ければ過学習しにくくなる」ということを意味しています。

Lasso

Lassoでは、L1ノルム(係数の絶対値の和)にペナルティを与えます。Lassoの場合、いくつかの係数が完全に0になるため、特徴選択として機能します。そのため、モデルの解釈がしやすくなります。

from sklearn.linear_model import Lasso

for alpha in [0.02, 0.5, 1]:
    lasso = Lasso(alpha=alpha).fit(X_train, y_train)
    print("\n alpha={}".format(str(alpha)))
    print("Train set score: {:.2f}".format(lasso.score(X_train, y_train)))
    print("Test set score: {:.2f}".format(lasso.score(X_test, y_test)))
    print("Number of features used:{}".format(np.sum(lasso.coef_ != 0)))
    plt.plot(lasso.coef_, label="alpha:{}".format(str(alpha)))
plt.legend()
plt.xlabel("Coef index")
plt.ylabel("Coef magnitude")
 alpha=0.02
Train set score: 0.87
Test set score: 0.74
Number of features used:26

 alpha=0.5
Train set score: 0.61
Test set score: 0.50
Number of features used:6

 alpha=1
Train set score: 0.29
Test set score: 0.21
Number of features used:4

f:id:mathgeekjp:20170831145145p:plain

考察

実際に使う場合、特徴量がかなり多い場合には、Lassoを用いたほうがよいかもしれません。Lassoは解釈しやすいモデルが得られ、特徴選択をしてくれるからです。LassoとRidgeを組合せたElasticNetがあり、この組合せが最も良い結果をもたらすといわれますが、そのためにL1正則化パラメータとL2正則化パラメータを調節する手間はかかります。特徴選択をする必要性が無い場合は、L2を使ったほうがよさそうです。

参考

  1. API Reference — scikit-learn 0.19.0 documentation

  2. shop.oreilly.com

  3. github.com