1 理論
線形重回帰(OLS)における損失関数を最小にする回帰モデルの自由度は一般的に(特にデータが少ない時に)高く、過学習を起こしてしまう可能性がある。これを対処するために、正則化と呼ばれる処理をした手法がLasso回帰・Ridge回帰・ElasticNet回帰である。
(正則化法における損失関数) = (OLSにおける損失関数)+α×(ペナルティ)^n
ただし、α: ハイパーパラメータ、n: 1(Lasso)もしくは2(Ridge)
図1. (a)Lasso回帰、(b)Ridge回帰の損失関数の最小化[1]
図1(a)では楕円が(OLSにおける損失関数)、中心の四角形がL1ノルムを示している。Lasso回帰における損失関数はそれぞれの線形結合であるため、図上の接している箇所におけるパラメータを用いる時、損失関数は最小となる。接する箇所はL1ノルムの頂点であるため、特徴量の重みが0になりやすい。同様に図1(b)では楕円が(OLSにおける損失関数)、中心の円がL2ノルムを示している。接点におけるパラメータを用いる時、損失関数は最小となる。
ElasticNet回帰はL1とL2ノルムを加えた損失関数を用いており、αの他にノルムの混合割合をハイパーパラメータとして考慮する必要がある。
[1] J. R. Statist. Soc. B (1996) 58, No. 1, pp. 267-288
2 コード
Lasso、Ridge、ElasticNet回帰をクロスバリデーション、グリッドサーチを用いて行うpythonコードを示す。パッケージはscikit-learnを用いた。
from sklearn.datasets import make_regression
from sklearn.linear_model import Lasso, Ridge, ElasticNet
from sklearn.model_selection import GridSearchCV, train_test_split
from sklearn.metrics import mean_squared_error, r2_score
import matplotlib.pyplot as plt
# 仮想データセットの生成
X, y = make_regression(n_samples=1000, n_features=100, noise=0.1, random_state=42)
# データセットをトレーニングセットとテストセットに分割
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# ハイパーパラメータのグリッドを定義
param_grid = {'alpha': [0.001, 0.01, 0.1, 1, 10, 100]}
# モデルとグリッドサーチの設定
models = {
'Lasso': Lasso(),
'Ridge': Ridge(),
'ElasticNet': ElasticNet(param_grid_elastic_net={'alpha': [0.001, 0.01, 0.1, 1, 10, 100], 'l1_ratio': [0.1, 0.5, 0.9]})
}
for name, model in models.items():
if name == 'ElasticNet':
grid_search = GridSearchCV(model, param_grid_elastic_net, cv=5, scoring='neg_mean_squared_error')
else:
grid_search = GridSearchCV(model, param_grid, cv=5, scoring='neg_mean_squared_error')
grid_search.fit(X_train, y_train)
best_model = grid_search.best_estimator_
y_pred = best_model.predict(X_test)
mse = mean_squared_error(y_test, y_pred)
r2 = r2_score(y_test, y_pred)
# プロット
plt.scatter(y_test, y_pred, alpha=0.3, label=name)
plt.plot([y_test.min(), y_test.max()], [y_test.min(), y_test.max()], 'k--', lw=2)
plt.xlabel('実測値')
plt.ylabel('予測値')
plt.title(f'{name}回帰:予測値 vs 実測値')
plt.legend()
plt.show()
print(f'{name}回帰の最適なパラメータ: {grid_search.best_params_}')
print(f'{name}回帰のテストMSE: {mse}')
print(f'{name}回帰のR^2値: {r2}\n')
3 Q&A
Q1. Lasso回帰とRidge回帰の特徴は?
A1. Lasso回帰:相関の小さい特徴量の重みを0にすることができ、スパースなモデルが得られることで、推定と変数選択を同時に実行できる。ただし特徴量を削りすぎて、真陽性の低下が起こる可能性がある。
Ridge回帰:相関の小さい特徴量の重みを0に近づけることができるが、特徴量は残したままにできる。ただし説明変数を多く選択してしまい偽陽性の増加に繋がる可能性がある。
Q2. 3つの手法のうち、どれを使ったらいいの?
A2. 単に精度の良いモデルを作るという目的であれば、計算コストが基本的に小さいので3つともモデルを作って評価して良いと思う。強いて選ぶなら、ElasticNet回帰はOLS、Lasso、Ridgeが含まれているため、ハイパーパラメータをグリッドサーチ・クロスバリデーションなどでチューニングしてElasticNetを用いれば良いのではないだろうか。ただし過学習により汎化性が落ちる可能性があり万能とは言えないため、LassoやRidgeも検討したら良いと思う。
特徴量選択により解釈性を高めたいのであればLasso回帰、EDAやVIFの計算などを通じて多重共線性が高いと分かればLassoもしくはRidge回帰を選ぶという選択肢もあると思う。