リッジ回帰とLASSOの理論 — 正則化の数学

重回帰分析では、説明変数の数 $p$ が増えるほどモデルの表現力は高まりますが、同時に過学習のリスクも増大します。特に $p$ がサンプル数 $n$ に近い、あるいは超える場合(高次元設定)、最小二乗推定量は不安定になり、予測精度が大きく劣化します。また、説明変数間に強い相関(多重共線性)がある場合、回帰係数の推定値が極端に大きくなり、解釈が困難になります。

たとえば、遺伝子発現データの解析を考えてみましょう。数万個の遺伝子の発現量(説明変数)から疾患のリスク(目的変数)を予測したいとき、サンプル数は数百程度しかないことが一般的です。この状況で通常の最小二乗法を適用すると、方程式の数よりも未知数の方が多く、解が一意に定まりません。仮に解が得られたとしても、学習データに完全にフィットする一方で、新しいデータへの予測精度は壊滅的に低くなります。

これらの問題に対する処方箋が正則化(regularization)です。正則化は、残差二乗和に回帰係数の大きさに対するペナルティ項を加えることで、係数を縮小(shrinkage)させ、過学習を防ぎます。

代表的な正則化手法がリッジ回帰(Ridge Regression)LASSO(Least Absolute Shrinkage and Selection Operator)です。

正則化回帰を理解すると、以下のような応用が開けます。

  • ゲノミクス: 遺伝子の数(数万)がサンプル数(数百)を大きく超える状況での予測
  • 画像処理: 高次元の特徴量からの予測、画像復元
  • 金融工学: 多数のファクターモデルにおける係数推定の安定化
  • 自然言語処理: 大規模な特徴空間でのテキスト分類
  • 推薦システム: ユーザー-アイテム行列の低ランク近似

本記事では、リッジ回帰とLASSOの理論を最適化問題として定式化し、それぞれの解の性質を比較します。バイアス-バリアンストレードオフの観点から正則化の効果を理解し、幾何学的な解釈を通じてL1とL2正則化の違いを明らかにします。Pythonで実装して理論を確認します。

本記事の内容

  • 過学習と正則化の必要性
  • リッジ回帰(L2正則化)の定式化と閉形式解
  • 特異値分解による解釈
  • LASSO(L1正則化)の定式化とスパース性
  • 幾何学的解釈 — なぜL1がスパースを生むか
  • ソフトしきい値演算子の導出
  • バイアス-バリアンストレードオフ
  • 正則化パスの可視化
  • Pythonでの実装

前提知識

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

過学習と正則化の必要性

バイアス-バリアンスのジレンマ

重回帰分析の最小二乗推定量は不偏(バイアスがゼロ)ですが、必ずしも予測誤差が最小とは限りません。予測誤差は次のように分解されます。

$$ \begin{equation} \text{予測誤差} = \text{バイアス}^2 + \text{バリアンス} + \text{ノイズ} \end{equation} $$

各項の意味は次の通りです。

  • バイアス: モデルの期待予測値と真の値のずれ。モデルの「正確さ」
  • バリアンス: データの変動に対するモデルの予測値の変動。モデルの「安定性」
  • ノイズ: データに含まれる本質的なランダム性。どんなモデルでも除去できない

最小二乗法はバイアスをゼロにしますが、特に $p$ が大きい場合にバリアンスが大きくなります。少しのバイアスを許容してバリアンスを大幅に減らすことで、総合的な予測誤差を改善できるかもしれません。これが正則化の基本的なアイデアです。

正則化の直感

正則化は、「回帰係数が大きくなりすぎないように制限する」ことでバリアンスを抑えます。なぜ大きな回帰係数が問題なのでしょうか。

回帰係数が大きいということは、説明変数の小さな変化がモデルの出力に大きな影響を与えることを意味します。これは「データの小さなノイズに敏感に反応する」ことと同義であり、過学習の典型的な兆候です。

たとえば、2つの説明変数 $x_1$ と $x_2$ が強く相関している場合($r \approx 0.99$)、$\hat{\beta}_1 = 1000$, $\hat{\beta}_2 = -998$ のような極端な値が推定されることがあります。$\hat{\beta}_1 x_1 + \hat{\beta}_2 x_2 \approx 2$ という結果は妥当でも、個々の係数は不安定で解釈困難です。正則化は、このような不安定さを制御します。

リッジ回帰から見ていきましょう。

リッジ回帰(L2正則化)

定式化

リッジ回帰は、最小二乗法の目的関数に回帰係数のL2ノルムの2乗をペナルティとして加えます。

$$ \begin{equation} \hat{\bm{\beta}}^{\text{ridge}} = \arg\min_{\bm{\beta}}\left\{\|\bm{y} – \bm{X}\bm{\beta}\|^2 + \lambda\|\bm{\beta}\|^2\right\} \end{equation} $$

ここで $\lambda \geq 0$ は正則化パラメータ(tuning parameter)です。$\lambda$ は2つの項のバランスを制御します。

  • $\lambda = 0$: ペナルティなし。通常の最小二乗法に一致
  • $\lambda \to \infty$: ペナルティが支配的。$\hat{\bm{\beta}} \to \bm{0}$ に縮小
  • 適度な $\lambda$: 残差を小さく保ちつつ、係数の大きさも抑える

展開すると次のようになります。

$$ \hat{\bm{\beta}}^{\text{ridge}} = \arg\min_{\bm{\beta}}\left\{(\bm{y} – \bm{X}\bm{\beta})^\top(\bm{y} – \bm{X}\bm{\beta}) + \lambda\bm{\beta}^\top\bm{\beta}\right\} $$

閉形式解の導出

$\bm{\beta}$ で微分してゼロとおきます。

$$ \frac{\partial}{\partial\bm{\beta}}\left[(\bm{y} – \bm{X}\bm{\beta})^\top(\bm{y} – \bm{X}\bm{\beta}) + \lambda\bm{\beta}^\top\bm{\beta}\right] = -2\bm{X}^\top\bm{y} + 2\bm{X}^\top\bm{X}\bm{\beta} + 2\lambda\bm{\beta} = \bm{0} $$

整理すると次の式が得られます。

$$ (\bm{X}^\top\bm{X} + \lambda\bm{I})\bm{\beta} = \bm{X}^\top\bm{y} $$

$\bm{X}^\top\bm{X} + \lambda\bm{I}$ は $\lambda > 0$ のとき常に正定値です。なぜなら、$\bm{X}^\top\bm{X}$ は半正定値で、$\lambda\bm{I}$ を加えるとすべての固有値が $\lambda$ 以上になるからです。したがって逆行列が存在し、リッジ推定量は次のように求まります。

$$ \begin{equation} \hat{\bm{\beta}}^{\text{ridge}} = (\bm{X}^\top\bm{X} + \lambda\bm{I})^{-1}\bm{X}^\top\bm{y} \end{equation} $$

最小二乗推定量 $\hat{\bm{\beta}}^{\text{OLS}} = (\bm{X}^\top\bm{X})^{-1}\bm{X}^\top\bm{y}$ と比較すると、$\lambda\bm{I}$ が加わっただけです。この $\lambda\bm{I}$ が $\bm{X}^\top\bm{X}$ の対角成分を「嵩上げ」し、逆行列を安定化させます。$\bm{X}^\top\bm{X}$ が(ほぼ)特異行列の場合でも、$\lambda > 0$ であれば必ず逆行列が存在します。

特異値分解による解釈

$\bm{X}$ の特異値分解 $\bm{X} = \bm{U}\bm{D}\bm{V}^\top$ を使うと、リッジ推定量の構造がより明確になります。ここで $\bm{U}$ は $n \times p$ の直交行列、$\bm{D} = \text{diag}(d_1, \dots, d_p)$ は特異値の対角行列、$\bm{V}$ は $p \times p$ の直交行列です。

$\bm{X}^\top\bm{X} = \bm{V}\bm{D}^2\bm{V}^\top$ を使うと、リッジ推定量は次のように書けます。

$$ \hat{\bm{\beta}}^{\text{ridge}} = \bm{V}(\bm{D}^2 + \lambda\bm{I})^{-1}\bm{D}\bm{U}^\top\bm{y} $$

OLS推定量を $\bm{V}$ の基底で表すと $\hat{\bm{\beta}}^{\text{OLS}} = \sum_{j=1}^{p}\frac{\bm{u}_j^\top\bm{y}}{d_j}\bm{v}_j$ となり、リッジ推定量は次のようになります。

$$ \begin{equation} \hat{\bm{\beta}}^{\text{ridge}} = \sum_{j=1}^{p}\frac{d_j^2}{d_j^2 + \lambda}\cdot\frac{\bm{u}_j^\top\bm{y}}{d_j}\bm{v}_j \end{equation} $$

因子 $\frac{d_j^2}{d_j^2 + \lambda}$ は縮小因子(shrinkage factor)です。この因子は常に0から1の間の値を取り、以下の性質を持ちます。

  • $d_j$ が大きい(データの情報が多い)方向: 縮小因子は1に近く、ほとんど縮小されない
  • $d_j$ が小さい(データの情報が少ない)方向: 縮小因子は0に近く、強く縮小される

つまり、リッジ回帰は最小二乗解の「不安定な成分」(特異値が小さい方向)を選択的に縮小する操作です。データから十分な情報が得られている方向はほぼ保持し、情報が少ない方向は積極的に抑えます。

バイアスとバリアンスの数学的な分析

リッジ推定量のバイアスとバリアンスを計算しましょう。

バイアス:

$$ \text{Bias}(\hat{\bm{\beta}}^{\text{ridge}}) = E[\hat{\bm{\beta}}^{\text{ridge}}] – \bm{\beta} = -\lambda(\bm{X}^\top\bm{X} + \lambda\bm{I})^{-1}\bm{\beta} $$

$\lambda > 0$ のときバイアスはゼロではありません。$\lambda$ が大きいほどバイアスは大きくなります。

バリアンス:

$$ \text{Cov}(\hat{\bm{\beta}}^{\text{ridge}}) = \sigma^2(\bm{X}^\top\bm{X} + \lambda\bm{I})^{-1}\bm{X}^\top\bm{X}(\bm{X}^\top\bm{X} + \lambda\bm{I})^{-1} $$

$\lambda$ が大きいほどバリアンスは小さくなります。$\lambda$ を大きくするとバイアスは増えますがバリアンスは減り、予測誤差を最小化する最適な $\lambda$ が存在します。

LASSOとの対比を見ていきましょう。

LASSO(L1正則化)

定式化

LASSOは、ペナルティ項にL2ノルムの代わりにL1ノルムを使います。

$$ \begin{equation} \hat{\bm{\beta}}^{\text{lasso}} = \arg\min_{\bm{\beta}}\left\{\frac{1}{2n}\|\bm{y} – \bm{X}\bm{\beta}\|^2 + \lambda\|\bm{\beta}\|_1\right\} \end{equation} $$

ここで $\|\bm{\beta}\|_1 = \sum_{j=1}^{p}|\beta_j|$ はL1ノルムです。ペナルティが各係数の絶対値の和であるという、一見小さな変更が、根本的に異なる性質をもたらします。

スパース性 — LASSOの最大の特徴

LASSOの最も重要な性質は、$\lambda$ が十分に大きいとき、一部の回帰係数が厳密にゼロになることです。つまり、LASSOは正則化と変数選択を同時に行います。

リッジ回帰ではすべての係数が非ゼロのまま縮小されるのに対し、LASSOは不要な変数の係数を完全にゼロにします。10,000個の遺伝子のうち実際に疾患に関係するのが10個だけだとすれば、LASSOはその10個を自動的に選び出す能力を持っています。

なぜL1ノルムがスパース解を生むのでしょうか。幾何学的に理解しましょう。

幾何学的解釈

制約付き最適化の観点では、LASSOは次の問題と等価です。

$$ \min_{\bm{\beta}} \|\bm{y} – \bm{X}\bm{\beta}\|^2 \quad \text{subject to} \quad \|\bm{\beta}\|_1 \leq t $$

同様に、リッジ回帰は次と等価です。

$$ \min_{\bm{\beta}} \|\bm{y} – \bm{X}\bm{\beta}\|^2 \quad \text{subject to} \quad \|\bm{\beta}\|_2^2 \leq t $$

2次元($p = 2$)の場合で考えます。

L1制約領域: $|\beta_1| + |\beta_2| \leq t$ は「ダイヤモンド形」(正方形を45度回転した形)になります。頂点は座標軸上にあり、$(\pm t, 0)$ と $(0, \pm t)$ の4つです。

L2制約領域: $\beta_1^2 + \beta_2^2 \leq t$ は「円形」になります。角がありません。

残差二乗和の等高線は楕円形です。この楕円を制約領域に向かって縮小していくと、楕円が制約領域に最初に接する点が解です。

  • L1(ダイヤモンド): 楕円が最初に接するのは、高い確率でダイヤモンドの(頂点)です。角では $\beta_1 = 0$ または $\beta_2 = 0$ であるため、スパースな解が得られます
  • L2(円): 円には角がないため、楕円が接する点で座標がちょうどゼロになることはほとんどありません

この幾何学的な議論は $p$ 次元に一般化されます。L1制約領域は超八面体(cross-polytope)であり、$p$ 個の座標軸上に角を持ちます。高次元ほど角の数に対する曲面の面積の比が大きくなり、スパースな解が得られやすくなります。

ソフトしきい値演算子の導出

直交設計($\bm{X}^\top\bm{X} = n\bm{I}$)の場合、LASSOの解は閉形式で書けます。この場合を詳しく導出しましょう。

LASSOの目的関数を $\beta_j$ に関してのみ書くと(他の $\beta_k$ は固定)次のようになります。

$$ \frac{1}{2n}(z_j – \beta_j)^2 + \lambda|\beta_j| $$

ここで $z_j = \hat{\beta}_j^{\text{OLS}}$ は最小二乗推定量です。

$\beta_j > 0$ の場合、微分してゼロとおくと $-\frac{1}{n}(z_j – \beta_j) + \lambda = 0$、つまり $\beta_j = z_j – n\lambda$ です。これは $z_j > n\lambda$ のときのみ正です。

$\beta_j < 0$ の場合、同様に $\beta_j = z_j + n\lambda$ で、$z_j < -n\lambda$ のときのみ負です。

$|z_j| \leq n\lambda$ の場合、$\beta_j = 0$ が最適です。

まとめると、LASSOの解はソフトしきい値演算子(soft thresholding operator)$S_\lambda$ で書けます。

$$ \begin{equation} \hat{\beta}_j^{\text{lasso}} = S_\lambda(\hat{\beta}_j^{\text{OLS}}) = \text{sign}(\hat{\beta}_j^{\text{OLS}})\max(|\hat{\beta}_j^{\text{OLS}}| – \lambda, 0) \end{equation} $$

OLS推定値の絶対値が $\lambda$ より小さければゼロに、大きければ $\lambda$ だけ原点方向に縮小されます。この「しきい値を超えた部分だけを保持し、原点方向に縮小する」操作がスパース性の源です。

比較として、リッジ回帰の直交設計での解は次のようになります。

$$ \hat{\beta}_j^{\text{ridge}} = \frac{1}{1 + \lambda}\hat{\beta}_j^{\text{OLS}} $$

リッジは全ての係数を一様に縮小する(乗法的縮小)のに対し、LASSOは閾値以下を切り捨てる(加法的縮小)という違いがあります。

一般の場合は閉形式解がなく、座標降下法(coordinate descent)などの反復アルゴリズムで解きます。座標降下法は、一度に1つの $\beta_j$ だけを最適化し、全変数を巡回するアルゴリズムで、ソフトしきい値演算子を反復的に適用することでLASSOの解を求めます。

Elastic Net — リッジとLASSOの融合

リッジとLASSOのペナルティを組み合わせたElastic Netも広く使われています。

$$ \hat{\bm{\beta}}^{\text{EN}} = \arg\min_{\bm{\beta}}\left\{\|\bm{y} – \bm{X}\bm{\beta}\|^2 + \lambda_1\|\bm{\beta}\|_1 + \lambda_2\|\bm{\beta}\|^2\right\} $$

Elastic NetはLASSOのスパース性とリッジの安定性を兼ね備えています。相関の高い変数群がある場合、LASSOはその中から1つだけを選ぶ傾向がありますが、Elastic Netはグループ全体を選択する性質を持ちます。

Pythonで実装して比較しましょう。

Pythonによる実装と可視化

正則化パスの比較

リッジとLASSOの正則化パスを比較し、係数がどのように縮小されていくかを可視化します。

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

np.random.seed(42)

# --- データ生成 ---
n, p = 100, 10
X = np.random.randn(n, p)
# 真の係数: 最初の3つだけ非ゼロ
beta_true = np.zeros(p)
beta_true[:3] = [3.0, -2.0, 1.5]
y = X @ beta_true + 0.5 * np.random.randn(n)

# --- 正則化パス ---
alphas = np.logspace(-3, 3, 100)

ridge_coefs = []
lasso_coefs = []

for alpha in alphas:
    ridge = Ridge(alpha=alpha, fit_intercept=False)
    ridge.fit(X, y)
    ridge_coefs.append(ridge.coef_.copy())

    lasso = Lasso(alpha=alpha, fit_intercept=False, max_iter=10000)
    lasso.fit(X, y)
    lasso_coefs.append(lasso.coef_.copy())

ridge_coefs = np.array(ridge_coefs)
lasso_coefs = np.array(lasso_coefs)

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

# (a) リッジ回帰の正則化パス
ax = axes[0]
for j in range(p):
    style = "-" if beta_true[j] != 0 else "--"
    ax.plot(np.log10(alphas), ridge_coefs[:, j], style,
            linewidth=1.5 if beta_true[j] != 0 else 0.8,
            label=f"$\\beta_{j+1}$ (true={beta_true[j]:.1f})" if beta_true[j] != 0 else None)
ax.axhline(0, color="k", linewidth=0.5)
ax.set_xlabel("$\\log_{10}(\\lambda)$", fontsize=11)
ax.set_ylabel("Coefficient value", fontsize=11)
ax.set_title("(a) Ridge regression path", fontsize=12)
ax.legend(fontsize=9)
ax.grid(True, alpha=0.3)

# (b) LASSOの正則化パス
ax = axes[1]
for j in range(p):
    style = "-" if beta_true[j] != 0 else "--"
    ax.plot(np.log10(alphas), lasso_coefs[:, j], style,
            linewidth=1.5 if beta_true[j] != 0 else 0.8,
            label=f"$\\beta_{j+1}$ (true={beta_true[j]:.1f})" if beta_true[j] != 0 else None)
ax.axhline(0, color="k", linewidth=0.5)
ax.set_xlabel("$\\log_{10}(\\lambda)$", fontsize=11)
ax.set_ylabel("Coefficient value", fontsize=11)
ax.set_title("(b) LASSO regression path", fontsize=12)
ax.legend(fontsize=9)
ax.grid(True, alpha=0.3)

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

正則化パスの比較から、リッジとLASSOの本質的な違いが読み取れます。

  1. リッジ(図a): $\lambda$ が大きくなるにつれて、すべての係数が連続的にゼロに向かって縮小していきます。しかし、どんなに $\lambda$ を大きくしても係数は厳密にはゼロになりません。不要な変数の係数も小さいながら残り続けます。これはL2ペナルティの「滑らかさ」に起因します

  2. LASSO(図b): $\lambda$ を増やすと、一部の係数が途中で厳密にゼロになることが明確に見えます。真にゼロの係数(破線)は比較的小さな $\lambda$ でゼロに達し、非ゼロの係数(実線)は大きな $\lambda$ まで残ります。これがLASSOの変数選択能力です

  3. スパース性の違い: LASSOは不要な変数を自動的に除外するため、解釈しやすいモデルが得られます。リッジはすべての変数を保持するため、多くの変数が弱い影響を持つ状況(dense signal)に適しています

バイアス-バリアンストレードオフの可視化

正則化パラメータ $\lambda$ を変化させたときの予測誤差の分解を、モンテカルロシミュレーションで可視化します。

import numpy as np
import matplotlib.pyplot as plt

np.random.seed(42)
n, p = 50, 20
n_test = 1000
n_sim = 200

# 真のパラメータ
beta_true = np.zeros(p)
beta_true[:5] = [2.0, -1.5, 1.0, -0.5, 0.3]
sigma = 1.0

# テストデータ(固定)
X_test = np.random.randn(n_test, p)
y_test_true = X_test @ beta_true

lambdas = np.logspace(-2, 3, 30)
bias2_list = []
variance_list = []
mse_list = []

for lam in lambdas:
    preds = []
    for _ in range(n_sim):
        X_train = np.random.randn(n, p)
        y_train = X_train @ beta_true + sigma * np.random.randn(n)
        # リッジ回帰
        beta_hat = np.linalg.solve(X_train.T @ X_train + lam * np.eye(p),
                                    X_train.T @ y_train)
        y_pred = X_test @ beta_hat
        preds.append(y_pred)

    preds = np.array(preds)  # (n_sim, n_test)
    mean_pred = preds.mean(axis=0)
    bias2 = np.mean((mean_pred - y_test_true)**2)
    variance = np.mean(preds.var(axis=0))
    mse = np.mean((preds - y_test_true[np.newaxis, :])**2)

    bias2_list.append(bias2)
    variance_list.append(variance)
    mse_list.append(mse)

fig, ax = plt.subplots(figsize=(8, 5.5))
ax.plot(np.log10(lambdas), bias2_list, "b-", linewidth=2, label="Bias$^2$")
ax.plot(np.log10(lambdas), variance_list, "r-", linewidth=2, label="Variance")
ax.plot(np.log10(lambdas), mse_list, "k--", linewidth=2, label="MSE")
ax.axhline(sigma**2, color="gray", linestyle=":", linewidth=1, label="Noise ($\\sigma^2$)")
opt_idx = np.argmin(mse_list)
ax.axvline(np.log10(lambdas[opt_idx]), color="green", linestyle="--", linewidth=1,
           label=f"Optimal $\\lambda$ = {lambdas[opt_idx]:.2f}")
ax.set_xlabel("$\\log_{10}(\\lambda)$", fontsize=11)
ax.set_ylabel("Error", fontsize=11)
ax.set_title("Bias-variance tradeoff in Ridge regression", fontsize=12)
ax.legend(fontsize=9)
ax.grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig("bias_variance_ridge.png", dpi=150, bbox_inches="tight")
plt.show()

このグラフは、正則化の本質であるバイアス-バリアンストレードオフを明確に示しています。

  1. $\lambda$ が小さい領域: バイアスはほぼゼロですが、バリアンスが大きくなっています。$p = 20, n = 50$ という設定では、正則化なし($\lambda \to 0$)のOLS推定量は不安定であり、データの変動に敏感に反応します

  2. $\lambda$ が大きい領域: バリアンスは非常に小さくなりますが、バイアスが急激に増大しています。$\lambda$ が大きすぎると、すべての係数がゼロに近づき、モデルは何も学習していない状態になります

  3. 最適な $\lambda$: MSE(バイアス$^2$ + バリアンス)が最小となる $\lambda$ が存在します(緑の破線)。この最適点では、バイアスの増加とバリアンスの減少がちょうどバランスしています

  4. ノイズフロア: 灰色の点線は避けられないノイズ $\sigma^2$ を示しています。どんなに $\lambda$ を最適化しても、MSEはこの値以下にはなりません

幾何学的解釈の可視化

L1とL2の制約領域と残差二乗和の等高線を2次元で可視化し、スパース性が生まれるメカニズムを図示します。

import numpy as np
import matplotlib.pyplot as plt

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

# 共通設定
beta1 = np.linspace(-3, 3, 300)
beta2 = np.linspace(-3, 3, 300)
B1, B2 = np.meshgrid(beta1, beta2)

# OLS解の位置
beta_ols = np.array([2.0, 1.5])

# 残差二乗和の等高線(楕円)
A = np.array([[2.0, 0.5], [0.5, 1.0]])
RSS = np.zeros_like(B1)
for i in range(len(beta1)):
    for j in range(len(beta2)):
        d = np.array([B1[i,j] - beta_ols[0], B2[i,j] - beta_ols[1]])
        RSS[i,j] = d @ A @ d

# (a) L1制約(LASSO)
ax = axes[0]
ax.contour(B1, B2, RSS, levels=15, colors="steelblue", alpha=0.5, linewidths=0.8)
# ダイヤモンド
t = 1.5
diamond_x = [t, 0, -t, 0, t]
diamond_y = [0, t, 0, -t, 0]
ax.fill(diamond_x, diamond_y, color="coral", alpha=0.2)
ax.plot(diamond_x, diamond_y, "r-", linewidth=2)
ax.plot(beta_ols[0], beta_ols[1], "k*", markersize=12, label="OLS solution")
ax.plot(t, 0, "ro", markersize=10, label="LASSO solution")
ax.set_xlabel("$\\beta_1$", fontsize=12)
ax.set_ylabel("$\\beta_2$", fontsize=12)
ax.set_title("(a) LASSO: L1 constraint", fontsize=12)
ax.legend(fontsize=9)
ax.set_aspect("equal")
ax.grid(True, alpha=0.3)
ax.axhline(0, color="k", linewidth=0.3)
ax.axvline(0, color="k", linewidth=0.3)

# (b) L2制約(Ridge)
ax = axes[1]
ax.contour(B1, B2, RSS, levels=15, colors="steelblue", alpha=0.5, linewidths=0.8)
theta = np.linspace(0, 2*np.pi, 100)
t2 = 1.5
ax.fill(t2*np.cos(theta), t2*np.sin(theta), color="lightgreen", alpha=0.2)
ax.plot(t2*np.cos(theta), t2*np.sin(theta), "g-", linewidth=2)
ax.plot(beta_ols[0], beta_ols[1], "k*", markersize=12, label="OLS solution")
# リッジ解の近似位置
ridge_sol = beta_ols * t2 / np.linalg.norm(beta_ols) * 0.85
ax.plot(ridge_sol[0], ridge_sol[1], "go", markersize=10, label="Ridge solution")
ax.set_xlabel("$\\beta_1$", fontsize=12)
ax.set_ylabel("$\\beta_2$", fontsize=12)
ax.set_title("(b) Ridge: L2 constraint", fontsize=12)
ax.legend(fontsize=9)
ax.set_aspect("equal")
ax.grid(True, alpha=0.3)
ax.axhline(0, color="k", linewidth=0.3)
ax.axvline(0, color="k", linewidth=0.3)

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

この幾何学的可視化から、L1とL2正則化の根本的な違いが明確に理解できます。

  1. LASSO(図a): 残差二乗和の等高線(青い楕円)がダイヤモンド型の制約領域(赤)と接する点が解です。この例では、等高線がダイヤモンドの $\beta_1$ 軸上の頂点で接しており、$\beta_2 = 0$ となるスパース解が得られています。楕円の傾きや制約の大きさを変えると、接点が異なる頂点に移動しますが、多くの場合で角に接するためスパースな解になります

  2. リッジ(図b): 等高線が円形の制約領域(緑)と接する点が解です。円には角がないため、接点で $\beta_1 = 0$ や $\beta_2 = 0$ になることはほぼありません。リッジ解は両方の成分が非ゼロのまま縮小されます

まとめ

本記事では、リッジ回帰とLASSOの理論を解説しました。

  • リッジ回帰: L2ペナルティ $\lambda\|\bm{\beta}\|^2$ を加え、閉形式解 $(\bm{X}^\top\bm{X}+\lambda\bm{I})^{-1}\bm{X}^\top\bm{y}$ を持つ。全係数を縮小するが変数選択は行わない。特異値分解で見ると、情報の少ない方向を選択的に縮小する操作
  • LASSO: L1ペナルティ $\lambda\|\bm{\beta}\|_1$ を加え、一部の係数を厳密にゼロにする。正則化と変数選択を同時に行う。閉形式解はなく、座標降下法で解く
  • 幾何学的解釈: L1制約領域のダイヤモンド形の角がスパース解を生み、L2制約領域の円形は角を持たないため連続的な縮小を行う
  • バイアス-バリアンス: 正則化はバイアスを増やしてバリアンスを減らし、最適な $\lambda$ で予測誤差が最小化される
  • ソフトしきい値: 直交設計ではLASSOの解がソフトしきい値演算子で表され、OLS推定量の絶対値が $\lambda$ 以下なら厳密にゼロ、超えた分だけ縮小される

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