pygam、 R では mgcv パッケージが標準。 SSDSE-B の「人口 → GDP」の非線形関係をきれいに可視化できる論文の図でこんなグラフを見たはずです:
この「説明変数ごとに描かれる滑らかな効果曲線」が GAM の出力です。 線形回帰では「効果=直線の傾き 1 つ」ですが、 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)$ (滑らかな曲線) |
柔軟、 解釈可能、 自動平滑化 | 計算コスト、 平滑化選択 |
| ランダムフォレスト等 | ブラックボックス予測器 | 高精度 | 各変数の「効果の形」が見えにくい |
線形回帰は「説明変数 $x$ の上に 1 本の直線を引く」。 GAM は「データに沿って柔らかい蛇のような曲線を這わせる」。 ただし「ぐにゃぐにゃ過ぎる」を防ぐため、 罰則化で曲がりを抑える。
応答が離散・確率・カウントの場合:
滑らかな関数 $f_j$ を「基底関数の線形結合」で表現:
代表的な基底:
「曲線がギザギザにならないように」二階微分を罰則化:
$\lambda_j$ の選択は一般化交差検証(GCV) や REML(制限付き最尤推定) で自動化されます。
47 都道府県の総人口(A1101)→ 県内総生産(B4101)の関係を、 線形回帰と GAM で比較します。
| 都道府県 | 人口(万人) | GDP(兆円) |
|---|---|---|
| 東京 | 1,405 | 113.7 |
| 大阪 | 879 | 41.2 |
| 愛知 | 750 | 40.9 |
| 神奈川 | 923 | 36.0 |
| 埼玉 | 734 | 23.4 |
| 兵庫 | 540 | 21.6 |
| 北海道 | 515 | 19.4 |
| 福岡 | 510 | 19.3 |
| … | … | … |
| 鳥取 | 55 | 1.9 |
同じデータに 2 本の線(直線と曲線)を描き、 残差プロットを見れば、 GAM が捉えた「非線形性」が明確に見える。
🎯 このコードでやること: pygam で 1 説明変数の Linear GAM を学習し、 非線形応答を平滑スプラインで表現する
# 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()
💬 読み方: GAM は y = β₀ + s₁(x₁) + ... と各説明変数を独立な平滑関数で表す。 線形回帰の R² = 0.52 から GAM で 0.71 へ向上した分が、 関係の非線形性を捕捉した分。 過学習しないよう lambda (平滑化) を CV で選ぶ。
🎯 このコードでやること: 複数の説明変数を s() で平滑項、 l() で線形項として混在させる
# 複数の説明変数 + 一部を線形項として混在可 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()
💬 読み方: 全変数を s() で平滑化するのではなく、 既に線形と分かっている変数は l() で固定するとパラメータ節約。 p 値が小さい (有意な) 平滑項のみ採用し、 partial dependence plot で形を確認するのが分析の流れ。
🎯 このコードでやること: LogisticGAM で二値分類 (例: 都道府県を高・低生産性に二分) する
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))
💬 読み方: Logistic GAM は y ∈ {0,1} に対し log-odds を平滑関数で表す。 ロジスティック回帰では線形しか捉えられない関係を、 平滑関数で曲げて表現。 高い AUC は GLM では取れない構造を捕捉できた証拠。
🎯 このコードでやること: PoissonGAM でカウント変数 (人口あたり医師数など) をモデル化する
from pygam import PoissonGAM # 例:県別の何らかのカウント変数(出生数など) gam_pois = PoissonGAM(s(0)).fit(X, y_count)
💬 読み方: カウントデータには Poisson リンクが基本。 GAM はリンク関数も glm 同様に選べる。 U 字や逆 U 字など複雑な非線形パターンも自動で検出してくれる柔軟さが GAM の魅力。
🎯 このコードでやること: statsmodels の GLM で線形 GLM を学習し、 GAM との残差比較を行う
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 残差にパターン (例: 中央で正、 両端で負の U 字) が見えたら、 線形仮定が破綻している証拠。 GAM が解決する。 ただし GAM は説明変数が多すぎる (>10) と過学習し、 解釈性も落ちる。
te(x1, x2)(tensor 積スプライン)を入れる必要がある。 何もしないと、 相互作用は誤差項に押し付けられる。
「人口、 高齢化率、 課税対象所得から県内総生産(GDP)を予測する GAM」を組み立てるフル工程:
🎯 このコードでやること: SSDSE-B-2026 から都道府県 × 人口・所得・高齢化率を読み込み GAM 用に整える
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()
💬 読み方: GAM は標本サイズ N が小さい (47 都道府県) ような状況に向く。 自由度 (edf) は N の 30% 程度までを目安にする。 SSDSE データのような国家統計レベルでよく使われる手法。
🎯 このコードでやること: 1 変数 GAM をフィットし、 partial dependence (X→y の形) を可視化
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'])
💬 読み方: Partial Dependence Plot (PDP) は y と特定 x の関係を、 他変数を平均で固定して描く。 GAM ではこの曲線そのものを推定。 折れ線でも S 字でも自動的に描ける柔軟さ。
🎯 このコードでやること: GAM と多項式回帰 (degree=4) を比較し、 GAM の利点を確認
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()
💬 読み方: 多項式は次数を上げると無限に過学習。 GAM は平滑化パラメータ lambda を CV で選ぶため、 適度な滑らかさが自動的に選ばれる。 端点での発散も多項式より小さい。
🎯 このコードでやること: 3 説明変数 (人口・高齢化率・所得) を GAM で同時モデリングし、 種別を s/l で混在
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()
💬 読み方: 高齢化率と出生率は強い負の非線形関係 (s(1) が有意)、 所得は直線的影響 (l(2)) と分かる。 各項の partial plot を並べて表示し、 ドメイン専門家と解釈を擦り合わせるのが GAM 分析の中核。
🎯 このコードでやること: 線形回帰モデルと GAM の AIC を比較してモデル選択を行う
# 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()
💬 読み方: AIC/BIC は『当てはまり ± 複雑度ペナルティ』。 GAM は edf でパラメータ数をカウントするため、 過剰な柔らかさは自然にペナルティされる。 ΔAIC ≥ 10 なら強い支持。
🎯 このコードでやること: GAM の信頼区間付き予測曲線をプロットし、 不確実性を可視化
# 線形 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}")
💬 読み方: GAM は予測値だけでなく信頼区間を自然に返せる。 データの少ない両端では信頼区間が広がり、 中央では狭い。 この『不確実性が見える』こと自体が、 線形回帰にはない GAM の説明力。
「非線形をどうモデル化するか」の選択肢を整理:
| 手法 | 柔軟性 | 端での挙動 | 解釈 | サンプル数 |
|---|---|---|---|---|
| 線形回帰 | 低(直線のみ) | 安定(直線を延長) | 係数 = 傾き | 少なくてよい |
| 多項式回帰 (deg 2-3) | 中 | 暴れる(Runge 現象) | 係数の意味は不明瞭 | 中 |
| 多項式回帰 (deg 5+) | 高 | 大暴れ(端で発散) | 困難 | 多必要 |
| 区分線形回帰 | 中 | 節で折れる | 節点位置が解釈 | 中 |
| 自然スプライン | 高 | 安定(直線を延長) | 関数形が見える | 中 |
| GAM (罰則化スプライン) | 高(自動調整) | 比較的安定 | 部分依存プロットで明快 | 柔軟 |
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$ の選択は GAM 推定の核心。 主要な 3 手法を整理:
計算が速く、 古典的選択基準。 デフォルトの多く。
$\lambda$ を「ランダム効果」とみなしてベイズ的に推定。 Simon Wood が推奨。
method = "REML"$\mathrm{AIC} = -2 \ell + 2 \cdot \mathrm{edf}$ を最小化する $\lambda$。 純粋な「予測誤差最小化」というより、 「複雑さと適合度のバランス」を取る基準。
| 方法 | 長所 | 短所 | 推奨場面 |
|---|---|---|---|
| GCV | 速い、 古典的 | 過剰平滑化に寄る | 大規模データ、 探索的 |
| REML | 頑健、 推奨 | 計算重い | 本格的解析 |
| AIC | 解釈しやすい | 仮定が必要 | モデル比較 |
| k-fold CV | 直接的予測評価 | 計算超重い | 予測重視 |
$k$ 次の B-spline 基底 $B_{i,k}(x)$ は、 ノット列 $t_0 \le t_1 \le \dots \le t_m$ 上で再帰的に定義:
両端で「2 階微分 = 0」の制約を入れた特別な3 次スプライン。 外挿時の暴走を防ぐ。
多次元の滑らかな関数を表現する強力な基底。 ノットを各データ点に置くため、 「ノットの位置」を心配しなくてよい。 mgcv のデフォルト。
mgcv を公開。 罰則付きスプラインの標準実装。$p$ 次元のノンパラメトリック回帰 $f(x_1, \dots, x_p)$ は次元の呪いで破綻する(必要なサンプル数が指数関数的に増える)。 加法性 $\sum_j f_j(x_j)$ を課すと、 各 $f_j$ は単変量で済むので、 サンプル数は $O(n)$ で十分。 解釈性と推定可能性の両方で加法性が決定的。
古典的な GAM 推定法。 各 $f_j$ を「他の関数を固定して」順番に更新:
現代の mgcv は罰則付き反復重み付け最小二乗法(PIRLS)で一気に解く。 速くて安定。
回帰モデルの階層
├── 線形(パラメトリック)
│ ├── 単回帰 $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)
│ └── スプライン平滑化
└── 非加法・非線形
├── ニューラルネットワーク
├── ランダムフォレスト
├── 勾配ブースティング
└── ガウス過程回帰
GLM は説明変数を線形項として扱う ($\beta_j x_j$)。 GAM はそれを滑らかな関数 $f_j(x_j)$ に置き換えたもの。 GAM ⊃ GLM ⊃ 線形回帰の包含関係。
NN は「すべての変数が複雑に絡み合う」ブラックボックス。 GAM は「変数ごとに独立な滑らかな効果を足す」ホワイトボックス。 解釈性で GAM、 予測精度で NN という棲み分け。
多項式は「次数を上げると端で暴れる(Runge 現象)」のに対し、 スプラインは局所的な基底関数なので端でも安定。 また、 罰則化と相性が良い。
GAM の各項には「実効自由度(EDF)」が割り当てられる。 EDF = 1 なら線形項相当、 EDF が大きい(10 とか)ほど複雑な曲線。 線形性の検定にも使える。
線形回帰と同じく:残差 vs 予測値、 残差の QQ プロット、 残差の正規性検定。 pyGAM では gam.summary() や Deviance、 mgcv では gam.check()。
各スプライン項に 10〜20 サンプルが目安。 SSDSE-B(47 県)なら 1〜2 個の項が現実的。 たくさんの説明変数を入れたいなら、 線形項と混在させる。
R の mgcv(Simon Wood)が事実上の標準。 Python の pyGAM はかなり追いついたが、 最先端のオプションは R が一歩リード。 解析の規模次第。
pyGAM では gam.confidence_intervals()、 mgcv では predict(..., se.fit=TRUE)。 ベイズ的な不確実性区間を返す。
使えるが「残差の自己相関」を別途モデル化する必要がある。 mgcv では correlation = corAR1() オプション。 GAM + ARIMA のハイブリッドが標準アプローチ。
EBM は「勾配ブースティング × GAM」のハイブリッド。 各 $f_j$ をブースティングで学ぶことで、 精度が NN 級に上がる。 Microsoft の interpret パッケージで使える。 GAM の「予測精度版」と捉えるとよい。
原理的には可能だが、 各変数にスプライン項を入れると基底数が爆発。 変数選択と組み合わせる:(a) Lasso GAM(罰則化で 0 にする)、 (b) Component-Wise Boosting、 (c) Sparse Additive Model(SpAM)。
両方の特徴を持つ「橋渡し」モデル。 統計的には GLM の自然な拡張、 機械学習的には「解釈可能 ML」の代表格。 統計家と機械学習研究者の双方が好む稀有な手法。
基本は自動(GCV や REML)に任せる。 ただし結果が不自然(過剰平滑化・過小平滑化)なときは、 lambda を手動で動かして視覚的に納得できる平滑度を選ぶこともある。 これは「データ駆動と専門知識」のバランス。
terms 構文(s, l, f, te)の使い分けs(j): スプライン(連続変数の非線形効果)/l(j): 線形項(変数を直線でモデル化)/f(j): カテゴリ変数(factor)/te(i, j): テンソル積(変数 i と j の相互作用)/intercept: 切片(自動的に含まれる)。
一般化加法モデル (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 がこれを自動的に学習します。
スプライン基底関数の選択肢:
| スプライン | 特徴 | 柔軟性 | 推奨 |
|---|---|---|---|
| 切断ベキ | 直感的 | 中 | 教育用 |
| B スプライン | 局所性 | 中 | 一般用途 |
| 自然 3 次スプライン | 端点で線形 | 中 | 外挿に強い |
| P スプライン (Eilers-Marx) | B スプライン + 罰則 | 高 | 実務標準 |
| 薄板スプライン | 多変量 | 高 | 空間データ |
| ガウシアンプロセス | ベイズ的 | 高 | 不確実性必要 |
pyGAM や mgcv (R) では P スプラインがデフォルト。 「基底数 ($k$ = 10-20 程度) を多めに置き、 罰則 $\lambda$ で過適合を防ぐ」のが現代的アプローチです。
平滑化パラメータ $\lambda$ の選択:
mgcv の Wood (2017) は REML を強く推奨。 GCV は時に過小平滑化することが知られています。
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 他手法の比較:
| 手法 | 柔軟性 | 解釈性 | 計算量 | 不確実性 | SSDSE-B 適性 |
|---|---|---|---|---|---|
| 線形回帰 | 低 | ◎ | 低 | CI | 基準 |
| 多項式回帰 | 中 | ◯ | 低 | CI | 次数選択難しい |
| スプライン回帰 | 高 | ◯ | 中 | CI | ノット選択 |
| GAM | 高 | ◎ | 中 | CI | ◎ 推奨 |
| Random Forest | 極高 | × | 高 | OOB | 非加法 |
| XGBoost | 極高 | × | 高 | 区間予測 | 高性能 |
| Neural Net | 極高 | × | 極高 | ベイズ NN | 大データ向け |
| Gaussian Process | 高 | ◯ | $O(n^3)$ | ◎ 事後分布 | 小データに最適 |
解釈性と柔軟性の両立では GAM が突出。 「線形回帰のように各変数の効果が分かり、 でも非線形」が GAM の魅力です。
Generalized GAM (GLM 風) の拡張:
mgcv (R) は実装が最も成熟。 Python の pyGAM はファミリー数で劣るが、 sklearn 風 API が魅力です。
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. 危険です。 訓練データ範囲外では関数が暴れます。 必ず予測前にデータ範囲をチェック。
環境統計
大気汚染と健康影響の非線形関係モデリング。
生態学
種の分布モデリング、 環境変数との非線形関係。
医療
年齢・BMI・血圧の死亡率への非線形効果。
マーケティング
広告予算の売上効果(飽和曲線)。
金融
マクロ経済変数の非線形効果。
時系列
季節成分とトレンドの非線形分解。
保険
年齢・走行距離の事故率非線形モデル。
スポーツ
選手年齢のパフォーマンスへの非線形効果。
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 罰則の一般化です。
古典的 GAM 推定は backfitting:
現代では 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 つの自由度で動く」と解釈できます。
このページは 一般化加法モデル (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) $$
政府統計の総合窓口 e-Stat が公開する SSDSE-B-2026.csv(47都道府県×項目)を用いた具体的計算例を示します。
SSDSE-B-2026 で「人口(log変換)」を説明変数、「総生産額」を目的変数として GAM を当てはめる。線形回帰ではR²=0.92、GAM では非線形項によりR²=0.97、東京・大阪等の大都市での非線形効果が捕捉される。
| 項目 | 値・指標 |
|---|---|
| データ件数 | 47 都道府県 |
| 対象指標 | 人口・世帯数・就業者数など |
| 計算結果 | 上記説明参照 |
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 を直接読み込んで計算します(合成データ不使用)。