正則化の理論 — L1(Lasso)とL2(Ridge)の幾何学的理解

機械学習モデルが訓練データに完璧にフィットしているのに、テストデータでは性能が悪い——この過学習の問題は、パラメータの数がデータ数に対して多すぎるときに頻繁に発生します。モデルはデータの本質的なパターンだけでなく、ノイズまで学習してしまうのです。

では、パラメータの自由度を制限する方法はないでしょうか。答えの一つが正則化(regularization)です。正則化は損失関数にパラメータの大きさに対するペナルティ項を加えることで、「データへのフィット」と「モデルの単純さ」のバランスを取ります。

正則化の中でも最も基本的なL1正則化とL2正則化は、見た目は似ていますが性質が大きく異なります。L2正則化はパラメータを0に近づけますが、L1正則化はパラメータを正確に0にする——この違いはなぜ生じるのでしょうか。

正則化を理解すると、以下のような判断ができるようになります。

  • 特徴量選択: L1正則化(Lasso)で不要な特徴量を自動的に除去する
  • 多重共線性への対処: L2正則化(Ridge)で不安定な推定を安定化する
  • 正則化の強さの調整: $\lambda$ の値と汎化性能の関係
  • Elastic Net: L1とL2を組み合わせる理由

本記事の内容

  • L2正則化(Ridge回帰)の理論と解
  • L1正則化(Lasso回帰)の理論とスパース性
  • 幾何学的な解釈 — なぜL1はスパースな解を生むのか
  • ベイズ推定の観点からの解釈
  • Pythonによる実装と可視化

前提知識

この記事を読む前に、以下の記事を読んでおくと理解が深まります。

正則化の一般的な枠組み

損失関数 + ペナルティ項

正則化付きの目的関数は一般に次の形をとります。

$$ \begin{equation} J_{\text{reg}}(\bm{w}) = \underbrace{\frac{1}{n}\sum_{i=1}^n L(y_i, \hat{f}(\bm{x}_i; \bm{w}))}_{\text{データ項(損失関数)}} + \underbrace{\lambda \Omega(\bm{w})}_{\text{正則化項}} \end{equation} $$

$\lambda \geq 0$ は正則化パラメータで、データ項と正則化項のバランスを制御します。

  • $\lambda = 0$: 正則化なし。データへのフィットのみを追求
  • $\lambda \to \infty$: 正則化が支配的。パラメータが0に向かう(モデルが極端に単純化される)

$\Omega(\bm{w})$ はパラメータの「複雑さ」を測るペナルティ関数で、典型的に以下の2つが使われます。

  • L2ペナルティ(Ridge): $\Omega(\bm{w}) = \|\bm{w}\|_2^2 = \sum_j w_j^2$
  • L1ペナルティ(Lasso): $\Omega(\bm{w}) = \|\bm{w}\|_1 = \sum_j |w_j|$

まず、L2正則化から詳しく見ていきましょう。

L2正則化(Ridge回帰)

目的関数と解析解

線形回帰 $\hat{y} = \bm{X}\bm{w}$ にL2正則化を加えたRidge回帰の目的関数は

$$ J_{\text{Ridge}}(\bm{w}) = \frac{1}{2n}\|\bm{y} – \bm{X}\bm{w}\|^2 + \frac{\lambda}{2}\|\bm{w}\|^2 $$

$\bm{w}$ で微分して0とおくと

$$ -\frac{1}{n}\bm{X}^T(\bm{y} – \bm{X}\bm{w}) + \lambda\bm{w} = \bm{0} $$

$\bm{w}$ について解くと、Ridge回帰の解析解が得られます。

$$ \begin{equation} \hat{\bm{w}}_{\text{Ridge}} = (\bm{X}^T\bm{X} + n\lambda\bm{I})^{-1}\bm{X}^T\bm{y} \end{equation} $$

通常の最小二乗解 $\hat{\bm{w}}_{\text{OLS}} = (\bm{X}^T\bm{X})^{-1}\bm{X}^T\bm{y}$ と比較すると、$\bm{X}^T\bm{X}$ に $n\lambda\bm{I}$ が加わっています。

安定化の効果

$\bm{X}^T\bm{X}$ が特異に近い(多重共線性がある)とき、$(\bm{X}^T\bm{X})^{-1}$ の固有値が非常に大きくなり、推定が不安定になります。$n\lambda\bm{I}$ を加えることで最小固有値が $n\lambda$ 以上になることが保証され、逆行列の計算が安定化されます。

$\bm{X}^T\bm{X}$ の固有値分解を $\bm{X}^T\bm{X} = \bm{Q}\bm{\Lambda}\bm{Q}^T$($\bm{\Lambda} = \text{diag}(\lambda_1, \ldots, \lambda_d)$)とすると

$$ \hat{\bm{w}}_{\text{Ridge}} = \bm{Q}\text{diag}\left(\frac{\lambda_j}{\lambda_j + n\lambda}\right)\bm{Q}^T \hat{\bm{w}}_{\text{OLS}} $$

各固有方向のOLS推定値が $\frac{\lambda_j}{\lambda_j + n\lambda}$ 倍に縮小(shrinkage)されます。小さな固有値 $\lambda_j$ に対応する方向(不安定な方向)ほど強く縮小され、推定が安定化されるのです。

L2正則化の勾配降下での解釈

L2正則化は勾配降下の更新則に「重み減衰」を加える効果があります。

$$ \bm{w}_{t+1} = \bm{w}_t – \eta(\nabla J + \lambda\bm{w}_t) = (1 – \eta\lambda)\bm{w}_t – \eta\nabla J $$

各ステップでパラメータが $(1 – \eta\lambda)$ 倍に縮小され、これが重み減衰(weight decay)と呼ばれる理由です。

L2正則化がパラメータを0に「近づける」一方で、正確に0にはしません。特徴量選択が必要な場合にはL1正則化が適しています。

L1正則化(Lasso回帰)

目的関数

L1正則化を加えたLasso回帰(Least Absolute Shrinkage and Selection Operator; Tibshirani, 1996)の目的関数は

$$ J_{\text{Lasso}}(\bm{w}) = \frac{1}{2n}\|\bm{y} – \bm{X}\bm{w}\|^2 + \lambda\|\bm{w}\|_1 $$

$\|\bm{w}\|_1 = \sum_j |w_j|$ はL1ノルムです。

スパース性 — なぜパラメータが正確に0になるのか

L1正則化の最も重要な性質は、解のスパース性です。十分大きな $\lambda$ のもとで、推定されたパラメータの一部が正確に0になります。

これを1次元の場合で確認しましょう。$J(w) = \frac{1}{2}(y – xw)^2 + \lambda|w|$ の最小化を考えます。

$w > 0$ のとき $|w| = w$ なので $J'(w) = -x(y – xw) + \lambda = 0$、つまり $w = \frac{xy – \lambda}{x^2}$。ただし $w > 0$ が必要なので $xy > \lambda$。

$w < 0$ のとき $|w| = -w$ なので $J'(w) = -x(y - xw) - \lambda = 0$、つまり $w = \frac{xy + \lambda}{x^2}$。ただし $w < 0$ が必要なので $xy < -\lambda$。

$|xy| \leq \lambda$ のとき、$w = 0$ が最適です。

まとめると、Lassoの1次元の解はソフト閾値関数(soft thresholding)で表されます。

$$ \begin{equation} \hat{w}_{\text{Lasso}} = \text{sign}(xy) \max\left(|xy| – \lambda, 0\right) / x^2 \end{equation} $$

$|xy| \leq \lambda$ の領域で $\hat{w} = 0$ となり、これが特徴量選択の効果です。OLS解の絶対値が $\lambda$ 以下の特徴量は、推定値が正確に0になります。

劣勾配法

L1ノルム $|w|$ は $w = 0$ で微分不可能ですが、劣微分(subdifferential)を使って最適化できます。

$$ \partial |w| = \begin{cases} \{1\} & w > 0 \\ \{-1\} & w < 0 \\ [-1, 1] & w = 0 \end{cases} $$

$w = 0$ での劣微分が区間 $[-1, 1]$ であることが、$w = 0$ が最適解になりうる理由の数学的根拠です。$w = 0$ が最適解であるための条件は「0が劣勾配に含まれる」、すなわちデータ項の勾配の絶対値が $\lambda$ 以下であることです。

幾何学的な解釈でこの現象をさらに深く理解しましょう。

幾何学的な解釈

制約付き最適化としての正則化

正則化付き最適化は、ラグランジュの未定乗数法により、次の制約付き最適化問題と等価です。

$$ \min_{\bm{w}} \frac{1}{2n}\|\bm{y} – \bm{X}\bm{w}\|^2 \quad \text{subject to} \quad \Omega(\bm{w}) \leq t $$

$t$ は $\lambda$ に対応する定数です。この視点から、L1とL2の違いが幾何学的に理解できます。

  • L2制約: $\|\bm{w}\|_2^2 \leq t$ は超球(2次元では円)
  • L1制約: $\|\bm{w}\|_1 \leq t$ は超多面体(2次元ではひし形/ダイヤモンド)

損失関数の等高線(楕円)が制約領域に接する点が最適解です。

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Circle, Polygon

fig, axes = plt.subplots(1, 3, figsize=(16, 5))

# 損失関数の等高線
def loss_contour(ax, w1_range, w2_range, w_opt, A):
    W1, W2 = np.meshgrid(w1_range, w2_range)
    # (w - w_opt)^T A (w - w_opt)
    Z = A[0,0]*(W1-w_opt[0])**2 + 2*A[0,1]*(W1-w_opt[0])*(W2-w_opt[1]) + A[1,1]*(W2-w_opt[1])**2
    return W1, W2, Z

w_range = np.linspace(-3, 3, 200)
w_opt = np.array([2.0, 1.5])  # OLS解
A = np.array([[1.0, 0.3], [0.3, 2.0]])  # ヘシアン

# (a) L2正則化(Ridge)
ax = axes[0]
W1, W2, Z = loss_contour(ax, w_range, w_range, w_opt, A)
ax.contour(W1, W2, Z, levels=15, cmap="Blues", alpha=0.7)

# L2制約(円)
theta = np.linspace(0, 2*np.pi, 200)
t = 1.5  # 制約の半径
ax.plot(t*np.cos(theta), t*np.sin(theta), "r-", linewidth=2.5)
ax.fill(t*np.cos(theta), t*np.sin(theta), color="red", alpha=0.1)

# Ridge解(等高線と円の接点)
# 数値的に近似
from scipy.optimize import minimize
result = minimize(lambda w: 0.5*((w-w_opt)@A@(w-w_opt)),
                  x0=[0.5,0.5], method="SLSQP",
                  constraints={"type":"ineq", "fun": lambda w: t**2 - w@w})
w_ridge = result.x
ax.plot(*w_ridge, "r*", markersize=15, zorder=5)
ax.plot(*w_opt, "b*", markersize=15, zorder=5, label="OLS solution")

ax.set_xlabel("$w_1$", fontsize=12)
ax.set_ylabel("$w_2$", fontsize=12)
ax.set_title("L2 (Ridge): Circular constraint", fontsize=13)
ax.set_xlim(-3, 3); ax.set_ylim(-3, 3)
ax.set_aspect("equal")
ax.grid(True, alpha=0.3)
ax.legend(fontsize=9)

# (b) L1正則化(Lasso)
ax = axes[1]
W1, W2, Z = loss_contour(ax, w_range, w_range, w_opt, A)
ax.contour(W1, W2, Z, levels=15, cmap="Blues", alpha=0.7)

# L1制約(ひし形)
diamond = np.array([[t, 0], [0, t], [-t, 0], [0, -t], [t, 0]])
ax.plot(diamond[:, 0], diamond[:, 1], "r-", linewidth=2.5)
ax.fill(diamond[:, 0], diamond[:, 1], color="red", alpha=0.1)

# Lasso解(等高線とひし形の接点)
result = minimize(lambda w: 0.5*((w-w_opt)@A@(w-w_opt)),
                  x0=[0.5,0.5], method="SLSQP",
                  constraints={"type":"ineq", "fun": lambda w: t - np.abs(w).sum()})
w_lasso = result.x
ax.plot(*w_lasso, "r*", markersize=15, zorder=5)
ax.plot(*w_opt, "b*", markersize=15, zorder=5, label="OLS solution")

ax.set_xlabel("$w_1$", fontsize=12)
ax.set_ylabel("$w_2$", fontsize=12)
ax.set_title("L1 (Lasso): Diamond constraint", fontsize=13)
ax.set_xlim(-3, 3); ax.set_ylim(-3, 3)
ax.set_aspect("equal")
ax.grid(True, alpha=0.3)
ax.axhline(0, color="gray", linewidth=0.5)
ax.axvline(0, color="gray", linewidth=0.5)
ax.legend(fontsize=9)

# (c) 正則化パスの比較
ax = axes[2]

np.random.seed(42)
n, d = 100, 5
X = np.random.randn(n, d)
w_true = np.array([3, -2, 0, 0, 1.5])
y = X @ w_true + np.random.randn(n) * 0.5

from sklearn.linear_model import Ridge, Lasso

lambdas = np.logspace(-3, 3, 100)

# Ridge解パス
ridge_coefs = []
for lam in lambdas:
    model = Ridge(alpha=lam).fit(X, y)
    ridge_coefs.append(model.coef_)
ridge_coefs = np.array(ridge_coefs)

# Lasso解パス
lasso_coefs = []
for lam in lambdas:
    model = Lasso(alpha=lam, max_iter=10000).fit(X, y)
    lasso_coefs.append(model.coef_)
lasso_coefs = np.array(lasso_coefs)

for j in range(d):
    ax.plot(lambdas, ridge_coefs[:, j], "--", color=f"C{j}", linewidth=1.5, alpha=0.6)
    ax.plot(lambdas, lasso_coefs[:, j], "-", color=f"C{j}", linewidth=2,
            label=f"$w_{j+1}$ (true={w_true[j]})")

ax.axhline(0, color="gray", linewidth=0.5)
ax.set_xscale("log")
ax.set_xlabel("$\\lambda$", fontsize=12)
ax.set_ylabel("Coefficient value", fontsize=12)
ax.set_title("Regularization Path (solid=Lasso, dashed=Ridge)", fontsize=12)
ax.legend(fontsize=8, loc="upper right")
ax.grid(True, alpha=0.3)

plt.tight_layout()
plt.savefig("l1_l2_geometry.png", dpi=150, bbox_inches="tight")
plt.show()

このグラフから、L1とL2正則化の本質的な違いが読み取れます。

  1. 左図(L2/Ridge): 損失関数の等高線(青い楕円)が円形の制約領域(赤)に接する点がRidge解です。円には角がないため、接点は一般に軸上にはなく、どのパラメータも0にはなりません。OLS解(青星)から原点に向かって縮小された位置にRidge解(赤星)があります

  2. 中央図(L1/Lasso): ひし形の制約領域は角(頂点)を持ち、等高線がこの角に接触する確率が高くなります。角は座標軸上にあるため、接点ではいくつかのパラメータが正確に0になります。これがL1正則化のスパース性の幾何学的な理由です

  3. 右図(正則化パス): $\lambda$ の変化に対する各係数の推移を示しています。Ridge(破線)は全ての係数が滑らかに0に近づきますが、正確に0にはなりません。Lasso(実線)は $\lambda$ がある閾値を超えると係数が正確に0になり、真に0である $w_3, w_4$ が最初に0になっています。これがLassoの特徴量選択の効果です

ベイズ推定からの解釈

L2正則化 = ガウス事前分布

パラメータに正規分布の事前分布 $w_j \sim N(0, \tau^2)$ を仮定すると、MAP推定(事後確率最大化)は

$$ \hat{\bm{w}}_{\text{MAP}} = \arg\max_{\bm{w}} \left[\ln p(\bm{y}|\bm{w}) + \ln p(\bm{w})\right] = \arg\min_{\bm{w}} \left[\frac{1}{2\sigma^2}\|\bm{y}-\bm{X}\bm{w}\|^2 + \frac{1}{2\tau^2}\|\bm{w}\|^2\right] $$

$\lambda = \sigma^2/(n\tau^2)$ とおけばRidge回帰と一致します。つまり、L2正則化はパラメータにガウス事前分布を置いたMAP推定に対応します。

L1正則化 = ラプラス事前分布

パラメータにラプラス分布の事前分布 $p(w_j) = \frac{1}{2b}\exp(-|w_j|/b)$ を仮定すると

$$ \hat{\bm{w}}_{\text{MAP}} = \arg\min_{\bm{w}} \left[\frac{1}{2\sigma^2}\|\bm{y}-\bm{X}\bm{w}\|^2 + \frac{1}{b}\|\bm{w}\|_1\right] $$

$\lambda = \sigma^2/(nb)$ とおけばLasso回帰と一致します。L1正則化はラプラス事前分布のMAP推定です。

ラプラス分布はガウス分布よりも0付近にピークが鋭く、裾が重い形状をしています。0付近に集中する事前分布は、多くのパラメータが0であること(スパース性)を反映しており、L1正則化がスパースな解を好む理由をベイズの観点から説明しています。

import numpy as np
import matplotlib.pyplot as plt
from scipy import stats

w = np.linspace(-4, 4, 500)

fig, ax = plt.subplots(figsize=(10, 6))

# ガウス事前分布(L2に対応)
for tau in [0.5, 1.0, 2.0]:
    ax.plot(w, stats.norm.pdf(w, 0, tau), linewidth=2,
            label=f"Gaussian ($\\tau={tau}$) → L2")

# ラプラス事前分布(L1に対応)
for b in [0.5, 1.0, 2.0]:
    ax.plot(w, stats.laplace.pdf(w, 0, b), "--", linewidth=2,
            label=f"Laplace ($b={b}$) → L1")

ax.set_xlabel("$w$", fontsize=13)
ax.set_ylabel("Prior density $p(w)$", fontsize=13)
ax.set_title("Bayesian Interpretation: Gaussian (L2) vs Laplace (L1) Priors", fontsize=14)
ax.legend(fontsize=9, ncol=2)
ax.grid(True, alpha=0.3)
ax.set_xlim(-4, 4)

plt.tight_layout()
plt.savefig("bayesian_priors.png", dpi=150, bbox_inches="tight")
plt.show()

このグラフから、ガウス事前分布とラプラス事前分布の形状の違いが読み取れます。ラプラス分布(破線)はガウス分布(実線)と比べて、0のピークが鋭く裾が重い形をしています。0に鋭いピークがあるため、事前分布はパラメータが0である可能性を強く支持し、MAP推定でパラメータが正確に0になりやすくなります。一方、ガウス分布は0付近で滑らかなので、パラメータは0に近づくものの正確に0にはなりません。

Elastic Net — L1とL2の組み合わせ

動機

Lassoは優れた特徴量選択を行いますが、いくつかの弱点があります。

  1. グループ効果: 相関の高い特徴量群がある場合、Lassoはそのうち1つだけを選びがちで、残りを0にしてしまう
  2. $p > n$ 問題: 特徴量数 $p$ がデータ数 $n$ より多い場合、Lassoは最大 $n$ 個の特徴量しか選択できない
  3. 不安定性: 相関した特徴量の間で選択結果が不安定になりやすい

定義

Elastic Net(Zou & Hastie, 2005)はL1とL2を混合した正則化です。

$$ \begin{equation} J_{\text{EN}}(\bm{w}) = \frac{1}{2n}\|\bm{y} – \bm{X}\bm{w}\|^2 + \lambda\left[\alpha\|\bm{w}\|_1 + \frac{1-\alpha}{2}\|\bm{w}\|_2^2\right] \end{equation} $$

$\alpha \in [0, 1]$ はL1とL2の混合比で、$\alpha = 1$ はLasso、$\alpha = 0$ はRidgeに対応します。

L2項がグループ効果を促進し(相関の高い特徴量を一緒に選択する傾向)、L1項がスパース性を維持します。

import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import ElasticNet, Lasso, Ridge
from sklearn.model_selection import cross_val_score

np.random.seed(42)

# 相関のある特徴量を含むデータ
n = 100
X1 = np.random.randn(n)
X2 = X1 + np.random.randn(n) * 0.1  # X1と高相関
X3 = np.random.randn(n)
X4 = np.random.randn(n)
X5 = np.random.randn(n)
X = np.column_stack([X1, X2, X3, X4, X5])
y = 3 * X1 + 3 * X2 + 0.5 * X3 + np.random.randn(n) * 0.5

fig, axes = plt.subplots(1, 2, figsize=(14, 5.5))

# (a) 正則化パス比較
ax = axes[0]
lambdas = np.logspace(-4, 1, 50)

for method, name, ls in [("lasso", "Lasso", "-"), ("en", "Elastic Net ($\\alpha=0.5$)", "--")]:
    coefs = []
    for lam in lambdas:
        if method == "lasso":
            model = Lasso(alpha=lam, max_iter=10000).fit(X, y)
        else:
            model = ElasticNet(alpha=lam, l1_ratio=0.5, max_iter=10000).fit(X, y)
        coefs.append(model.coef_)
    coefs = np.array(coefs)

    for j in range(5):
        ax.plot(lambdas, coefs[:, j], ls, color=f"C{j}", linewidth=2 if j < 3 else 1)

ax.axhline(0, color="gray", linewidth=0.5)
ax.set_xscale("log")
ax.set_xlabel("$\\lambda$", fontsize=12)
ax.set_ylabel("Coefficient", fontsize=12)
ax.set_title("Regularization Path: Lasso (-) vs Elastic Net (--)", fontsize=12)
ax.grid(True, alpha=0.3)

# 凡例を手動で追加
from matplotlib.lines import Line2D
legend_elements = [Line2D([0],[0], color=f"C{j}", linewidth=2,
                          label=f"$w_{j+1}$") for j in range(5)]
ax.legend(handles=legend_elements, fontsize=9)

# (b) 交差検証スコアの比較
ax = axes[1]
alphas_cv = np.logspace(-3, 1, 30)

for alpha_ratio, name, color in [(1.0, "Lasso", "blue"),
                                   (0.5, "Elastic Net ($\\alpha=0.5$)", "green"),
                                   (0.0, "Ridge", "red")]:
    scores = []
    for a in alphas_cv:
        if alpha_ratio == 1.0:
            model = Lasso(alpha=a, max_iter=10000)
        elif alpha_ratio == 0.0:
            model = Ridge(alpha=a)
        else:
            model = ElasticNet(alpha=a, l1_ratio=alpha_ratio, max_iter=10000)
        cv_score = cross_val_score(model, X, y, cv=5, scoring="neg_mean_squared_error")
        scores.append(-cv_score.mean())

    ax.plot(alphas_cv, scores, color=color, linewidth=2, label=name)

ax.set_xscale("log")
ax.set_xlabel("$\\lambda$", fontsize=12)
ax.set_ylabel("Cross-validation MSE", fontsize=12)
ax.set_title("Cross-Validation Error Comparison", fontsize=13)
ax.legend(fontsize=10)
ax.grid(True, alpha=0.3)

plt.tight_layout()
plt.savefig("elastic_net.png", dpi=150, bbox_inches="tight")
plt.show()

このグラフから、L1、L2、Elastic Netの違いが読み取れます。

  1. 左図(正則化パス): Lasso(実線)は相関の高い $w_1, w_2$ のうち片方を0にする傾向があり、どちらが選ばれるかは不安定です。Elastic Net(破線)は $w_1, w_2$ を同程度に保ちながら縮小し、グループ効果が機能しています

  2. 右図(交差検証誤差): 3つの手法の最適な $\lambda$ での性能を比較すると、Elastic Netが最も低い交差検証誤差を達成しています。特に相関した特徴量がある場合、LassoやRidge単体よりもElastic Netが有効であることがわかります

正則化パラメータ $\lambda$ の選択

正則化パラメータ $\lambda$ の最適値は問題依存であり、通常は交差検証(cross-validation)で選択します。

scikit-learnでは LassoCV, RidgeCV, ElasticNetCV が交差検証付きの正則化を提供しています。

import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import LassoCV, RidgeCV

np.random.seed(42)

# データ生成
n = 200
d = 20
X = np.random.randn(n, d)
w_true = np.zeros(d)
w_true[:5] = [3, -2, 1.5, -1, 0.8]  # 5個だけ非零
y = X @ w_true + np.random.randn(n) * 0.5

# LassoCVによる自動選択
lasso_cv = LassoCV(cv=5, alphas=np.logspace(-4, 1, 100)).fit(X, y)

fig, axes = plt.subplots(1, 2, figsize=(14, 5.5))

# (a) 交差検証スコアと最適lambda
ax = axes[0]
mean_mse = np.mean(lasso_cv.mse_path_, axis=1)
std_mse = np.std(lasso_cv.mse_path_, axis=1)

ax.semilogx(lasso_cv.alphas_, mean_mse, "b-", linewidth=2)
ax.fill_between(lasso_cv.alphas_, mean_mse - std_mse, mean_mse + std_mse,
                color="blue", alpha=0.1)
ax.axvline(lasso_cv.alpha_, color="red", linestyle="--", linewidth=2,
           label=f"Optimal $\\lambda$ = {lasso_cv.alpha_:.4f}")
ax.set_xlabel("$\\lambda$", fontsize=12)
ax.set_ylabel("Cross-validation MSE", fontsize=12)
ax.set_title("LassoCV: Automatic $\\lambda$ Selection", fontsize=13)
ax.legend(fontsize=11)
ax.grid(True, alpha=0.3)

# (b) 推定されたパラメータ vs 真のパラメータ
ax = axes[1]
x_pos = np.arange(d)
width = 0.35

ax.bar(x_pos - width/2, w_true, width, color="steelblue", alpha=0.8, label="True")
ax.bar(x_pos + width/2, lasso_cv.coef_, width, color="coral", alpha=0.8, label="Lasso estimate")

ax.set_xlabel("Feature index", fontsize=12)
ax.set_ylabel("Coefficient value", fontsize=12)
ax.set_title("True vs Estimated Coefficients", fontsize=13)
ax.legend(fontsize=11)
ax.grid(True, alpha=0.3, axis="y")
ax.set_xticks(x_pos)

plt.tight_layout()
plt.savefig("lasso_cv.png", dpi=150, bbox_inches="tight")
plt.show()

# 結果の確認
print(f"Optimal lambda: {lasso_cv.alpha_:.4f}")
print(f"Number of non-zero coefficients: {np.sum(lasso_cv.coef_ != 0)}")
print(f"True non-zero: {np.sum(w_true != 0)}")

このグラフから、LassoCVの自動パラメータ選択の効果が確認できます。

  1. 左図(交差検証曲線): $\lambda$ に対する交差検証MSEはU字型のカーブを描きます。$\lambda$ が小さすぎると過学習で誤差が高く、$\lambda$ が大きすぎると未学習で誤差が高くなります。赤い破線が交差検証で選ばれた最適な $\lambda$ を示しています

  2. 右図(係数の比較): 真のパラメータ(青)のうち非零である最初の5つが、Lassoの推定値(赤)で正確に回復されています。残りの15個の真に0であるパラメータも、Lassoにより正確に0と推定されています。これがLassoの特徴量選択能力の実証です

まとめ

本記事では、L1正則化(Lasso)とL2正則化(Ridge)の理論を幾何学的・ベイズ的観点から解説しました。

  • L2正則化(Ridge) はパラメータを原点に向かって縮小する。解析解が存在し、多重共線性を安定化する効果がある
  • L1正則化(Lasso) はパラメータを正確に0にするスパース性を持ち、特徴量選択として機能する
  • 幾何学的には、L2の円形制約領域は角がないため接点は軸上にならず、L1のひし形制約領域は角が軸上にあるため接点で0が発生する
  • ベイズ的には、L2はガウス事前分布、L1はラプラス事前分布のMAP推定に対応する
  • Elastic NetはL1とL2を組み合わせ、スパース性とグループ効果の両方を実現する
  • 正則化パラメータ $\lambda$ は交差検証で選択する

次のステップとして、以下の記事も参考にしてください。