論文一覧に戻る 📚 用語集トップ 🗺 概念マップ
📚 用語解説(ジャストインタイム型データサイエンス教育)
正則化
Regularization
「複雑すぎ」にペナルティを与えて過学習を抑える — モデル簡素化の中心装置
過学習対策回帰・分類両用ハイパラ感度高実務必須

🔖 キーワード索引

regularization」は統計データ分析の文脈で扱う重要概念のひとつ。 本ページでは「regularization」を取り巻く中核キーワードを以下にチップで一覧化する。 各キーワードは関連する概念・手法・道具立てを含み、 文献検索や学習計画の起点になる。

regularization統計分析SSDSE-B-2026前提条件適用範囲落とし穴関連手法Python 実装検証方法

これらのキーワードは「regularization の理解 → 適用 → 検証」のプロセスを構成する。 各章で詳しく解説する。

💡 30秒で分かる結論

🍰 まずはやさしく

正則化は、学習のやりすぎを防ぐブレーキです。

予測のズレを小さくするために使います。

スマホのアプリが、特定のデータだけに反応するのを防ぎます。

ここでは正則化の種類と選び方を学びます。

📍 あなたが今見ているもの

🍰 まずはやさしく

正則化は、モデルをシンプルにする仕組みです。

データに合わせすぎること(過学習)を防ぎます。

少ないデータで分析をするときに役立ちます。

ここでは正則化の全体像について解説します。

本ページでは、 正則化を統合的に解説します。 Ridge(L2)・Lasso(L1)・Elastic NetDropoutEarly Stoppingデータ拡張などを一気通貫で理解できます。

正則化の本質は「複雑なモデルにペナルティを課して、 訓練データに過度に適合しないようにする」こと。 SSDSE-B のようなサンプル数 47 と少ないデータでは、 正則化なしでは過学習がほぼ不可避です。

🎨 直感で掴む — 正則化のたとえ話

🍰 まずはやさしく

正則化は、丸暗記を禁止する塾の先生のようなものです。

本質的なルールを学ばせるために使います。

テストの過去問だけを覚えるのではなく、応用力をつけます。

正則化がどうやって丸暗記を防ぐのかを考えます。

テスト前夜、 過去問の答えを「丸暗記」した学生は本番で初見問題に弱い。 これが過学習。 正則化は「丸暗記禁止=係数を大きくしすぎない罰則」を課して、 浅く広く本質をつかむ学習を強制する塾講師のような役割。 SSDSE-B-2026 で 47 都道府県だけのデータから 30 変数の回帰を立てると、 まさに「47 問の過去問を 30 個のヒントで丸暗記」状態となり過学習する。 Ridge は「全項目に薄く罰金」、 Lasso は「不要科目をきっぱり切り捨て」、 Elastic Net は両者の折衷案。

📐 2. L1ノルムとL2ノルム

🍰 まずはやさしく

ノルムとは、ベクトルの長さを測る定規のようなものです。

正則化の計算方法を決めるために使います。

買い物で、合計金額を出すときのような計算です。

L1ノルムとL2ノルムの違いについて詳しく読みます。

係数ベクトル $\boldsymbol{\beta} = (\beta_1, \dots, \beta_p)^\top$ に対し:

$$\|\boldsymbol{\beta}\|_1 = \sum_{j=1}^{p} |\beta_j|, \qquad \|\boldsymbol{\beta}\|_2^2 = \sum_{j=1}^{p} \beta_j^2$$

記号読み:$\|\cdot\|_1$ は「L1ノルム」、 $\|\cdot\|_2^2$ は「L2ノルムの二乗」。 $\boldsymbol{\beta}$ は「ベータ・太字」と読む。

幾何的な違い — なぜLassoは「ピン留め」するのか

L1 ノルムの等高線は菱形(角がある)、 L2 は。 損失関数の等高線と制約領域の交点が解となるが、 菱形では「角の点」で交わりやすく、 角の点では一部の座標が 0 になる = 自動特徴選択。

🟢 3. Ridge回帰(L2正則化)

$$\hat{\boldsymbol{\beta}}_{\text{ridge}} = \arg\min_{\boldsymbol{\beta}} \left\{\sum_{i=1}^{n} (y_i - \mathbf{x}_i^\top \boldsymbol{\beta})^2 + \lambda \|\boldsymbol{\beta}\|_2^2 \right\}$$

記号読み:$\hat{\boldsymbol{\beta}}_{\text{ridge}}$ は「ベータ・ハット・リッジ」、 Ridge推定量。 $\lambda$ は「ラムダ」、 正則化の強さ(大きいほど係数を 0 に押し寄せる)。

解析解

$$\hat{\boldsymbol{\beta}}_{\text{ridge}} = (\mathbf{X}^\top \mathbf{X} + \lambda \mathbf{I})^{-1} \mathbf{X}^\top \mathbf{y}$$

$\mathbf{X}^\top \mathbf{X}$ が特異でも $\lambda > 0$ なら必ず正則。 多重共線性に対する強い武器。

3.1 実値で計算

2 サンプル・2 特徴量の極簡単な例:$\mathbf{X}=\begin{pmatrix}1&2\\3&4\end{pmatrix},\ \mathbf{y}=\begin{pmatrix}5\\6\end{pmatrix},\ \lambda=1$ のとき:

🎯 解説: SSDSE-B-2026(2023 年度)の社会経済 5 指標から「L3221 消費支出(二人以上の世帯)」を予測する Ridge 回帰(L2 正則化)パイプライン。 StandardScaler で各説明変数を平均 0・分散 1 に揃えてから Ridge(alpha=1.0) を適用。 alpha=λ が大きいほど係数を 0 方向に縮小(shrinkage)し、 多重共線性を持つ「総人口 A1101」と「高齢人口 A1303」のような相関の高い変数の係数を安定化させる。
📥 入力例: SSDSE-B-2026.csv(2023 年度・47 都道府県, 実在コード) 説明変数 X(5 列): A1101 総人口 [人](東京 14,086,000 が最大) A1303 65歳以上人口 [人] B4101 年平均気温 [℃] A4101 出生数 [人] A9101 婚姻件数 [件] 目的変数 y: L3221 消費支出(二人以上の世帯)[円/月] 223,423-344,092
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
import pandas as pd
from sklearn.linear_model import Ridge
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1]).query('`SSDSE-B-2026` == 2023')
X = df[['A1101', 'A1303', 'B4101', 'A4101', 'A9101']]
y = df['L3221']

model = Pipeline([
    ('scale', StandardScaler()),
    ('ridge', Ridge(alpha=1.0)),
])
model.fit(X, y)
print('係数:', dict(zip(X.columns, model.named_steps['ridge'].coef_)))
📤 実行例: 係数: {'A1101': 10733.1, 'A1303': -5094.0, 'B4101': -6705.8, 'A4101': 549.9, 'A9101': 2339.0} → 説明変数は標準化済みなので「1 標準偏差変化したときの消費支出変化(円/月)」を意味 → A1101 総人口 +1σ → 消費支出 +10,733 円(人口規模の大きい県ほど支出が高い傾向) → B4101 年平均気温 +1σ → 消費支出 -6,706 円(温暖な県ほど支出が低め) → OLS(λ=0)では総人口の係数が +270,656 円と暴走するが、 Ridge が桁違いに縮小している
💬 読み方: Ridge は係数を完全に 0 にしない(縮小のみ)のに対し、 Lasso(L1)は係数をピッタリ 0 にして変数選択を行う。 alpha は GridSearchCV または RidgeCV で交差検証して決める。 標準化なしで Ridge を使うと、 単位の大きい変数(総人口 1,400 万 等)が小さい変数(年平均気温 15 等)と公平に罰せられず、 結果が歪む。 必ず StandardScaler を前段に挟むのが定石。 ベイズ的にはガウス事前分布 N(0,1/λ) と等価。

🔴 4. Lasso回帰(L1正則化)

$$\hat{\boldsymbol{\beta}}_{\text{lasso}} = \arg\min_{\boldsymbol{\beta}} \left\{\sum_{i=1}^{n}(y_i - \mathbf{x}_i^\top \boldsymbol{\beta})^2 + \lambda \|\boldsymbol{\beta}\|_1 \right\}$$

L1 罰則ののせいで、 $\lambda$ を大きくすると一部の係数がピッタリ 0 になる。 これが Lasso の変数選択効果

4.1 正則化パス

$\lambda$ を 0 から ∞ まで動かしながら、 係数がどう変化するかを描いた図を正則化パスと呼ぶ。 どの変数がどの順に 0 に落ちるかが見える。

📥 入力例(SSDSE-B-2026 の 2023 年・47 都道府県から 3 行) 都道府県 A1101(総人口) A1303(65歳以上人口) A4101(出生数) A9101(婚姻件数) B4101(年平均気温) 北海道 5,092,000 1,681,000 24,430 17,281 11.0 東京都 14,086,000 3,205,000 86,348 71,774 17.6 沖縄県 1,468,000 350,000 12,549 6,316 23.8 …(全 47 行)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.linear_model import Lasso
from sklearn.preprocessing import StandardScaler

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1]).query('`SSDSE-B-2026` == 2023')
X = df[['A1101', 'A1303', 'B4101', 'A4101', 'A9101']]
y = df['L3221']
X = StandardScaler().fit_transform(X)

alphas = np.logspace(-3, 2, 100)
coefs = []
for a in alphas:
    m = Lasso(alpha=a, max_iter=10000).fit(X, y)
    coefs.append(m.coef_)

coefs = np.array(coefs)
plt.figure(figsize=(10, 5))
for j in range(coefs.shape[1]):
    plt.plot(alphas, coefs[:, j], label=f'beta_{j+1}')
plt.xscale('log')
plt.xlabel('λ (alpha)')
plt.ylabel('係数')
plt.title('Lasso の正則化パス')
plt.legend()
plt.tight_layout()
plt.show()

🟣 5. Elastic Net

L1 と L2 を線形結合:

$$\hat{\boldsymbol{\beta}}_{\text{EN}} = \arg\min_{\boldsymbol{\beta}} \left\{\text{RSS} + \lambda \big(\alpha \|\boldsymbol{\beta}\|_1 + (1-\alpha)\|\boldsymbol{\beta}\|_2^2 \big) \right\}$$

$\alpha=1$ で Lasso、 $\alpha=0$ で Ridge。 中間ではグループ選択(相関の高い変数を「一緒に残す/落とす」)ができる。

1
2
3
4
from sklearn.linear_model import ElasticNetCV
en = ElasticNetCV(l1_ratio=[0.1, 0.5, 0.9, 1.0], cv=5, max_iter=10000)
en.fit(X, y)
print('best alpha:', en.alpha_, 'best l1_ratio:', en.l1_ratio_)

🧠 6. ベイズ的解釈 — 正則化=事前分布のMAP推定

$$\hat{\boldsymbol{\beta}}_{\text{MAP}} = \arg\max_{\boldsymbol{\beta}} \big[\log p(\mathbf{y}|\boldsymbol{\beta}) + \log p(\boldsymbol{\beta})\big]$$

🎯 7. 正則化の強さ λ の選び方

7.1 交差検証で選ぶ(基本)

1
2
3
4
5
6
7
8
9
import numpy as np
from sklearn.linear_model import RidgeCV, LassoCV
ridge = RidgeCV(alphas=[0.01, 0.1, 1, 10, 100], cv=5).fit(X, y)
print('best alpha:', ridge.alpha_)

# alphas=None は新しい scikit-learn では使えない。
# 候補の配列を自分で渡すのが、どの版でも通る書き方
lasso = LassoCV(alphas=np.logspace(-3, 2, 50), cv=5, max_iter=10000).fit(X, y)
print('best alpha:', lasso.alpha_)

7.2 1-SEルール

最小 CV 誤差から「1 標準誤差以内」で最も強い正則化(最も単純)を選ぶ。 解釈性重視のときに使う。

7.3 標準化を忘れない

L1/L2 ペナルティは係数のスケールに依存。 必ず StandardScaler でスケーリングする。

🧪 8. 深層学習における正則化

8.1 Weight Decay(L2 と等価)

$$\boldsymbol{\theta} \leftarrow \boldsymbol{\theta} - \eta(\nabla L + \lambda \boldsymbol{\theta})$$

SGD では L2 正則化と等価だが、 Adam では AdamW として分離した方が良い。

8.2 Dropout

学習時に各ニューロンを確率 $p$ でランダムに 0 化する。 アンサンブル平均効果で過学習を抑える。

1
2
3
4
5
6
import torch.nn as nn
model = nn.Sequential(
    nn.Linear(20, 64), nn.ReLU(), nn.Dropout(0.3),
    nn.Linear(64, 32), nn.ReLU(), nn.Dropout(0.3),
    nn.Linear(32, 1),
)

8.3 Early Stopping

検証損失が改善しなくなった時点で学習を打ち切り、 最良時点の重みを使う。 暗黙的な正則化。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
best_loss = float('inf')
patience, wait = 10, 0
for epoch in range(200):
    train_one_epoch()
    val_loss = evaluate()
    if val_loss  best_loss:
        best_loss = val_loss
        wait = 0
        save_checkpoint()
    else:
        wait += 1
        if wait >= patience:
            print(f'Early stop at epoch {epoch}')
            break

8.4 データ拡張

訓練データを拡張(画像なら回転・反転、 テキストなら同義語置換)して実効サンプル数を増やす。 効果的な暗黙的正則化。

8.5 BatchNorm / LayerNorm

各層の活性化を正規化することで学習を安定化。 副次的に正則化効果も持つ。

📊 9. 正則化手法の比較

手法 対象 主効果 特徴選択 適用場面
Ridge (L2)線形・GLM係数縮小×多重共線性
Lasso (L1)線形・GLM係数 0 化高次元データ
Elastic Net線形・GLM縮小 + 選択相関群あり
DropoutNNアンサンブル化×深層学習
Weight DecayNNL2 と等価×深層学習
Early StoppingNN・GBM最適点で停止×反復学習
データ拡張画像・テキスト実効 n 増×深層学習

Ridge と Lasso の決定的な違いは「等高線が円か菱形か」という幾何的事実から導かれる。 ここを腑に落とすと、 「なぜ Lasso だけがゼロを生むか」が完全に分かる。

🔬 数式を言葉で読み解く — 罰則最小化と制約最小化の双対性

Ridge は次の 2 形式が同値: $$\hat\beta^{Ridge} = \arg\min_\beta \sum_i (y_i - \mathbf{x}_i^\top \beta)^2 + \lambda \|\beta\|_2^2$$ $$\hat\beta^{Ridge} = \arg\min_\beta \sum_i (y_i - \mathbf{x}_i^\top \beta)^2 \quad \text{s.t.}\ \|\beta\|_2^2 \le t$$ λ と t は 1 対 1 対応する(KKT 条件)。 λ→∞ ↔ t→0(強い縮小)、 λ→0 ↔ t→∞(OLS)。

Lasso も同様に $\|\beta\|_1 \le t$ という菱形制約の双対表現を持つ。 重要なのは「等高線が制約領域に最初に触れる点が解」という幾何的解釈で、 **L2 円は丸いので軸上に触れにくく、 L1 菱形は頂点で軸上に触れやすい** → Lasso 解はスパース。

🔬 数式を言葉で読み解く — Ridge の解析解

Ridge は閉形式解を持つ: $$\hat\beta^{Ridge} = (X^\top X + \lambda I_p)^{-1} X^\top y$$ OLS の $(X^\top X)^{-1}$ に対角 $\lambda I_p$ が加わり、 多重共線性で特異な $X^\top X$ を正則化(regularize)する。 これが「正則化」という名前の由来そのもの。

🔬 数式を言葉で読み解く — Lasso の劣勾配と座標降下法

Lasso の目的関数 $\|y - X\beta\|_2^2 + \lambda\|\beta\|_1$ は β=0 で微分不能。 劣勾配を使う: $$\partial_{\beta_j} = -2 X_j^\top(y - X\beta) + \lambda \cdot \text{sign}(\beta_j)$$ 座標降下では各 $\beta_j$ をソフト閾値関数で更新: $$\hat\beta_j \leftarrow S_\lambda\!\left(\frac{X_j^\top r}{X_j^\top X_j}\right), \quad S_\lambda(z) = \text{sign}(z) \max(|z| - \lambda, 0)$$ 閾値 λ より小さい入力は問答無用でゼロに切り落とされる。 これがスパース性を生む計算機構。

🔬 数式を言葉で読み解く

🧮 SSDSE-B-2026 実値計算例 — 「消費支出 L3221」を α 別に予測

正則化の強さ α を変えると係数と CV-MSE がどう変わるか、 47 都道府県データ(SSDSE-B-2026)で具体的に確認します。 ここでは「消費支出 L3221(二人以上の世帯)」を目的変数、 数値列すべてを説明変数とした Ridge/Lasso を、 標準化済みで 5-fold CV にかけたときの一般的な傾向を掲載します(再現コードは下の Python 章を参照)。

α Ridge CV-MSE Lasso CV-MSE Lasso 非ゼロ係数 / 全体 解釈
0.001≈ OLS と同等≈ OLS と同等ほぼ全部罰則がほぼ無効。 過学習リスク大
0.01わずかに改善わずかに改善大半が残る弱い縮小、 過学習対策には不十分
0.1良好(CV最小近辺)多くの場合最良半分〜2/3SSDSE-B-2026 で典型的に選ばれる α
1.0縮小強い残り 5-10 個程度5-10 / 全体特徴選択が強く効く
10.0過剰縮小ほぼ全部 00〜2バイアスが大きすぎる

具体的に再現するためのコード(実行で実値が得られます):

📥 入力例(SSDSE-B-2026 の 2023 年・47 都道府県から 3 行) 都道府県 L3221(消費支出(二人以上の世帯)) 北海道 296,888 東京都 341,320 沖縄県 251,222 …(全 47 行)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
import numpy as np, pandas as pd
from sklearn.linear_model import Ridge, Lasso
from sklearn.model_selection import cross_val_score
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1]).query('`SSDSE-B-2026` == 2023')
num = df.select_dtypes(include='number')
X = num.drop(columns=['L3221'])
y = num['L3221']

for alpha in [0.001, 0.01, 0.1, 1.0, 10.0]:
    for name, M in [('Ridge', Ridge), ('Lasso', Lasso)]:
        pipe = Pipeline([('s', StandardScaler()), ('m', M(alpha=alpha, max_iter=20000))])
        mse = -cross_val_score(pipe, X, y, cv=5,
                               scoring='neg_mean_squared_error').mean()
        print(f'α={alpha:>7} {name}: MSE = {mse:.3f}')

※ SSDSE-B-2026 の数値列構成・件数(47都道府県)によって CV-MSE の絶対値はやや変動します。 重要なのは α 0.1 付近で最小α=10 で過剰縮小というです。

🧮 ベイズ事前分布としての解釈 — 正則化 = MAP 推定

Ridge と Lasso は、 それぞれ係数 β に**ガウス事前分布**または**Laplace 事前分布**を置いた MAP 推定と等価。 これは正則化が単なる工学的 trick ではなく、 ベイズ統計の中で自然な位置を持つことを示す。

🔬 数式を言葉で読み解く — Ridge ↔ ガウス事前

$\beta_j \sim \mathcal{N}(0, \tau^2)$ の事前分布を仮定すると、 MAP 推定は $$\arg\max_\beta \log p(y|X,\beta) + \log p(\beta) = \arg\min_\beta \|y - X\beta\|_2^2 + \frac{\sigma^2}{\tau^2}\|\beta\|_2^2$$ となり、 $\lambda = \sigma^2 / \tau^2$ と置けば Ridge に一致。 τ が大きい(事前が広い)と λ 小、 τ が小さいと λ 大。

🔬 数式を言葉で読み解く — Lasso ↔ Laplace 事前

$\beta_j \sim \text{Laplace}(0, b)$ では事前密度が $e^{-|\beta_j|/b}$ の形で、 対数尤度を取ると $-|\beta_j|/b$ → 罰則項が L1 になる。 $\lambda = 2\sigma^2 / b$。 Laplace 分布は原点に「尖り」を持つので、 係数を 0 に押し込みやすい。

📊 表: 事前分布と正則化の対応

事前分布罰則形対応する正則化
Gaussian $\mathcal{N}(0,\tau^2)$$\lambda\|\beta\|_2^2$Ridge
Laplace$\lambda\|\beta\|_1$Lasso
Horseshoe非凸ペナルティSCAD, MCP の親戚
Spike-and-Slab$\ell_0$ 罰則の連続近似ベイズ変数選択
Group Lasso 用事前$\lambda \sum_g \|\beta_g\|_2$Group Lasso
Cauchy非凸ロバスト推定

🧮 数式に値を入れて手で計算する: Ridge ペナルティ

合成 3 係数 β=[2,1,-0.5] で L2 ペナルティを計算する。

Step 1: ペナルティ

L2 = λ·Σβ² λ=0.1: 0.1·(4+1+0.25) = 0.525 λ=1.0: 1.0·5.25 = 5.25 λ=10: 52.5

Step 2: 影響

λ 大 → 係数が縮小 Ridge は係数を 0 にしない (Lasso と違う)

🐍 Python で再現

1
2
3
4
import numpy as np
beta = np.array([2, 1, -0.5])
for lam in [0.1, 1.0, 10]:
    print(f"λ={lam}: penalty = {lam * (beta**2).sum()}")

📤 実行結果

λ=0.1: penalty = 0.525 λ=1.0: penalty = 5.25 λ=10: penalty = 52.5

💬 手計算 (Step 1) と Python 出力が完全一致。

🐍 13. ライブラリ早見表

手法 パッケージ・クラス 主要引数
Ridge回帰sklearn.linear_model.Ridgealpha, solver, fit_intercept
RidgeCVsklearn.linear_model.RidgeCValphas, cv, scoring
Lasso回帰sklearn.linear_model.Lassoalpha, max_iter, selection
LassoCVsklearn.linear_model.LassoCValphas, cv, max_iter
Elastic Netsklearn.linear_model.ElasticNetalpha, l1_ratio
L1 LogisticLogisticRegression(penalty='l1')C = 1/λ, solver='liblinear'
Dropouttorch.nn.Dropoutp (dropout率)
Weight Decaytorch.optim.SGD/AdamWweight_decay
Early Stopcallbacks=lgb.early_stopping(N)stopping_rounds

📜 14. 正則化の歴史

💼 15. 実務応用例

正則化は実装ライブラリによって微妙に挙動が変わります。 ここでは 4 種類の代表的アプローチを並べ、 「同じデータ・同じ α でも結果が変わる可能性」を意識できるようにします。

🅰️ scikit-learn の Pipeline + GridSearchCV(推奨デフォルト)

📥 入力例(SSDSE-B-2026 の 2023 年・47 都道府県から 3 行) 都道府県 L3221(消費支出(二人以上の世帯)) 北海道 296,888 東京都 341,320 沖縄県 251,222 …(全 47 行)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
import pandas as pd, numpy as np
from sklearn.linear_model import Ridge, Lasso, ElasticNet
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import GridSearchCV

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1]).query('`SSDSE-B-2026` == 2023')
num = df.select_dtypes(include='number')
X, y = num.drop(columns=['L3221']), num['L3221']

# 教材では計算時間の都合で候補を絞っています
# (本来は alpha を 30 点、l1_ratio を 6 点、max_iter=30000 くらいで探索します)
pipe = Pipeline([('s', StandardScaler()), ('m', ElasticNet(max_iter=5000))])
grid = {'m__alpha': np.logspace(-3, 1, 12),
        'm__l1_ratio': [.1, .5, .9, 1.0]}
gs = GridSearchCV(pipe, grid, cv=5, scoring='neg_mean_squared_error', n_jobs=-1)
gs.fit(X, y)
print('Best α:', gs.best_params_['m__alpha'])
print('Best l1_ratio:', gs.best_params_['m__l1_ratio'])
print('Best CV-MSE:', -gs.best_score_)

🅱️ statsmodels — 検定統計量も得たい場合

📥 入力例(SSDSE-B-2026 の 2023 年・47 都道府県から 3 行) 都道府県 L3221(消費支出(二人以上の世帯)) 北海道 296,888 東京都 341,320 沖縄県 251,222 …(全 47 行)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
import statsmodels.api as sm
import pandas as pd
# 年度の列名は、英字コードで読むと 'SSDSE-B-2026'、日本語で読むと '年度'。
# ここでは英字コードの列(L3221 など)を使うので skiprows=[1] で読む
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932',
                 skiprows=[1]).query('`SSDSE-B-2026` == 2023')
num = df.select_dtypes(include='number')
X = sm.add_constant(num.drop(columns=['L3221']))
y = num['L3221']

# Ridge は fit_regularized で L1_wt=0
res_ridge = sm.OLS(y, X).fit_regularized(alpha=0.1, L1_wt=0)
# fit_regularized の params は ndarray なので、列名を付け直してから表示する
print('Ridge coef:')
print(pd.Series(res_ridge.params, index=X.columns).head())
# Lasso は L1_wt=1
res_lasso = sm.OLS(y, X).fit_regularized(alpha=0.1, L1_wt=1)
print('Lasso non-zero:', (res_lasso.params != 0).sum())

🅲 scipy.optimize で「数式から」正則化を実装(教育用)

📥 入力例(SSDSE-B-2026 の 2023 年・47 都道府県から 3 行) 都道府県 L3221(消費支出(二人以上の世帯)) 北海道 296,888 東京都 341,320 沖縄県 251,222 …(全 47 行)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import numpy as np, pandas as pd
from scipy.optimize import minimize
from sklearn.preprocessing import StandardScaler

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1]).query('`SSDSE-B-2026` == 2023')
num = df.select_dtypes(include='number')
X = StandardScaler().fit_transform(num.drop(columns=['L3221']))
y = num['L3221'].values

def ridge_loss(beta, X, y, alpha):
    r = y - X @ beta
    return r @ r + alpha * (beta @ beta)

beta0 = np.zeros(X.shape[1])
res = minimize(ridge_loss, beta0, args=(X, y, 1.0), method='L-BFGS-B')
print('Ridge β:', res.x.round(3))

def lasso_loss(beta, X, y, alpha):
    r = y - X @ beta
    return r @ r + alpha * np.abs(beta).sum()

res2 = minimize(lasso_loss, beta0, args=(X, y, 1.0), method='L-BFGS-B')
print('Lasso β:', res2.x.round(3))

🅳 glmnet スタイル — RidgeCV / LassoCV / ElasticNetCV を一気に

1
2
3
4
5
6
7
8
9
from sklearn.linear_model import RidgeCV, LassoCV, ElasticNetCV
import numpy as np
# 教材では alpha の候補を 50 点 → 20 点に、max_iter を 30000 → 5000 に減らしています
alphas = np.logspace(-3, 2, 20)
print('Ridge α*:', RidgeCV(alphas=alphas, cv=5).fit(X, y).alpha_)
print('Lasso α*:', LassoCV(alphas=alphas, cv=5, max_iter=5000, n_jobs=-1).fit(X, y).alpha_)
en = ElasticNetCV(l1_ratio=[.1, .5, .7, .9, .95, .99, 1.0],
                  alphas=alphas, cv=5, max_iter=5000, n_jobs=-1).fit(X, y)
print('EN α*, l1_ratio*:', en.alpha_, en.l1_ratio_)

📦 ライブラリ別「正則化強度」パラメータ対応表

ライブラリ パラメータ 大きい値 = 強い罰則?
sklearn Ridge / Lassoalphaはい
sklearn LogisticRegressionCいいえ(C = 1/α)
statsmodels fit_regularizedalpha, L1_wtはい
glmnet (R)lambdaはい
XGBoostreg_alpha, reg_lambdaはい
PyTorch optimizerweight_decayはい

⚠️ 10. よくある落とし穴

落とし穴 対処
標準化を忘れるL1/L2 は係数スケールに依存。 必ず StandardScaler を Pipeline で。
切片に罰則をかけるsklearn は既定で切片に罰則なし。 自前実装では注意。
λ を訓練データで選ぶ必ず CV で選ぶ。 訓練最適は λ=0 になる。
Lasso の係数を「効果量」と誤解縮小推定なので OLS の効果量とは別物。 推論をしたいなら別途 OLS で再推定。
Dropout を予測時にも使う推論時は model.eval() で必ず Dropout を切る。
Early Stopping の判断にテスト集合必ず検証集合を使う。 テスト集合はリーク厳禁。
Weight Decay を BN/LN にも適用BN/LN のスケール・バイアスには WD をかけないのが標準。

🏋️ 11. 練習問題(SSDSE-B-2026)

Q1. 消費支出 L3221 を全数値列で予測する。 OLS・Ridge・Lasso・Elastic Net の 4 モデルを 5-fold CV で比較しなさい。
📥 入力例(SSDSE-B-2026 の 2023 年・47 都道府県から 3 行) 都道府県 L3221(消費支出(二人以上の世帯)) 北海道 296,888 東京都 341,320 沖縄県 251,222 …(全 47 行)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
import pandas as pd
from sklearn.linear_model import LinearRegression, Ridge, Lasso, ElasticNet
from sklearn.model_selection import cross_val_score
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1]).query('`SSDSE-B-2026` == 2023')
num = df.select_dtypes(include='number')
X = num.drop(columns=['L3221'])
y = num['L3221']

models = {
    'OLS':  LinearRegression(),
    'Ridge':Ridge(alpha=10.0),
    'Lasso':Lasso(alpha=0.1, max_iter=10000),
    'EN':   ElasticNet(alpha=0.1, l1_ratio=0.5, max_iter=10000),
}
for name, m in models.items():
    pipe = Pipeline([('scale', StandardScaler()), ('m', m)])
    sc = cross_val_score(pipe, X, y, cv=5, scoring='neg_mean_squared_error')
    print(f'{name}: MSE = {-sc.mean():.2f}')
Q2. Lasso の正則化パスを描き、 どの順で係数が 0 に落ちるか観察しなさい。

本ページの 4.1 のコードを参考に、 SSDSE-B のすべての数値列を使って正則化パスを描き、 最も early に残る変数 3 つを言語化してください。

Q3. RidgeCV と LassoCV で選ばれた α と、 そのときの CV 誤差を比較しなさい。
1
2
3
4
5
6
7
from sklearn.linear_model import RidgeCV, LassoCV
import numpy as np
ridge = RidgeCV(alphas=np.logspace(-3, 3, 50)).fit(X, y)
lasso = LassoCV(alphas=np.logspace(-3, 3, 50), cv=5, max_iter=10000).fit(X, y)
print('Ridge best α:', ridge.alpha_)
print('Lasso best α:', lasso.alpha_)
print('Lasso 非ゼロ係数:', (lasso.coef_ != 0).sum(), '/', len(lasso.coef_))

📝 12. 報告フォーマット

❌ NG例

「Lasso を使ったら良い結果が出ました。」

✅ OK例

「20変数の入力に対し、 標準化後 LassoCV(5-fold, α grid = logspace(-3,3,50))で α = 0.12 を選択。 12 変数の係数が 0 となり、 残った 8 変数で MSE = 1.83 を達成(OLS = 2.71、 +33% 改善)。 残った変数は 1-SE ルールでも同一であり、 安定した選択と判断した。」

⚠️ 落とし穴(補強版 — 実務でやりがちな7パターン)

① 標準化を CV 全体ではなく fold 内でしない(情報リーク)
CV の外側でStandardScaler を fit してから cross_val_score に渡すと、 検証集合の統計量が訓練側に漏れます。 これは正則化された推定の汎化誤差を楽観評価する重大なバグで、 論文でも頻繁に見られます。 正解は Pipeline で ('scaler', StandardScaler())('model', Ridge()) を結び、 fold 内で fit/transform を完結させること。 sklearn の Pipeline はこれを自動でやってくれる強力なツールなので、 必ず使ってください。
② Lasso の係数を「重要度」と直接解釈する
Lasso は縮小変数選択を同時に行うため、 残った係数は OLS の効果量より系統的に 0 に向かってバイアスがかかっています。 つまり「Lasso 係数が 0.3」は「真の効果量が 0.3」ではなく、 0 寄りに圧縮された値です。 解釈・推論を目的とするなら、 Lasso で変数選択した後に選ばれた変数だけで OLS を再フィットする「post-selection OLS」が定番。 ただし、 これも厳密な信頼区間としては問題があり、 Tibshirani らの「selective inference」を併用する必要があります。
③ α の最小値だけで決めずに 1-SE ルールを忘れる
CV-MSE が最小の α を選ぶのが基本ですが、 「最小値 + 1 標準誤差以内の中で最も罰則が強いα」を選ぶ「1-SE ルール」が推奨されます。 これは CV 誤差自体に推定誤差があるためで、 統計的に同等な α の中ではより単純(=罰則の強い)モデルを選ぶ方が、 新規データに対する性能が安定するからです。 SSDSE-B のような n=47 の小標本では特に重要で、 これを忘れると α が過小評価されがちです。
④ Ridge を変数選択に使ってしまう
Ridge は係数を 0 に縮める力はあっても正確に 0 にすることはほぼないので、 変数選択には向きません。 「変数を減らしたい」のなら Lasso か Elastic Net を使うべきです。 一方で、 「全変数を残しつつ係数を安定化したい」(特に多重共線性がある場合)には Ridge が最適。 目的に応じて使い分けを意識しましょう。 「とりあえず Ridge」「とりあえず Lasso」は思考停止のサインです。
⑤ ダミー変数を標準化してしまう
カテゴリ変数を one-hot 化したダミー(0/1)を StandardScaler に通すと、 (0−p̂)/√(p̂(1−p̂)) のような変換になり、 解釈が難しくなります。 罰則の効き方も意図とずれます。 多くの実装で連続変数だけ標準化し、 ダミーはそのまま渡すのが定石。 ColumnTransformer で連続列だけ StandardScaler、 ダミー列は passthrough または OneHotEncoder で分けて処理してください。
⑥ Elastic Net の l1_ratio をデフォルトのまま動かさない
sklearn の ElasticNet は l1_ratio=0.5 がデフォルトですが、 これが最適とは限りません。 ElasticNetCV では l1_ratio=[.1, .5, .7, .9, .95, .99, 1] のようにグリッドで探すのが標準です。 l1_ratio=1 だと Lasso、 0 だと Ridge と等価なので、 自分のデータで最適なバランスを探りましょう。 「Elastic Net 使いました」だけでは不十分で、 l1_ratio と α の両方を報告するのが論文・実務での作法です。
⑦ Weight Decay を「等価な L2」だと盲信する(Adam の罠)
SGD では Weight Decay と L2 正則化は数学的に等価ですが、 Adam では一致しません。 Adam は勾配を分散で正規化するため、 L2 損失に加える形では効果が歪みます。 Loshchilov & Hutter(2017)が提案した AdamW は、 Weight Decay を分離して適用することで、 本来の正則化効果を取り戻しました。 深層学習で正則化を語るときは Adam + weight_decay ではなく AdamW を使うのが現在の標準です。

⚠️ 正則化の 7 つの落とし穴

  1. 標準化を忘れると罰則が単位依存になる — 「面積(㎡)と価格(円)」を混ぜると、 円のスケールに罰則が引きずられ、 面積係数が異常に縮小。 必ず StandardScaler で X を標準化。 sklearn の Ridge は normalize が deprecated になったので、 Pipeline で手動標準化が必須。
  2. λ を CV せずに勘で決める — λ=1 をデフォルトで使うと、 データのスケールによっては「OLS と区別不能」あるいは「全て 0」になる。 必ず RidgeCV / LassoCV / ElasticNetCV で対数スケール(np.logspace(-4, 4, 100))から選択。
  3. Lasso の係数を有意性として誤読 — Lasso でゼロにならなかった係数は「重要」ではない。 同じ λ で再学習しただけで別の変数が選ばれることがある(不安定)。 Stability Selection(RandomizedLasso)や bootstrap 平均を併用。
  4. 多重共線群で Lasso が代表 1 つだけ拾う — 相関 0.99 の 2 変数があると Lasso はランダムに片方を 0 にする。 「両方とも意味があるのに片方しか選ばれない」誤読が起きる。 ElasticNet や Group Lasso に切替。
  5. 非線形効果を取りこぼす — 罰則は線形項に対してのみ。 「特徴量と y が U 字関係」だと、 Ridge/Lasso では捉えられない。 多項式特徴量(PolynomialFeatures)や Spline 拡張後に正則化を適用。
  6. λ の単調性に過信 — 「λ が大きいほど係数は単調に縮む」と思い込みがちだが、 Lasso では λ の経路に沿って **係数の符号が反転** することもある(特に多重共線下)。 lasso_path で経路を可視化して確認。
  7. Lasso の漸近正規性を仮定して t 検定 — OLS と違い、 Lasso 係数の漸近分布は単純な正規分布にならない。 信頼区間が欲しいなら **debiased Lasso**、 **selective inference**(Lee et al., 2016)、 もしくは bootstrap を使う。

🛠 λ 選択の 5 手法とその使い分け

λ 選択は正則化の中心的問いで、 文脈に応じた 5 つの方法がある。

手法原理推奨ケース
K-fold CVCV-RMSE 最小予測精度重視、 標準
1-SE rule最小 + 1SE の最大 λ解釈性重視、 よりスパースに
AIC / BIC情報量規準CV が計算困難な大規模
経路アルゴリズム全 λ に対する解を一括計算解釈と可視化
empirical Bayes事前分布のハイパーパラメータ MLEベイズ的解釈

🐍 1-SE rule で Lasso の λ を「より保守的」に選ぶ

このコードでやること: LassoCV で CV 曲線を取り、 最小値 + 1SE の範囲で最大の λ を選ぶ(R の glmnet のデフォルトと同じ流儀)。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import numpy as np
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import Lasso
from sklearn.model_selection import KFold

# Xs / ys はこのブロックで作る(標準化した X と、配列にした y)
Xs = StandardScaler().fit_transform(X)
ys = np.asarray(y, dtype=float)
alphas = np.logspace(-3, 2, 100)

kf = KFold(n_splits=10, shuffle=True, random_state=42)
mse_path = np.zeros((len(alphas), 10))
for j, (tr, va) in enumerate(kf.split(Xs)):
    for i, a in enumerate(alphas):
        m = Lasso(alpha=a, max_iter=10000).fit(Xs[tr], ys[tr])
        mse_path[i, j] = np.mean((ys[va] - m.predict(Xs[va])) ** 2)

mean = mse_path.mean(axis=1)
se   = mse_path.std(axis=1) / np.sqrt(10)
i_min = np.argmin(mean)
i_1se = np.max(np.where(mean <= mean[i_min] + se[i_min])[0])
print(f"min CV: α = {alphas[i_min]:.4f}, MSE = {mean[i_min]:.4f}")
print(f"1-SE:  α = {alphas[i_1se]:.4f}, MSE = {mean[i_1se]:.4f}")
print(f"非ゼロ係数 (1-SE 解) = "
      f"{sum(abs(Lasso(alpha=alphas[i_1se], max_iter=10000).fit(Xs, ys).coef_) > 1e-6)}")

📤 実行例:

min CV: α = 1.0723, MSE = 21.2704 1-SE: α = 1.2045, MSE = 26.7427 非ゼロ係数 (1-SE 解) = 10

💬 SSDSE-B-2026 の 2023 年・47 都道府県(n=47、 説明変数 109 列)で実際に走らせると、 1-SE ルールが選ぶ α は最小 CV の 1.0723 に対し 1.2045、 わずか 1.12 倍にしかならず、 非ゼロ係数も 10 個のまま変わらない。 α の候補を 100 点に細かく刻んだうえ CV 誤差の谷が浅いためで、 「1-SE ルールを使えば必ずモデルが小さくなる」わけではないことが分かる。 1-SE ルールはあくまで同程度の誤差なら単純な方を選ぶという基準であり、 効き方はデータ次第。 なお CV MSE が 21 と極端に小さいのは、 n=47 に対し p=109 と変数が多く、 L3221 と強く連動する費目列が説明変数側に残っているため(実務ではこうした漏れを先に取り除く)。

🗺 概念マップ

正則化を中心に、 罰則項の形(L2・Group・Fused・Adaptive・非凸 SCAD/MCP・Square-root)で派生する 6 つの正則化手法を放射状に配置した。 SSDSE-B-2026 の県別多変数回帰で、 変数のグルーピング・順序・スケールに応じてどの罰則を選ぶかが俯瞰できる。

regularization 係数の大きさそのもの Group Lasso Fused Lasso Adaptive Lasso SCAD / MCP Square-root La

正則化を中心に、 上に 係数の大きさそのものを罰則として効かせる L2/Ridge、 右上に変数群を一括で 0/非 0 にする Group Lasso、 右下に隣接係数の差を罰則化する Fused Lasso、 下に係数ごとに重み付き L1 罰則を与える Adaptive Lasso、 左下に L1 の縮小バイアスを軽減する非凸罰則 SCAD / MCP、 左上に λ チューニング不要を狙う Square-root Lasso が配置される。 SSDSE-B-2026 の県別変数を 30 以上扱う回帰では、 まず標準的な Ridge/Lasso/Elastic Net を試した後、 変数のグルーピング構造(例: 消費支出の費目別内訳 L322101〜L322110)や 順序構造(例: 年少 A1301・生産年齢 A1302・高齢 A1303 の年齢 3 区分人口)に応じて Group/Fused 系へ進むのが定石です。

このマップで重要なのは、 すべての変種が「経験リスク + 罰則」という共通骨格を持ち、 罰則の形だけが異なる点。 罰則項の幾何(L1 の角張り・L2 の丸み・非凸罰則のくぼみ)がそのまま 解の性質(スパース性・縮小度・バイアス)を決めるため、 SSDSE 列の構造に応じて罰則を選ぶことが、 正則化を「単なるおまじない」から脱却させる鍵になります。

🔗 隣接手法への橋渡し

「正則化」は単独で完結する手法ではなく、 隣接領域と連携することで真価を発揮する。

「複雑すぎるモデルにペナルティを課す」 思想は線形回帰・深層学習・木モデルすべてに通底し、 汎化性能を実用水準まで引き上げる現代機械学習の必須技法である。

🌳 手法選択フロー

「正則化」を回帰・ニューラルネットワークの過学習対策に使うとき、 係数の性質と特徴量数で判定する。

  1. 特徴量を絞り込みたいか? 多数の説明変数から重要なものを選別 → Lasso (L1) が係数を 0 に押す。 すべての変数を残しつつ縮小 → Ridge (L2)
  2. 多重共線性が強いか? 説明変数同士が高相関 → Ridge が安定。 グループ的に効く変数群がある → Elastic Net (L1+L2 混合)
  3. ハイパーパラメータ λ をどう選ぶか? 交差検証 で λ を grid search、 AIC/BIC を最小化する λ を選ぶ、 もしくは経験則で 0.01〜10 の対数スケールを試す

正則化はスケール依存なので 標準化 が必須。 SSDSE-B-2026 で総人口 A1101(人)・年平均気温 B4101(℃)・消費支出 L3221(円)のように単位が異なる変数を入れる場合、 標準化を忘れると λ が一部の変数だけに強く効いて意図しない結果になる。

🎮 触って理解する

下の λ(正則化強度)スライダー を動かすと、 少数のノイズ入りデータ(正則化の挙動を見るための合成デモデータ。 SSDSE-B-2026 の実測値ではありません)に 9 次多項式 を当てはめた曲線がリアルタイムに変化します。 λ を小さくすると曲線が ぐにゃぐにゃ(過学習)、 大きくすると ほぼ直線(過度な平滑化) になり、 検証誤差が最小 になる「ちょうど良い λ」が中間に存在することを体感してください。 L2 (Ridge)L1 (Lasso) を切り替えると、 係数の縮み方(L2 は一様に縮小 / L1 は一部が ちょうど 0 に)が変わります。

罰則の種類:
λ = 0.0501
① 当てはめ曲線(●訓練 / ○検証)
② 誤差 vs λ(縦線=現在位置)
③ 各次数の係数(標準化後の大きさ/L1 では一部が 0 に)
方式: L2 (Ridge) 訓練RMSE: - 検証RMSE: - 非ゼロ係数: - / 9 検証最小の λ: -
🧭 直感: 正則化は「データへの忠実さ(訓練誤差を下げる力)」と「モデルの単純さ(係数を小さく保つ力)」の 綱引き です。 λ はそのバランスつまみで、 λ→0 は忠実さ最優先=過学習λ→∞ は単純さ最優先=ほぼ定数/直線。 訓練誤差は λ とともに単調に増えますが、 検証誤差は U 字 を描き、 その底(②の曲線の最下点)が汎化性能の最良点です。
⚠️ よくある落とし穴:
  • 標準化は必須。 罰則 Σβ² や Σ|β| は係数の絶対量に効くため、 単位(円・人・℃)がバラバラだとスケールの大きい変数だけが不当に強く縮む。 このデモも多項式基底を 標準化してから 罰則を課しています(標準化)。
  • λ を「なんとなく」で決めない。 訓練誤差だけを見ると「λ は小さいほど良い」と錯覚する。 必ず 交差検証 の検証誤差(②の U 字)で選ぶこと。
  • 切片は罰則しない。 定数項まで縮めると予測が原点方向に偏る。 このデモは y を中心化して切片を別扱いにしています。
🚀 発展: L1(Lasso)の罰則面は原点で「角張って」いるため、 解が座標軸上(=係数ちょうど 0)に乗りやすく スパース性(変数選択) が生まれます。 ③の棒グラフで L1 に切り替えると、 λ を上げるにつれ棒が次々と 完全に消える(0 になる) のが見えます。 L2 は係数を 0 に潰さず一様に縮めるだけ。 両者の中間が Elastic Net(L1+L2 混合)で、 相関の強い変数群をまとめて残しつつスパース性も得たいときに有効です。 λ 選択の背景理論は バイアス・分散トレードオフ、 過学習の検知は 過学習交差検証 を参照。

🔖 キーワード索引(チップから該当箇所へジャンプ)

論文記事から各用語のリンクをクリックすると、 該当箇所が開きます:

なぜ正則化 L1/L2ノルム Ridge回帰 Lasso回帰 Elastic Net ベイズ的解釈 正則化パス λの選び方 Dropout Weight Decay Early Stopping データ拡張 BatchNorm バイアス・分散

🔖 キーワード索引(拡張版 — 論文・実務で頻出する用語)

既存索引に加え、 派生概念や近接トピックへのジャンプを充実させました。 用語をクリックすると該当章へ移動します。

過学習の予防 縮小推定 スパース性 グループ選択 MAP推定 ラプラス事前分布 1-SE ルール CV grid 確率的正則化 AdamW 暗黙の正則化 SSDSE-B実値 標準化忘れ CV情報リーク scikit-learn statsmodels scipy.optimize

🧭 解説深化 — 「有効自由度」と「パネルCV」から正則化を見直す

本ページはここまで「係数がどう縮むか」を中心に正則化を説明してきた。 この深化セクションでは視点を変え、 ①モデルが実質的に使えるパラメータ数(有効自由度)が λ でどう減るか② SSDSE-B-2026 が 47 都道府県 × 12 年のパネルデータであることを無視すると λ 選択そのものが歪む、 という 2 つの未出の角度から掘り下げる。 数値はすべて SSDSE-B-2026 の実測値である。

💡 直感 — λ は「使えるパラメータ数」を連続的に絞るダイヤル

Ridge 回帰の複雑さは「変数が 5 個だから自由度 5」とは限らない。 標準化済み計画行列 $\mathbf{X}$ の特異値を $d_1 \ge d_2 \ge \dots \ge d_p$ とすると、 Ridge の有効自由度

$$\mathrm{df}(\lambda) = \sum_{j=1}^{p} \frac{d_j^2}{d_j^2 + \lambda}$$

で与えられる($\lambda=0$ なら各項が 1 になり df$=p$、 $\lambda\to\infty$ で df$\to 0$)。 つまり λ は変数を「入れる/外す」の二択ではなく、 パラメータ数を小数単位で連続的に減らすダイヤルとして働く。

本ページのRidge例と同じ設定(2023 年・47 都道府県、 標準化した A1101 総人口・A1303 65歳以上人口・B4101 年平均気温・A4101 出生数・A9101 婚姻件数の 5 変数)で実測すると、 特異値は 13.68, 6.79, 1.30, 0.38, 0.18 となり、 df(λ) は次の通り:

λ (alpha)df(λ)意味
0(OLS)5.005 変数をフルに使用
12.76実質「約 2.8 変数」相当
101.93実質 2 変数弱
1000.99ほぼ 1 変数分の複雑さ

注目すべきは λ=1 の時点で自由度がすでに 5 → 2.76 に半減していること。 理由は特異値の偏りにある。 2023 年断面では総人口 A1101 と 65歳以上人口 A1303 の相関が r = 0.991 と極端に高く(多重共線性)、 この共線方向に対応する特異値(0.38 や 0.18)は極小になる。 df の式で $d_j^2$ が小さい項は少しの λ で分母に負け、 真っ先に 0 へ潰れる。 つまり Ridge はデータが最も信用できない(=共線で推定が暴れる)方向から順に自由度を奪う装置であり、 「全係数を一様に縮める」という素朴なイメージより精密な動作をしている。 本文 3 章で OLS の総人口係数 +270,656 円が Ridge で +10,733 円まで縮む劇的な変化も、 「共線方向の自由度が最初に没収される」ことの現れとして統一的に理解できる。

⚠️ 落とし穴(重要) — パネル構造を無視した CV は λ 選択ごと歪む

本ページの λ 選択の議論はすべて「CV で選べ」だったが、 SSDSE-B-2026 の全期間データは 47 都道府県 × 12 年(2012–2023)= 564 行のパネルデータである。 ここに普通の KFold(shuffle=True) を使うと、 同じ県の別年度が訓練側と検証側に分かれる。 都道府県の特徴は年をまたいでほぼ不変(東京は毎年東京)なので、 検証集合の情報が事実上訓練側に漏れ、 誤差が楽観的に見積もられる。 実測すると:

CV の切り方CV RMSE(円/月)評価している能力
KFold(5, shuffle, seed=0)22,131「見たことのある県の別年度」を当てる能力
GroupKFold(5, 県単位)24,994「未知の県」を当てる能力

設定: StandardScaler + Ridge(alpha=1.0) の Pipeline、 説明変数は上と同じ 5 列、 目的変数 L3221 消費支出(全 564 行、 平均 287,336 円・標準偏差 23,591 円)。 実測値。

行シャッフル KFold は GroupKFold より約 13% 楽観的だった。 怖いのは誤差の過小評価そのものより、 この歪んだ CV 曲線の底で λ を選んでしまうこと — リークがあると「訓練に似たデータなら小さい λ でも当たる」ため λ が過小に選ばれ、 本番(未知の県・将来年度)で過学習が再発する。 つまり正則化を入れたのに、 正則化の強さを決める物差し自体が壊れている状態になる。 対策は問いに合わせて CV を切ること: 「未知の県への一般化」なら GroupKFold(groups=県)、 「将来年度の予測」なら年で切る TimeSeriesSplit 型の分割を使う(パネルデータ交差検証)。 これは本文の落とし穴①(fold 内標準化)とは別種のリークで、 Pipeline を正しく使っていても発生する点に注意。

🚀 発展 — df(λ) がつなぐ「正則化と情報量規準」、 そして統一視点

有効自由度の御利益は直感だけではない。 AIC・BIC・GCV はパラメータ数 $p$ を罰する規準だが、 正則化モデルでは $p$ の代わりに $\mathrm{df}(\lambda)$ を差し込める:

$$\mathrm{GCV}(\lambda) = \frac{\frac{1}{n}\sum_i (y_i - \hat{y}_i)^2}{\left(1 - \mathrm{df}(\lambda)/n\right)^2}$$

これにより CV を回さずに λ を選ぶ近道が得られ(本文 7 章の表にある「AIC/BIC」経路の理論的根拠)、 AIC の世界と正則化の世界が df(λ) を蝶番として接続される。 さらにこの視点は深層学習にも伸びる: Early Stopping は「勾配降下の反復回数 $t$ が λ の逆数の役割を果たし、 途中で止めることで有効自由度を絞る」操作と解釈でき($t \propto 1/\lambda$ の対応が線形モデルで示せる)、 Dropout もアンサンブル平均を通じて実効的な複雑さを下げる装置と読める。 「Ridge・Lasso・Early Stopping・Dropout はどれも有効自由度をデータ量に見合う水準まで絞る手段」という統一視点を持つと、 手法の暗記が原理の理解に変わる。 厳密な展開は Hastie, Tibshirani & Friedman The Elements of Statistical Learning の 3.4 節・7.6 節(自由度と楽観バイアス)が定番。