はじめに #
Elastic Net は回帰手法の1つで、\(L_1\)ノルムと\(L_2\)ノルムを用いて回帰係数の重みに制限を加えることで、予測性能を向上させることを目的としています。Elastic Netはラッソ回帰とリッジ回帰を組み合わせたものと言えます。
この記事ではPythonとScikit-learnによるサンプルコードも示します。実行環境は以下の通りです。
- Python: 3.9.7
- NumPy: 1.20.3
- sklearn: 0.24.2
Elastic Netのモデル #
Elastic Netの予測モデルは、重回帰モデルやリッジ回帰モデルなどと同様に次式で表されます。
$$ y = w_1 x_1 + w_2 x_2 + ... + w_N x_N + w_0 $$ここで、説明変数の数を\(N\), 説明変数を\(x_1, x_2, ..., x_N\), 目的変数を\(y\)と置いています。また、\(w_1, w_2, ..., w_N\)は重み、\(w_0\)は切片です。簡単のため、重みと係数をまとめて
$$\boldsymbol{w}=[w_0, w_1, w_2, ..., w_N]^{\top}$$とベクトル化します。
Elastic Netでは、モデルの学習において、最小化する関数として次式の\(J(\boldsymbol{w})\)を考えます。
$$ J(\boldsymbol{w}) = \frac{1}{2} \mathrm{MSE}(\boldsymbol{w}) + \alpha r \sum_{i=1}^{M} |\boldsymbol{w}_i| + \frac{1}{2} \alpha (1-r) \sum_{i=1}^{M} \boldsymbol{w}_i^2 $$右辺第1項の\(\mathrm{MSE}(\boldsymbol{w})\)は、予測値と実際の目的変数の平均二乗誤差 (mean square error, MSE) です。また、右辺第2項、右辺第3項はそれぞれ\(L_1\)ノルムと\(L_2\)ノルムによる正則化項です。
\(\alpha\)は正則化の強さを表すパラメータで、0以上の値を取ります。\(r\)は\(L_1\)ノルム正則化と\(L_2\)ノルム正則化の比率を表すパラメータで、0以上1以下の値を取ります。\(r=0\)のときは\(L_2\)ノルム正則化のみ有効になり、反対に\(r=1\)のときは\(L_1\)ノルム正則化のみ有効になります。
scikit-learnのElastic Net #
ElasticNetクラス #
scikit-learnではsklearn.linear_model.ElasticNetというクラスにElastic Netが実装されています。
sklearn.linear_model.ElasticNet(alpha=1.0, l1_ratio=0.5,
fit_intercept=True, normalize='deprecated', precompute=False,
max_iter=1000, copy_X=True, tol=0.0001, warm_start=False,
positive=False, random_state=None, selection='cyclic')
主なパラメータの意味は以下の通りです。
alpha(float): 正則化の強さ\(\alpha\)です。デフォルト値は1.0.l1_ratio(float): L1ノルム正則化の比率\(r\)です。デフォルト値は0.5.fit_intercept(bool):Trueの場合、切片を計算します。予測モデルが原点を通ることが想定される場合はFalseに設定します。tol(float): 最適化の許容誤差です。双対ギャップがtol以下になると計算を停止します。デフォルトは1e-4.random_state(int/None): 学習時の乱数シード。selectionが'random'のとき、常に同じ結果を得たい場合は適当な整数を指定します。Noneの場合、結果は変わり得ます。デフォルトはNone。selection(str):'cyclic'に設定すると、係数を順々に更新します。'random'に設定すると、係数をランダムな順序で更新します。tolが1e-4より大きい場合、'random'に設定すると早く収束する可能性が高くなります。
また、主なメソッドは以下の通りです。
fit(X, y): 特徴量X, クラスyを教師データとして学習する。predict(X): 特徴量Xに対する予測結果を返す。
使用例 #
ElasticNetクラスの使用例を示します。X_trainは行がサンプル、列が特徴量の2次元配列です(PandasのDataFrameなどでも可)。y_trainは目的変数の1次元配列です。次に、ElasticNetクラスのオブジェクトをregという名前で作成します(regはregressorから名付けています)。
import numpy as np
from sklearn.linear_model import ElasticNet
# 学習データ
X_train = np.array([[0, 1],
[3, 2],
[5, -2]])
y_train = np.array([3, 10, 23])
reg = ElasticNet(alpha=1.5, l1_ratio=0.7)
fitメソッドで学習し、predictメソッドで予測します。予測結果は1次元配列となります。
# 学習
reg.fit(X_train, y_train)
X_test = np.array([[2, 1],
[8, 2]])
# 予測
y_pred = reg.predict(X_test)
print(y_pred)
実行結果:
[ 9.3064187 23.28020001]
係数を確認するにはreg.coef_, 切片を確認するにはreg.intercept_を表示します。
print(reg.coef_)
print(reg.intercept_)
実行結果:
[ 2.57345047 -1.46692149]
5.626439250561536