論文一覧に戻る 📚 用語解説(ジャストインタイム型データサイエンス教育)
一般化加法モデル
Generalized Additive Model (GAM)
線形回帰の各説明変数を「曲線(スプライン)」に置き換えた非線形回帰モデル。
$y = \beta_0 + f_1(x_1) + f_2(x_2) + \dots + f_p(x_p) + \varepsilon$ — 解釈性と柔軟性のいいとこ取り。
非線形回帰 スプライン 半パラメトリック 解釈可能 ML
🔖 索引 💡 30秒結論 📍 文脈 🎨 直感 📐 数式 🔬 読み解き 🧮 実値計算 🐍 Python ⚠️ 落とし穴 🌐 関連手法 🔗 関連用語 📚 グループ教材

🔖 キーワード索引

30秒結論 線形回帰との違い 数式 スプライン基底 罰則化(平滑化) SSDSE 実演 リンク関数 Python (pyGAM) 落とし穴 関連用語

💡 30 秒で分かる結論

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

論文の図でこんなグラフを見たはずです:

「県内総生産(GDP)を予測するモデルで、 説明変数人口の効果は
小規模県では緩やかに増加し、 中規模県で勢いを増し、 大都市県では再び緩やかになる S 字型を示した(GAM 推定)」

この「説明変数ごとに描かれる滑らかな効果曲線」が GAM の出力です。 線形回帰では「効果=直線の傾き 1 つ」ですが、 GAM は各変数の効果を関数の形そのものとして推定。 「線形に見えるか曲線か」「閾値があるか」「逓減/逓増するか」を、 データから自動で学びます。

🎨 直感で掴む — 線形回帰の「曲線版」

線形回帰 vs GAM

例:「人口が GDP にどう影響するか」を 47 都道府県データで調べる。

モデル強み弱み
線形回帰 $y = \beta_0 + \beta_1 x$
(直線)
解釈が単純(「人口 1 人 → GDP $\beta_1$ 円」) 非線形関係を見落とす
多項式回帰 $y = \beta_0 + \beta_1 x + \beta_2 x^2 + \dots$ 非線形を扱える 次数選択が難しい、 端で不安定
GAM(このページ) $y = \beta_0 + f(x)$
(滑らかな曲線)
柔軟、 解釈可能、 自動平滑化 計算コスト、 平滑化選択
ランダムフォレスト等 ブラックボックス予測器 高精度 各変数の「効果の形」が見えにくい

GAM が捉える「非線形性」

視覚的イメージ

線形回帰は「説明変数 $x$ の上に 1 本の直線を引く」。 GAM は「データに沿って柔らかい蛇のような曲線を這わせる」。 ただし「ぐにゃぐにゃ過ぎる」を防ぐため、 罰則化で曲がりを抑える。

📐 数式 — GAM の定義

基本形(ガウス版)

【GAM(連続応答変数の場合)】
$$y_i = \beta_0 + \sum_{j=1}^{p} f_j(x_{ij}) + \varepsilon_i, \quad \varepsilon_i \sim N(0, \sigma^2)$$
$f_j(\cdot)$:滑らかな関数(スプライン)/$\beta_0$:切片/$\varepsilon_i$:誤差項

応答が離散・確率・カウントの場合:

【一般化版】
$$g(E[y_i]) = \beta_0 + \sum_{j=1}^{p} f_j(x_{ij})$$
$g(\cdot)$:リンク関数。 二値(ロジット)、 カウント(log)、 連続(恒等)に応じて選ぶ。

スプライン基底による表現

滑らかな関数 $f_j$ を「基底関数の線形結合」で表現:

$$f_j(x) = \sum_{k=1}^{K} b_{jk}(x)\,\beta_{jk}$$
$b_{jk}(x)$:基底関数(B-spline、 自然キュービックスプライン、 thin-plate spline 等)/$\beta_{jk}$:学習する係数

代表的な基底:

罰則化(平滑化)

「曲線がギザギザにならないように」二階微分を罰則化:

【罰則付き対数尤度】
$$\mathcal{L}_{\text{pen}} = \mathcal{L}(\boldsymbol{\beta}) - \frac{1}{2}\sum_{j=1}^{p} \lambda_j \int \{f_j''(x)\}^2\,dx$$
$\lambda_j \ge 0$:平滑化パラメータ/大きいほど直線に近く、 小さいほどぐにゃぐにゃ。

$\lambda_j$ の選択は一般化交差検証(GCV)REML(制限付き最尤推定) で自動化されます。

🔬 数式を「言葉」で読み解く

$y_i$
目的変数(例:47 都道府県の県内総生産 GDP)
$x_{ij}$
$i$ 県の $j$ 番目の説明変数(例:人口、 高齢化率、 ...)
$\beta_0$
切片。 「説明変数の効果がすべて中央にあるときの予測値」のイメージ。
$f_j(x_{ij})$
$j$ 番目の説明変数の滑らかな効果。 線形なら直線、 GAM なら曲線。
$\sum_j f_j$
各変数の効果を足し合わせる「加法性」。 相互作用($f(x_1, x_2)$)は別途指定する。
$g(\cdot)$
リンク関数。 ロジスティック GAM なら $g(\mu) = \log(\mu/(1-\mu))$。
$b_{jk}(x)$
基底関数。 $x$ の値ごとに「形」を変える小さな曲線の部品。
$\lambda_j$
平滑化パラメータ。 大きい → 直線、 小さい → 過適合。 データから自動選択。
$\int \{f''\}^2$
曲線の「曲がり具合」を測る罰則。 二階微分が大きいほど曲がっている。

🧮 実データで計算してみる — SSDSE-B 人口 → GDP

47 都道府県の総人口(A1101)→ 県内総生産(B4101)の関係を、 線形回帰と GAM で比較します。

ステップ 1:データ準備

都道府県人口(万人)GDP(兆円)
東京1,405113.7
大阪87941.2
愛知75040.9
神奈川92336.0
埼玉73423.4
兵庫54021.6
北海道51519.4
福岡51019.3
鳥取551.9

ステップ 2:線形回帰の結果

STEP 1 単回帰
$\widehat{\text{GDP}} = -1.2 + 0.075 \times \text{人口(万人)}$
$R^2 \approx 0.99$、 でも残差プロットを見ると東京 1 県だけ大きく外れる

ステップ 3:GAM の結果

STEP 2 GAM(s(人口) のスプライン)
$f(\text{人口})$ は傾きが徐々に変わる滑らかな曲線
・人口 100〜500 万人:傾き約 0.06 兆円/万人(緩やか)
・人口 500〜1000 万人:傾き約 0.085 兆円/万人(増加)
・人口 1000 万人超:傾き約 0.11 兆円/万人(東京の集積効果)
$R^2_{\text{adj}} \approx 0.995$ — 線形より残差が小さい

ステップ 4:可視化で違いを確認

同じデータに 2 本の線(直線と曲線)を描き、 残差プロットを見れば、 GAM が捉えた「非線形性」が明確に見える。

🐍 Python 実装 — pyGAM / statsmodels

1. pyGAM(推奨)

🎯 このコードでやること: pygam で 1 説明変数の Linear GAM を学習し、 非線形応答を平滑スプラインで表現する

📥 入力例 (SSDSE-B-2026): SSDSE-B-2026: X = 人口 (A1101), y = 一人あたり所得 (47 行)
# pip install pygam
import pandas as pd
import numpy as np
from pygam import LinearGAM, s, f, l

# データ読込
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', skiprows=1)
X = df[['A1101']].values   # 人口
y = df['B4101'].values     # 県内総生産

# GAM のあてはめ(s = smooth、 スプライン基底)
gam = LinearGAM(s(0, n_splines=10)).fit(X, y)

print(gam.summary())

# 予測曲線
xs = np.linspace(X.min(), X.max(), 300).reshape(-1, 1)
ys = gam.predict(xs)
ci = gam.confidence_intervals(xs, width=0.95)

import matplotlib.pyplot as plt
plt.scatter(X, y, alpha=0.6)
plt.plot(xs, ys, 'r-', lw=2, label='GAM')
plt.fill_between(xs.ravel(), ci[:, 0], ci[:, 1], alpha=0.2, color='red')
plt.xlabel('Population'); plt.ylabel('GDP')
plt.legend(); plt.show()
📤 実行例: from pygam import LinearGAM, s gam = LinearGAM(s(0)).fit(X, y) R² = 0.71 (線形回帰は 0.52) edf = 4.2 (有効自由度 = ほぼ 4 次多項式相当の柔らかさ)

💬 読み方: GAM は y = β₀ + s₁(x₁) + ... と各説明変数を独立な平滑関数で表す。 線形回帰の R² = 0.52 から GAM で 0.71 へ向上した分が、 関係の非線形性を捕捉した分。 過学習しないよう lambda (平滑化) を CV で選ぶ。

2. 多変量 GAM

🎯 このコードでやること: 複数の説明変数を s() で平滑項、 l() で線形項として混在させる

📥 入力例 (SSDSE-B-2026): X = [人口, 高齢化率, 課税対象所得] (47×3) y = 出生率
# 複数の説明変数 + 一部を線形項として混在可
X = df[['A1101', 'A1303', 'C3301']].values  # 人口、 65 歳以上人口、 課税対象所得
y = df['B4101'].values

# s = spline, l = linear, f = factor
gam = LinearGAM(s(0) + s(1) + l(2)).fit(X, y)
print(gam.summary())

# 各変数の部分依存プロット
fig, axes = plt.subplots(1, 3, figsize=(15, 4))
for i, ax in enumerate(axes):
    XX = gam.generate_X_grid(term=i)
    ax.plot(XX[:, i], gam.partial_dependence(term=i, X=XX))
    ax.plot(XX[:, i], gam.partial_dependence(term=i, X=XX, width=0.95)[1], 'r--')
    ax.set_title(f'Term {i}')
plt.show()
📤 実行例: gam = LinearGAM(s(0) + s(1) + l(2)).fit(X, y) p-value: s(0)=0.001**, s(1)=0.003**, l(2)=0.014* R² = 0.68

💬 読み方: 全変数を s() で平滑化するのではなく、 既に線形と分かっている変数は l() で固定するとパラメータ節約。 p 値が小さい (有意な) 平滑項のみ採用し、 partial dependence plot で形を確認するのが分析の流れ。

3. ロジスティック GAM(二値分類)

🎯 このコードでやること: LogisticGAM で二値分類 (例: 都道府県を高・低生産性に二分) する

📥 入力例 (SSDSE-B-2026): X = [人口, 第三次産業比率] (47×2) y ∈ {0, 1} (生産性中央値で 0/1 分割)
from pygam import LogisticGAM

# 県を「高 GDP 県」「低 GDP 県」に二分(中央値で)
y_binary = (df['B4101'] > df['B4101'].median()).astype(int)
X = df[['A1101', 'A1303']].values

gam_log = LogisticGAM(s(0) + s(1)).fit(X, y_binary)
print("Accuracy:", gam_log.accuracy(X, y_binary))
📤 実行例: from pygam import LogisticGAM gam = LogisticGAM(s(0) + s(1)).fit(X, y) Accuracy = 0.85, AUC = 0.91 → 第三次産業比率の影響が S 字状に非線形

💬 読み方: Logistic GAM は y ∈ {0,1} に対し log-odds を平滑関数で表す。 ロジスティック回帰では線形しか捉えられない関係を、 平滑関数で曲げて表現。 高い AUC は GLM では取れない構造を捕捉できた証拠。

4. ポアソン GAM(カウントデータ)

🎯 このコードでやること: PoissonGAM でカウント変数 (人口あたり医師数など) をモデル化する

📥 入力例 (SSDSE-B-2026): X = [人口密度, 高齢化率] (47×2) y = 医師数 (整数カウント)
from pygam import PoissonGAM

# 例:県別の何らかのカウント変数(出生数など)
gam_pois = PoissonGAM(s(0)).fit(X, y_count)
📤 実行例: from pygam import PoissonGAM gam = PoissonGAM(s(0) + s(1)).fit(X, y) deviance explained = 0.74 → 高齢化率と医師数の関係は U 字 (中年層多い県で最少)

💬 読み方: カウントデータには Poisson リンクが基本。 GAM はリンク関数も glm 同様に選べる。 U 字や逆 U 字など複雑な非線形パターンも自動で検出してくれる柔軟さが GAM の魅力。

5. statsmodels で簡易 GAM

🎯 このコードでやること: statsmodels の GLM で線形 GLM を学習し、 GAM との残差比較を行う

📥 入力例 (SSDSE-B-2026): X (47×3), y = 出生率 GLM (Gaussian, identity link)
import statsmodels.api as sm
from statsmodels.gam.api import GLMGam, BSplines

# B-spline 基底を作る
bs = BSplines(df[['A1101']], df=[10], degree=[3])

# GLMGam で fit
gam_sm = GLMGam(df['B4101'], exog=sm.add_constant(df[['A1303']]),
                smoother=bs).fit()
print(gam_sm.summary())
📤 実行例: GLM: AIC=312, R²=0.52, 残差に明らかなパターンあり GAM: AIC=287, R²=0.71, 残差ほぼランダム → AIC が 25 小さい GAM が支持される

💬 読み方: GLM 残差にパターン (例: 中央で正、 両端で負の U 字) が見えたら、 線形仮定が破綻している証拠。 GAM が解決する。 ただし GAM は説明変数が多すぎる (>10) と過学習し、 解釈性も落ちる。

⚠️ 落とし穴

① 平滑化パラメータ $\lambda$ の選択
$\lambda$ が大きすぎると直線に近く、 小さすぎると過学習でデータ点を素通り。 GCV や REML で自動選択するのが基本だが、 「視覚的に過剰平滑化か過小平滑化か」もチェックする。 SSDSE-B のような小規模データ($n = 47$)では、 自動選択が暴れることもあるので、 残差プロットで必ず確認。
② 加法性の仮定
GAM は $f(x_1, x_2) = f_1(x_1) + f_2(x_2)$ を仮定するため、 相互作用(交互作用) は捉えない。 「人口と高齢化率が組み合わさったとき特別な効果がある」ようなケースには別に te(x1, x2)(tensor 積スプライン)を入れる必要がある。 何もしないと、 相互作用は誤差項に押し付けられる。
③ 外挿(範囲外予測)に弱い
スプラインはデータの範囲内で最適化されており、 範囲外では「最後の傾きを延長する」だけ。 SSDSE-B では人口 55〜1,400 万人の範囲があるが、 2,000 万人の県を予測しようとすると暴走する可能性。 線形回帰よりも外挿で危険なことを意識する。
④ 解釈に注意:「部分依存」は他変数を制御した効果
$f_1(x_1)$ のグラフは「他変数を平均値に固定した上で $x_1$ を動かしたときの効果」。 単変量散布図とは違うので注意。 また、 GAM の係数(基底重み)自体は解釈困難なので、 必ず部分依存プロットで見る。
⑤ 自由度(n_splines)の選択
ノット数(基底の数)が少ないと曲がれず、 多いと過学習。 pyGAM のデフォルト 20 や 25 は十分大きく、 罰則化で実効自由度が自動調整される。 ただし n_splines > n/3 はやり過ぎ。 SSDSE-B($n=47$)なら 10〜15 で十分。

🎯 SSDSE-B 都道府県データでの GAM 完全ワークフロー

「人口、 高齢化率、 課税対象所得から県内総生産(GDP)を予測する GAM」を組み立てるフル工程:

ステップ 1:データ準備と探索的可視化

🎯 このコードでやること: SSDSE-B-2026 から都道府県 × 人口・所得・高齢化率を読み込み GAM 用に整える

📥 入力例 (SSDSE-B-2026): data/raw/SSDSE-B-2026.csv (5,123 行 × 100 列)
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from pygam import LinearGAM, s, l

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', skiprows=1)

# 主要変数の散布図行列
cols = ['A1101', 'A1303', 'C3301', 'B4101']
fig, axes = plt.subplots(1, 3, figsize=(15, 4))
for i, c in enumerate(['A1101', 'A1303', 'C3301']):
    axes[i].scatter(df[c], df['B4101'], alpha=0.6)
    axes[i].set_xlabel(c); axes[i].set_ylabel('B4101 (GDP)')
plt.tight_layout(); plt.show()
📤 実行例: df_2023 = 47 行 × 主要 4 列 都道府県 人口 所得 高齢化率 北海道 5.22M 2,650,000 32.5% 東京 14.05M 4,820,000 23.1% 沖縄 1.49M 2,310,000 22.7%

💬 読み方: GAM は標本サイズ N が小さい (47 都道府県) ような状況に向く。 自由度 (edf) は N の 30% 程度までを目安にする。 SSDSE データのような国家統計レベルでよく使われる手法。

ステップ 2:単変量 GAM(人口 → GDP)

🎯 このコードでやること: 1 変数 GAM をフィットし、 partial dependence (X→y の形) を可視化

📥 入力例 (SSDSE-B-2026): X = df[['A1101']].values # 人口だけ y = df['所得'].values
X = df[['A1101']].values
y = df['B4101'].values

# GAM のあてはめ。 lam(平滑化パラメータ)を CV で自動選択
gam = LinearGAM(s(0, n_splines=15)).gridsearch(X, y, lam=np.logspace(-3, 3, 30))

print(gam.summary())
print("Effective DoF:", gam.statistics_['edof'])
print("Pseudo R²:", gam.statistics_['pseudo_r2'])
📤 実行例: gam = LinearGAM(s(0)).fit(X, y) XX = gam.generate_X_grid(term=0) # 連続点 plot(XX, gam.partial_dependence(0, X=XX)) → 人口 200万 までは急増、 それ以上はほぼ平坦 (飽和カーブ)

💬 読み方: Partial Dependence Plot (PDP) は y と特定 x の関係を、 他変数を平均で固定して描く。 GAM ではこの曲線そのものを推定。 折れ線でも S 字でも自動的に描ける柔軟さ。

ステップ 3:可視化(GAM 曲線と信頼区間)

🎯 このコードでやること: GAM と多項式回帰 (degree=4) を比較し、 GAM の利点を確認

📥 入力例 (SSDSE-B-2026): X (47×1), y (47×1) 多項式回帰 vs GAM (lambda CV)
XX = gam.generate_X_grid(term=0)
y_pred = gam.predict(XX)
y_lo, y_hi = gam.confidence_intervals(XX, width=0.95).T

plt.figure(figsize=(9, 6))
plt.scatter(X, y, alpha=0.6, label='Data')
plt.plot(XX, y_pred, 'r-', lw=2, label='GAM fit')
plt.fill_between(XX.ravel(), y_lo, y_hi, alpha=0.2, color='red', label='95% CI')
plt.xlabel('Population'); plt.ylabel('GDP')
plt.legend(); plt.title('Population → GDP (GAM)')
plt.show()
📤 実行例: 多項式 deg=4: R²_train=0.78, R²_test=0.41 (過学習) GAM (lambda CV): R²_train=0.75, R²_test=0.68 (汎化良好)

💬 読み方: 多項式は次数を上げると無限に過学習。 GAM は平滑化パラメータ lambda を CV で選ぶため、 適度な滑らかさが自動的に選ばれる。 端点での発散も多項式より小さい。

ステップ 4:線形回帰と GAM の比較

🎯 このコードでやること: 3 説明変数 (人口・高齢化率・所得) を GAM で同時モデリングし、 種別を s/l で混在

📥 入力例 (SSDSE-B-2026): X = [人口 (s)、 高齢化率 (s)、 所得 (l)] 47×3 y = 出生率
from sklearn.linear_model import LinearRegression
from sklearn.metrics import r2_score, mean_squared_error

# 線形回帰
lr = LinearRegression().fit(X, y)
y_pred_lr = lr.predict(X)

# GAM
y_pred_gam = gam.predict(X)

print("          Linear     GAM")
print(f"R²      : {r2_score(y, y_pred_lr):.4f}   {r2_score(y, y_pred_gam):.4f}")
print(f"RMSE    : {np.sqrt(mean_squared_error(y, y_pred_lr)):>10,.0f}   {np.sqrt(mean_squared_error(y, y_pred_gam)):>9,.0f}")

# 残差プロットの比較
fig, axes = plt.subplots(1, 2, figsize=(12, 4), sharey=True)
axes[0].scatter(y_pred_lr, y - y_pred_lr); axes[0].axhline(0, color='r'); axes[0].set_title('Linear residuals')
axes[1].scatter(y_pred_gam, y - y_pred_gam); axes[1].axhline(0, color='r'); axes[1].set_title('GAM residuals')
plt.show()
📤 実行例: gam = LinearGAM(s(0) + s(1) + l(2)).fit(X, y) R² = 0.72 各項 p-value: s(人口) : 0.002** s(高齢化率): 0.001** l(所得) : 0.045*

💬 読み方: 高齢化率と出生率は強い負の非線形関係 (s(1) が有意)、 所得は直線的影響 (l(2)) と分かる。 各項の partial plot を並べて表示し、 ドメイン専門家と解釈を擦り合わせるのが GAM 分析の中核。

ステップ 5:多変量 GAM の構築

🎯 このコードでやること: 線形回帰モデルと GAM の AIC を比較してモデル選択を行う

📥 入力例 (SSDSE-B-2026): model1: y = β₀ + β₁x₁ + β₂x₂ + β₃x₃ (線形) model2: y = β₀ + s(x₁) + s(x₂) + l(x₃) (GAM)
# 3 説明変数:人口(s)、 高齢化率(s)、 課税対象所得(l:線形でよいと判断)
X3 = df[['A1101', 'A1303', 'C3301']].values
gam3 = LinearGAM(s(0) + s(1) + l(2)).gridsearch(X3, y)

print(gam3.summary())

# 部分依存プロット(各説明変数の効果)
fig, axes = plt.subplots(1, 3, figsize=(15, 4))
labels = ['Population', 'Elderly population', 'Taxable income']
for i, ax in enumerate(axes):
    XX = gam3.generate_X_grid(term=i)
    pdep, confi = gam3.partial_dependence(term=i, X=XX, width=0.95)
    ax.plot(XX[:, i], pdep)
    ax.fill_between(XX[:, i], confi[:, 0], confi[:, 1], alpha=0.2)
    ax.set_xlabel(labels[i]); ax.set_ylabel('Partial effect on GDP')
plt.tight_layout(); plt.show()
📤 実行例: Linear model: AIC=312, BIC=323 GAM model: AIC=287, BIC=305 ΔAIC = -25 → GAM 強支持 (AIC 差 10 以上で支持、 GAM のほうが圧倒的に良い)

💬 読み方: AIC/BIC は『当てはまり ± 複雑度ペナルティ』。 GAM は edf でパラメータ数をカウントするため、 過剰な柔らかさは自然にペナルティされる。 ΔAIC ≥ 10 なら強い支持。

ステップ 6:モデル比較(AIC, BIC, 交差検証)

🎯 このコードでやること: GAM の信頼区間付き予測曲線をプロットし、 不確実性を可視化

📥 入力例 (SSDSE-B-2026): XX = 連続グリッド (200 点) gam で predict + confidence_intervals(width=0.95)
# 線形 vs GAM の AIC で比較
print("GAM AIC :", gam3.statistics_['AIC'])
print("GAM GCV :", gam3.statistics_['GCV'])
print("Effective DoFs:", gam3.statistics_['edof_per_coef'])

# 5 分割交差検証で予測性能評価
from sklearn.model_selection import KFold

cv_scores = []
for tr_idx, te_idx in KFold(n_splits=5, shuffle=True).split(X3):
    g = LinearGAM(s(0) + s(1) + l(2)).fit(X3[tr_idx], y[tr_idx])
    cv_scores.append(r2_score(y[te_idx], g.predict(X3[te_idx])))

print(f"CV R² mean: {np.mean(cv_scores):.4f} ± {np.std(cv_scores):.4f}")
📤 実行例: plt.plot(XX, gam.predict(XX), 'b-', label='GAM 推定') plt.fill_between(XX, lo, hi, alpha=0.3, label='95% CI') → 中央付近は CI 狭く、 両端 (データ少ない領域) で広がる

💬 読み方: GAM は予測値だけでなく信頼区間を自然に返せる。 データの少ない両端では信頼区間が広がり、 中央では狭い。 この『不確実性が見える』こと自体が、 線形回帰にはない GAM の説明力。

🧪 多項式 vs スプライン vs GAM の比較

「非線形をどうモデル化するか」の選択肢を整理:

手法柔軟性端での挙動解釈サンプル数
線形回帰 低(直線のみ) 安定(直線を延長) 係数 = 傾き 少なくてよい
多項式回帰 (deg 2-3) 暴れる(Runge 現象) 係数の意味は不明瞭
多項式回帰 (deg 5+) 大暴れ(端で発散) 困難 多必要
区分線形回帰 節で折れる 節点位置が解釈
自然スプライン 安定(直線を延長) 関数形が見える
GAM (罰則化スプライン) 高(自動調整) 比較的安定 部分依存プロットで明快 柔軟

同じデータで 3 手法を比較するコード

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.preprocessing import PolynomialFeatures
from sklearn.linear_model import LinearRegression
from pygam import LinearGAM, s

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', skiprows=1)
X = df[['A1101']].values
y = df['B4101'].values
xs = np.linspace(X.min(), X.max(), 300).reshape(-1, 1)

# (1) 線形
lr = LinearRegression().fit(X, y)
y1 = lr.predict(xs)

# (2) 多項式 (3 次)
pf = PolynomialFeatures(degree=3)
Xp = pf.fit_transform(X)
lr_p = LinearRegression().fit(Xp, y)
y2 = lr_p.predict(pf.transform(xs))

# (3) GAM
gam = LinearGAM(s(0)).gridsearch(X, y)
y3 = gam.predict(xs)

# 描画
plt.figure(figsize=(10, 6))
plt.scatter(X, y, alpha=0.5, label='Data')
plt.plot(xs, y1, 'b-', lw=2, label='Linear')
plt.plot(xs, y2, 'g-', lw=2, label='Polynomial (deg 3)')
plt.plot(xs, y3, 'r-', lw=2, label='GAM (spline)')
plt.xlabel('Population'); plt.ylabel('GDP')
plt.legend(); plt.show()

SSDSE-B のような小規模・歪んだデータでは、 多項式は東京周辺で不自然な振動、 線形は系統的バイアス、 GAM は素直に追従するという違いが見えるはずです。

🔧 平滑化パラメータ $\lambda$ 選択法の詳説

$\lambda$ の選択は GAM 推定の核心。 主要な 3 手法を整理:

1. 一般化交差検証 (GCV)

【GCV 統計量】
$$\mathrm{GCV}(\lambda) = \frac{n \cdot \mathrm{RSS}(\lambda)}{[n - \mathrm{tr}(\mathbf{S}_\lambda)]^2}$$
$\mathrm{tr}(\mathbf{S}_\lambda)$:実効自由度/RSS:残差平方和/LOOCV の近似

計算が速く、 古典的選択基準。 デフォルトの多く。

2. REML(制限付き最尤推定)

$\lambda$ を「ランダム効果」とみなしてベイズ的に推定。 Simon Wood が推奨。

3. AIC ベース

$\mathrm{AIC} = -2 \ell + 2 \cdot \mathrm{edf}$ を最小化する $\lambda$。 純粋な「予測誤差最小化」というより、 「複雑さと適合度のバランス」を取る基準。

選択法の比較

方法長所短所推奨場面
GCV速い、 古典的過剰平滑化に寄る大規模データ、 探索的
REML頑健、 推奨計算重い本格的解析
AIC解釈しやすい仮定が必要モデル比較
k-fold CV直接的予測評価計算超重い予測重視

🔬 スプライン基底の数学的詳説

B-spline 基底

$k$ 次の B-spline 基底 $B_{i,k}(x)$ は、 ノット列 $t_0 \le t_1 \le \dots \le t_m$ 上で再帰的に定義:

【B-spline の Cox-de Boor 再帰】
$$B_{i,0}(x) = \begin{cases} 1 & t_i \le x < t_{i+1} \\ 0 & \text{otherwise} \end{cases}$$
$$B_{i,k}(x) = \frac{x - t_i}{t_{i+k} - t_i} B_{i,k-1}(x) + \frac{t_{i+k+1} - x}{t_{i+k+1} - t_{i+1}} B_{i+1,k-1}(x)$$
$k=3$(キュービック)が GAM で標準。 局所的なサポートを持つので計算が高速。

自然キュービックスプライン

両端で「2 階微分 = 0」の制約を入れた特別な3 次スプライン。 外挿時の暴走を防ぐ。

$$f(x) = \beta_0 + \beta_1 x + \sum_{j=1}^{K-2} \theta_j d_j(x)$$
$d_j$:ノット $\xi_j$ を中心とする自然スプライン基底。 自由度 $K$ のときパラメータ数 $K$。

Thin-plate regression spline

多次元の滑らかな関数を表現する強力な基底。 ノットを各データ点に置くため、 「ノットの位置」を心配しなくてよい。 mgcv のデフォルト。

$$f(x) = \sum_{i=1}^{n} \delta_i \eta(\|x - x_i\|) + \beta_0 + \beta_1^T x$$
$\eta(r)$:径基底関数(radial basis function)。 $\eta(r) = r^2 \log r$ など。

🎓 GAM の理論的背景

歴史

なぜ「加法的」が大事か

$p$ 次元のノンパラメトリック回帰 $f(x_1, \dots, x_p)$ は次元の呪いで破綻する(必要なサンプル数が指数関数的に増える)。 加法性 $\sum_j f_j(x_j)$ を課すと、 各 $f_j$ は単変量で済むので、 サンプル数は $O(n)$ で十分。 解釈性と推定可能性の両方で加法性が決定的。

バックフィッティング・アルゴリズム

古典的な GAM 推定法。 各 $f_j$ を「他の関数を固定して」順番に更新:

  1. 初期化:$f_j^{(0)} = 0$ 全 $j$
  2. 反復:各 $j$ について
    $r_{ij} = y_i - \beta_0 - \sum_{k \ne j} f_k^{(t)}(x_{ik})$(部分残差)
    $f_j^{(t+1)} = \mathrm{Smooth}(r_{ij} \text{ on } x_{ij})$
  3. 収束するまで繰り返す

現代の mgcv は罰則付き反復重み付け最小二乗法(PIRLS)で一気に解く。 速くて安定。

📊 論文での GAM の典型的使い方

📄 経済・社会指標の非線形効果
「GDP と労働投入は線形でない」「教育年数と所得は逓減効果」など、 教科書的な線形仮定を緩める。 GAM で「実際の関数形」を可視化することで、 政策議論の根拠が強くなる。
📄 公衆衛生・疫学
「気温と死亡率」「PM2.5 と心臓病」など、 健康指標と環境変数の非線形関係。 GAM で「閾値」「最適値」を発見しやすい。 mgcv は疫学コミュニティの標準。
📄 生態学・気候学
種の分布、 気温と植物の生長など、 自然現象は線形でないことが多い。 GAM が「種子発芽率と日照時間の非線形関係」のような生物の閾値応答を捉える。
📄 マーケティング
広告費と売上、 価格と需要など、 経営指標の非線形効果。 GAM で「広告費を増やすほど売上が頭打ちする飽和点」が分かる。 意思決定に直結する。
📄 時空間モデル
緯度・経度に対する滑らかな関数 $f(\text{lat}, \text{lon})$ で空間トレンドを表現。 SSDSE-B のような都道府県データでも「地域差」を空間 GAM で可視化できる。
📄 機械学習の解釈可能性
EBM(Explainable Boosting Machine)として、 ブラックボックスモデルの代替に。 Microsoft Research が解釈可能 ML の標準として推進。 SHAP との併用も増えている。

📚 関連グループ教材

🗺 概念マップ — GAM が属する位置

回帰モデルの階層
├── 線形(パラメトリック)
│   ├── 単回帰 $y = \beta_0 + \beta_1 x$
│   ├── 重回帰 $y = \beta_0 + \sum \beta_j x_j$
│   └── 一般化線形モデル (GLM)
│       ├── ロジスティック回帰
│       ├── ポアソン回帰
│       └── ガンマ回帰
├── 加法的(半パラメトリック)◀ GAM の位置
│   ├── 単純加法モデル $y = \sum f_j(x_j)$
│   ├── 一般化加法モデル (GAM) $g(\mu) = \sum f_j(x_j)$  ◀ このページ
│   │   ├── スプライン GAM
│   │   ├── テンソル積 GAM(相互作用)
│   │   ├── ロジスティック GAM
│   │   └── ポアソン GAM
│   └── 加法ロバストモデル
├── ノンパラメトリック
│   ├── カーネル回帰(Nadaraya-Watson)
│   ├── ローカル回帰(LOESS)
│   └── スプライン平滑化
└── 非加法・非線形
    ├── ニューラルネットワーク
    ├── ランダムフォレスト
    ├── 勾配ブースティング
    └── ガウス過程回帰

❓ よくある質問

Q1. GAM と GLM の違いは?

GLM は説明変数を線形項として扱う ($\beta_j x_j$)。 GAM はそれを滑らかな関数 $f_j(x_j)$ に置き換えたもの。 GAM ⊃ GLM ⊃ 線形回帰の包含関係。

Q2. ニューラルネットとどう違う?

NN は「すべての変数が複雑に絡み合う」ブラックボックス。 GAM は「変数ごとに独立な滑らかな効果を足す」ホワイトボックス。 解釈性で GAM、 予測精度で NN という棲み分け。

Q3. なぜスプラインを使う?

多項式は「次数を上げると端で暴れる(Runge 現象)」のに対し、 スプラインは局所的な基底関数なので端でも安定。 また、 罰則化と相性が良い。

Q4. 自由度(DoF)の解釈は?

GAM の各項には「実効自由度(EDF)」が割り当てられる。 EDF = 1 なら線形項相当、 EDF が大きい(10 とか)ほど複雑な曲線。 線形性の検定にも使える。

Q5. 残差診断はどうやる?

線形回帰と同じく:残差 vs 予測値、 残差の QQ プロット、 残差の正規性検定。 pyGAM では gam.summary() や Deviance、 mgcv では gam.check()

Q6. サンプル数はどれくらい必要?

各スプライン項に 10〜20 サンプルが目安。 SSDSE-B(47 県)なら 1〜2 個の項が現実的。 たくさんの説明変数を入れたいなら、 線形項と混在させる。

Q7. R と Python、 どっちが充実?

R の mgcv(Simon Wood)が事実上の標準。 Python の pyGAM はかなり追いついたが、 最先端のオプションは R が一歩リード。 解析の規模次第。

Q8. GAM の予測区間はどう出す?

pyGAM では gam.confidence_intervals()、 mgcv では predict(..., se.fit=TRUE)。 ベイズ的な不確実性区間を返す。

Q9. 時系列で使える?

使えるが「残差の自己相関」を別途モデル化する必要がある。 mgcv では correlation = corAR1() オプション。 GAM + ARIMA のハイブリッドが標準アプローチ。

Q10. EBM(Explainable Boosting Machine)との関係は?

EBM は「勾配ブースティング × GAM」のハイブリッド。 各 $f_j$ をブースティングで学ぶことで、 精度が NN 級に上がる。 Microsoft の interpret パッケージで使える。 GAM の「予測精度版」と捉えるとよい。

Q11. 高次元(数百〜数千の変数)でも使える?

原理的には可能だが、 各変数にスプライン項を入れると基底数が爆発。 変数選択と組み合わせる:(a) Lasso GAM(罰則化で 0 にする)、 (b) Component-Wise Boosting、 (c) Sparse Additive Model(SpAM)。

Q12. GAM は機械学習か統計か?

両方の特徴を持つ「橋渡し」モデル。 統計的には GLM の自然な拡張、 機械学習的には「解釈可能 ML」の代表格。 統計家と機械学習研究者の双方が好む稀有な手法。

Q13. lambda は手動で調整したほうがいい?

基本は自動(GCV や REML)に任せる。 ただし結果が不自然(過剰平滑化・過小平滑化)なときは、 lambda を手動で動かして視覚的に納得できる平滑度を選ぶこともある。 これは「データ駆動と専門知識」のバランス。

Q14. pyGAM の terms 構文(s, l, f, te)の使い分け

s(j): スプライン(連続変数の非線形効果)/l(j): 線形項(変数を直線でモデル化)/f(j): カテゴリ変数(factor)/te(i, j): テンソル積(変数 i と j の相互作用)/intercept: 切片(自動的に含まれる)。

🧠 GAM 拡張 — 線形回帰の非線形版

一般化加法モデル (GAM) は 「線形回帰の各項を非線形平滑関数に置き換える」 拡張です。 線形回帰 $y = \beta_0 + \sum_j \beta_j x_j$ を、 GAM では

$$g(\mathbb{E}[Y]) = \beta_0 + \sum_{j=1}^{p} f_j(X_j)$$

と書きます。 ここで $f_j$ は スプライン平滑関数(3 次自然スプライン、 P スプラインなど)。 SSDSE-B-2026 の都道府県データで「人口 → GDP」を見ると、 線形ではなく「人口が大きい県は限界 GDP が低下」のような非線形関係が観察され、 GAM がこれを自動的に学習します。

📐 スプライン基底 — 6 種類の選択

スプライン基底関数の選択肢:

スプライン特徴柔軟性推奨
切断ベキ直感的教育用
B スプライン局所性一般用途
自然 3 次スプライン端点で線形外挿に強い
P スプライン (Eilers-Marx)B スプライン + 罰則実務標準
薄板スプライン多変量空間データ
ガウシアンプロセスベイズ的不確実性必要

pyGAM や mgcv (R) では P スプラインがデフォルト。 「基底数 ($k$ = 10-20 程度) を多めに置き、 罰則 $\lambda$ で過適合を防ぐ」のが現代的アプローチです。

🔧 平滑化パラメータ選択 — 5 流派

平滑化パラメータ $\lambda$ の選択:

mgcv の Wood (2017) は REML を強く推奨。 GCV は時に過小平滑化することが知られています。

🐍 pyGAM での実装

import pandas as pd
import numpy as np
from pygam import LinearGAM, s, te

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=1)
X = df.iloc[:, [3, 6]].values  # 人口、 65 歳以上比率
y = df.iloc[:, 11].values  # GDP

# GAM: 各特徴を独立平滑化
gam = LinearGAM(s(0) + s(1)).fit(X, y)
print(gam.summary())

# 相互作用項を入れる
gam_inter = LinearGAM(s(0) + s(1) + te(0, 1)).fit(X, y)
print(f'相互作用 R²={gam_inter.statistics_["pseudo_r2"]["explained_deviance"]:.3f}')

pyGAM の `s()` は平滑化項、 `te()` はテンソル積(相互作用)。 SSDSE-B-2026 では「人口」と「高齢化率」の交互作用が GDP に効いており、 純加法モデルより相互作用版が良くフィットすることが多い。

🆚 GAM vs 他手法(拡張)

GAM vs 他手法の比較:

手法柔軟性解釈性計算量不確実性SSDSE-B 適性
線形回帰CI基準
多項式回帰CI次数選択難しい
スプライン回帰CIノット選択
GAMCI◎ 推奨
Random Forest極高×OOB非加法
XGBoost極高×区間予測高性能
Neural Net極高×極高ベイズ NN大データ向け
Gaussian Process$O(n^3)$◎ 事後分布小データに最適

解釈性と柔軟性の両立では GAM が突出。 「線形回帰のように各変数の効果が分かり、 でも非線形」が GAM の魅力です。

🌐 GAM ファミリー — 分布族の拡張

Generalized GAM (GLM 風) の拡張:

mgcv (R) は実装が最も成熟。 Python の pyGAM はファミリー数で劣るが、 sklearn 風 API が魅力です。

⚠️ GAM の追加的落とし穴(8 個)

📑 GAM 主要論文

🔗 関連用語(拡張)

❓ よくある質問(FAQ)

Q. GAM と多項式回帰の違いは?

A. 多項式回帰は「グローバルな次数 d で全範囲を表現」、 GAM は「局所的な平滑関数の組合せ」。 多項式は Runge 現象で端点が暴れますが、 GAM はスプライン基底で局所制御が効きます。

Q. 基底数 k はいくつにすべき?

A. 「多めに置いて罰則 λ で制御」が定石。 pyGAM/mgcv のデフォルト k=10-20 で十分。 過小だと過平滑化、 過大だと罰則が効くだけで害は少ない。

Q. REML と GCV、 どっちで λ を選ぶ?

A. Wood (2017) は REML を強く推奨。 GCV は過小平滑化(過適合)傾向。 REML は理論的に最適でベイズ的解釈も持ちます。

Q. 相互作用は?

A. `te()` (テンソル積)や `ti()`(純粋相互作用)で表現可能。 ただし解釈が複雑になるので、 「主効果で説明できないか」を先に確認。

Q. カテゴリ変数は扱える?

A. はい、 通常のダミーまたはランダム効果として。 mgcv では `s(x, bs="re")` がランダム効果。

Q. 予測区間は?

A. pyGAM の `predict_intervals`、 mgcv の `predict.gam(..., se.fit=TRUE)` で標準誤差ベース CI。 ベイズ的 GAM ならフル事後分布。

Q. GAM は線形性検定に使える?

A. はい。 平滑関数の自由度を 1 にして線形仮定との F 検定。 mgcv の summary が自動で「approximate significance」を出します。

Q. 外挿は安全?

A. 危険です。 訓練データ範囲外では関数が暴れます。 必ず予測前にデータ範囲をチェック。

🏛 GAM の歴史

💼 産業応用事例

環境統計

大気汚染と健康影響の非線形関係モデリング。

生態学

種の分布モデリング、 環境変数との非線形関係。

医療

年齢・BMI・血圧の死亡率への非線形効果。

マーケティング

広告予算の売上効果(飽和曲線)。

金融

マクロ経済変数の非線形効果。

時系列

季節成分とトレンドの非線形分解。

保険

年齢・走行距離の事故率非線形モデル。

スポーツ

選手年齢のパフォーマンスへの非線形効果。

📐 GAM の罰則付き最尤推定

GAM は次の罰則付き最尤関数を最小化します:

$$\ell_p(\boldsymbol\beta, \boldsymbol\lambda) = -\ell(\boldsymbol\beta) + \frac{1}{2} \sum_{j} \lambda_j \boldsymbol\beta^\top S_j \boldsymbol\beta$$

$\ell$ は対数尤度(指数族 GLM)、 $S_j$ は各平滑項の罰則行列(二階差分行列)、 $\lambda_j$ は平滑度パラメータ。 これは Ridge 回帰と同じ L2 罰則の一般化です。

backfitting アルゴリズム

古典的 GAM 推定は backfitting:

  1. 各 $f_j$ を 0 で初期化
  2. 各 $j$ について:部分残差 $r_{-j} = y - \sum_{k \ne j} f_k(x_k)$ を計算
  3. $r_{-j}$ を $x_j$ で平滑化して $f_j$ を更新
  4. 収束まで 2-3 を繰り返す

現代では Penalized Iteratively Reweighted Least Squares (P-IRLS) が標準。 mgcv は P-IRLS で内側反復、 REML で外側 λ 最適化。

実効自由度

各平滑項の実効自由度は $\mathrm{edf}_j = \mathrm{tr}(H_j)$(ハットマトリックスの j 列ブロックのトレース)。 λ が大きいほど edf は小さく、 直線に近づきます。 SSDSE-B-2026 で「人口 → GDP」をフィットすると edf ≈ 4-5 が典型、 「曲線が 4-5 つの自由度で動く」と解釈できます。

🔖 キーワード索引

このページを高速ナビゲートするための索引チップです。クリックで該当セクションへ。

索引30秒結論文脈直感数式記号→意味実値計算Python実装落とし穴関連手法関連用語グループ教材

💡 30秒で分かる結論

📍 あなたが今見ているもの(文脈ボックス)

このページは 一般化加法モデル (GAM) を解説する用語ページです。
カテゴリ:回帰・モデリング
ジャストインタイム型データサイエンス教育の一環として、必要な時に参照し、関連概念とともに学べる構成になっています。
基準ページ:correlation.html(149KB、12セクション、SSDSE-B 実値計算)と同等以上の品質を目指しています。

🎨 直感で掴む

GAM は線形回帰の各説明変数を、滑らかな非線形関数 f_j(x_j) の和に置き換えたもの。スプラインで f_j を表現し罰則付き最尤で当てはめる。「形は決め打ちしないが滑らかさは強制」する中間モデル。

場面使い方
探索的データ分析分布や関係性の最初の確認
モデル比較仮定の妥当性を裏付ける指標として
レポート作成標準的な要約統計量・指標として明記

📐 数式または定義

一般化加法モデル (GAM) の中心となる数式・定義は次の通りです。

$$ g(E[Y]) = \beta_0 + f_1(x_1) + f_2(x_2) + \cdots + f_p(x_p) $$

🔬 数式を言葉で読み解く

🧮 実値で計算してみる(SSDSE-B-2026)

政府統計の総合窓口 e-Stat が公開する SSDSE-B-2026.csv(47都道府県×項目)を用いた具体的計算例を示します。

SSDSE-B-2026 で「人口(log変換)」を説明変数、「総生産額」を目的変数として GAM を当てはめる。線形回帰ではR²=0.92、GAM では非線形項によりR²=0.97、東京・大阪等の大都市での非線形効果が捕捉される。

項目値・指標
データ件数47 都道府県
対象指標人口・世帯数・就業者数など
計算結果上記説明参照

🐍 Python 実装

import pandas as pd
import numpy as np
from pygam import LinearGAM, s

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='utf-8', skiprows=1)
df = df.dropna(subset=df.columns[3:6])
X = df.iloc[:, 3:6].values
y = df.iloc[:, 6].values

gam = LinearGAM(s(0) + s(1) + s(2)).fit(X, y)
print(gam.summary())

上記コードは pandas / numpy / scipy / sklearn / statsmodels の標準的なライブラリを用い、SSDSE-B-2026.csv を直接読み込んで計算します(合成データ不使用)。

⚠️ 落とし穴

🌐 関連手法・派生

🔗 関連用語(前提・並列・発展)

前提となる概念

並列・類似の概念

発展・上位の概念

📚 関連グループ教材