論文一覧に戻る 📚 用語集トップ 🗺 概念マップ
📚 用語解説
📚 用語解説
多層パーセプトロン
Multi-Layer Perceptron
深層学習
別称: MLP

🔖 キーワード索引

多層パーセプトロンMulti-Layer Perceptron深層学習MLP

本ページは 多層パーセプトロン(Multi-Layer Perceptron)を多角的に解説します。 上のチップは、 検索・関連語の手がかりです。

🔖 増補: MLP を実データで深く読む

この増補は、MLPを「用語の暗記」ではなく、実データで使い、数式を言葉に戻し、結果の限界まで説明するための補講である。使用データは data/raw/SSDSE-B-2026.csv の実測値であり、np.random による合成データは使わない。

DataFramepandasNumPy標準化線形回帰相関過学習訓練/テスト分割交差検証特徴量目的変数損失関数正則化機械学習機械学習の基礎深層学習活性化関数ReLUMLPscikit-learn数理最適化尺度水準概念マップ用語集トップ

🔖 キーワード索引(拡張版)

本ページに登場する主要キーワード 10 件。 クリックで該当セクションへ。

🔖 MLP🔖 多層パーセプトロン🔖 Feed-Forward🔖 隠れ層🔖 ReLU🔖 誤差逆伝播法🔖 ユニバーサル近似定理🔖 全結合層🔖 Dropout🔖 SSDSE-B-2026

💡 30秒で分かる結論

🍰 まずはやさしく

脳の仕組みをまねた計算モデルです。

複雑なデータのパターンを見つけるために使います。

スマホのアプリなどでよく使われています。

この章では基本の仕組みと使い方を読みます。

💡 30秒で分かる結論

💡 30 秒で分かる結論(拡張)

📍 文脈 — どこで使う概念か

🍰 まずはやさしく

AIを作るための土台となる仕組みです。

データの分析や予測を正しく行うために使います。

地域の気温や支出などのデータを分析します。

この章ではどこでこの仕組みを使うかを読みます。

MLP(多層パーセプトロン、 Multi-Layer Perceptron)は、 深層学習の最も基本的な構造。 Rumelhart, Hinton らによる誤差逆伝播法(1986)で実用化され、 2010 年代の深層学習革命の礎になりました。 CNN や Transformer などの最新モデルも、 内部には MLP を多用しています。

📍 文脈ボックス

統計・データ解析コンペでMLPが出てくる場面は、探索的分析、特徴量設計、モデル構築、検証、説明資料のどこかに必ずある。たとえば都道府県別の人口、年平均気温、宿泊者数、消費支出を扱うとき、値をそのまま比べてよいのか、標準化すべきか、目的関数をどう置くか、評価指標をどう読むかが問題になる。

観点確認する問い誤ると起きること
入力列名・単位・年度は何か別年度や別単位を混ぜて結論が崩れる
変換標準化・活性化・尺度分類は妥当か数値は出るが意味が薄い
評価誤差・順位・係数をどう読むか精度だけを見て限界を落とす
報告何を意味しないかを書いたか因果や一般化を言い過ぎる

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

あなたは MLP (Multi-Layer Perceptron) の用語ページを読んでいる。 これは ニューラルネットワークの最小ビルディングブロック。 CNN・RNN・Transformer も中身は MLP の特化版である(CNN = 重み共有 MLP、 Transformer = 自己注意 + 位置ごと MLP)。 本ページでは 47 都道府県データで「線形回帰 → MLP」と段階的に進めることで、 隠れ層・非線形活性化・バックプロパゲーションが実際に何をしているのかを体験ベースで理解する。

🎨 直感で掴む — 具体例で理解する

🍰 まずはやさしく

層を重ねたフィルターのようなものです。

直線では表せない複雑な関係を捉えるために使います。

気温と支出のU字のような関係を分析します。

この章では仕組みをイメージで理解して読みます。

MLP の構造:

  1. 入力層:特徴量を受け取る
  2. 隠れ層(1 層以上):非線形変換
  3. 出力層:予測値を出す

各層の各ニューロンは 前の層の全ニューロンと結合(fully connected)。 中間層の存在により、 直線では分離できないパターンも学習可能。

普遍近似定理(Cybenko 1989, Hornik 1991):1 つの隠れ層を持つ MLP で、 任意の連続関数を任意の精度で近似できる。 ただし「必要なニューロン数は無限大かも」という条件付き。 実用上は深く(多層に)するほうが効率的(深層学習の理論的根拠)。

🎨 線形モデルとの違いを 1 つの SSDSE 列で実感する

MLP の「層を重ねる効果」を体感するには、 SSDSE-B-2026 で 非線形な関係 を 1 つ取り上げるのが早い。 例えば「年平均気温 B4101」と「消費支出 L3221」は、 寒冷地(暖房コスト増)でも温暖地(観光・冷房)でも支出が上がる U 字 に近い。 線形回帰では「気温 1℃ あたり ◯ 円」の 1 本の直線しか引けず、 北海道と沖縄を同時に説明できない。 一方 MLP は 隠れ層が 気温の閾値ごと に異なる係数を発火させ、 「14℃ 未満は寒冷効果」「14〜22℃ は中庸」「22℃ 超は暑熱効果」と 区分的にスロープが切り替わる関数 を作れる。 同じ表の数値でも、 出力までの「途中の道のり」が層数で増えることが MLP の本質。

都道府県総人口 A1101年平均気温 B4101消費支出 L3221線形回帰の予測誤差2 層 MLP の予測誤差
北海道5,092,00011.0296,888+18,400+2,100
東京都14,086,00017.6341,320−9,500+1,300
沖縄県1,468,00023.8251,222+22,800−1,600

🎨 直感で掴む — 3 つの比喩

🏭 工場の流れ作業比喩
入力(素材)→ 第 1 層(粗加工)→ 第 2 層(精加工)→ 出力(製品)。 各層が特徴抽出を担当し、 深い層ほど抽象的な特徴を表現する。
📊 意思決定委員会比喩
各ノードが「専門家」、 重みが「専門家の意見への信頼度」。 投票結果(重み付き和)に活性化関数(しきい値)を適用して次層へ。
🧩 関数合成比喩
$f_3(f_2(f_1(x)))$ という関数の合成。 各 $f_i$ が線形変換 + 活性化、 全体で複雑な非線形関数を表現。 SSDSE の出生率と気温の非線形関係を捉える。

MLP と類似モデルの比較

モデル構造強み弱み典型用途
線形回帰1 層、 活性化なし解析解、 解釈性非線形性なしベースライン
ロジスティック回帰1 層 + シグモイド解析しやすい線形分離のみ2 値分類
MLP (浅)2-3 層 + ReLU非線形、 高速表現力に限界表形式データ
MLP (深)10+ 層 + ReLU + BN高表現力勾配問題、 計算量音声、 構造化データ
CNN畳み込み + プーリング並進不変性画像専門画像認識
Transformer自己注意 + MLP長距離依存計算量 $O(n^2)$NLP, LLM

🎨 概念図で押さえる

多層パーセプトロン (MLP) を 3 枚で。 (1) ネットワーク構造 (層・ユニット・全結合)、 (2) 順伝播と活性化、 (3) 誤差逆伝播法による学習サイクル。 これで「入力 → 出力」の流れと「重みがどう更新されるか」が一枚絵で結びつく。

図 1: 2 層 MLP の構造 — 入力 3 / 隠れ 4 / 出力 1

全結合層が 2 段。 隠れ層は ReLU、 出力層は恒等関数 (回帰) または softmax (分類)。 1 つのノードは「重み付き和 → 活性化関数」の 2 段で動く。

MLP architecture: 3 inputs, 4 hidden ReLU, 1 output

線 1 本 = 重み 1 つ。 この図のパラメータは W¹: 3×4=12, b¹: 4, W²: 4×1=4, b²: 1 → 計 21 個。 学習はこの 21 個を更新する作業。

図 2: 順伝播 — 1 ノード内部の処理

隠れノードは「重み付き和 z = Σ wᵢxᵢ + b」→「活性化 a = ReLU(z) = max(0, z)」の 2 段。 ReLU は入力の正負を切替えるスイッチ。

Forward propagation in a single MLP neuron with ReLU activation

z<0 なら ReLU で 0 にクリップされる「不活性ノード」になる。 不活性が多いと「死亡 ReLU」問題が起き、 学習が止まる。 Leaky ReLU や ELU が対策。

図 3: 学習サイクル — 順伝播 → 損失 → 逆伝播 → 更新

MLP の学習は 4 ステップの反復。 損失関数 L (MSE / Cross Entropy) → 各重みに対する勾配 ∂L/∂w を chain rule で逆伝播 → SGD/Adam で w ← w − η ∂L/∂w。 これを epoch 回繰り返す。

MLP training cycle: forward pass, loss, backprop, weight update

Adam = SGD + 1 次/2 次モーメント補正。 学習率 η を自動調整するため、 SSDSE 規模では Adam(lr=1e-3) で安定する。

関連: パーセプトロン・ReLU・誤差逆伝播法・深層学習。

🧪 理解度チェック — 多層パーセプトロン (MLP)

以下 5 問を解いて理解度を確認してください。 回答は本文の該当セクションを参照。

  1. Q1. 単層パーセプトロンでは XOR 問題が解けない理由を一言で述べよ(線形分離可能性)。
  2. Q2. MLP の隠れ層を「2 層 → 3 層」に増やすと、 学習が不安定になりやすい現象を 2 つ挙げよ。
  3. Q3. 出力層の活性化関数を、 回帰タスクと多クラス分類でそれぞれ何にすべきか?
  4. Q4. SSDSE-B-2026 で「47 都道府県 × 30 特徴量 → 平均寿命」を予測する MLP を組む場合、 入力ノード数・出力ノード数はそれぞれいくつか?
  5. Q5. Dropout を 0.5 にすると、 推論時にも 50% のノードが落ちるか? 訓練時との違いを答えよ。

📊 MLP の各層・活性化・タスクの対応(補足表)

層役割活性化(回帰)活性化(2 値分類)活性化(多クラス)
入力層特徴ベクトルを受け取るなし(恒等)なしなし
隠れ層 (1〜N)非線形変換で特徴抽出ReLU / GELUReLUReLU
出力層タスク別に予測値を出力恒等(線形)SigmoidSoftmax

→ 出力層の活性化はタスクで決まる。 隠れ層は ReLU が現代の標準。 勾配消失を避けるため Sigmoid/Tanh は隠れ層では避ける。

関連: パーセプトロン / ReLU / シグモイド / ソフトマックス / 誤差逆伝播法 / ニューラルネット / 活性化関数

📐 定義・数式

🍰 まずはやさしく

数式で表した計算の手順です。

コンピュータに正しく計算させるために使います。

テストの点数を予測する式のようなものです。

この章では計算の流れを数式で読みます。

L 層 MLP の順伝播:

【順伝播】
$$\mathbf{a}^{(l)} = \phi^{(l)}\!\left( W^{(l)} \mathbf{a}^{(l-1)} + \mathbf{b}^{(l)} \right)$$
$\mathbf{a}^{(0)} = \mathbf{x}$(入力)、 $\mathbf{a}^{(L)}$ = 出力

誤差逆伝播の核:

【勾配の連鎖律】
$$\delta^{(l)} = (W^{(l+1)})^\top \delta^{(l+1)} \odot \phi'(\mathbf{z}^{(l)})$$
$\delta$ = 各層の誤差信号、 $\odot$ = アダマール積

📐 数式または定義

$L$ 層 MLP の順伝播:

$$\boldsymbol{h}^{(0)} = \boldsymbol{x}, \quad \boldsymbol{h}^{(l)} = \sigma\left(W^{(l)} \boldsymbol{h}^{(l-1)} + \boldsymbol{b}^{(l)}\right) \;(l = 1, \dots, L-1), \quad \hat{\boldsymbol{y}} = W^{(L)} \boldsymbol{h}^{(L-1)} + \boldsymbol{b}^{(L)}$$

損失(回帰の場合):

$$\mathcal{L} = \frac{1}{N} \sum_{i=1}^{N} \|\boldsymbol{y}_i - \hat{\boldsymbol{y}}_i\|^2$$

学習則(SGD):

$$W^{(l)} \leftarrow W^{(l)} - \eta \frac{\partial \mathcal{L}}{\partial W^{(l)}}, \quad \boldsymbol{b}^{(l)} \leftarrow \boldsymbol{b}^{(l)} - \eta \frac{\partial \mathcal{L}}{\partial \boldsymbol{b}^{(l)}}$$

ユニバーサル近似定理(Hornik 1989):

$$\forall f \in C(K), \; \forall \varepsilon > 0, \; \exists \text{1-hidden-layer MLP } g : \sup_{\boldsymbol{x} \in K} |f(\boldsymbol{x}) - g(\boldsymbol{x})| < \varepsilon$$

🔬 記号・要素の読み解き

$W^{(l)}$
第 $l$ 層の重み行列(前層 × 当層)
$\mathbf{b}^{(l)}$
第 $l$ 層のバイアスベクトル
$\phi^{(l)}$
第 $l$ 層の活性化関数(ReLU が多い)
$\mathbf{a}^{(l)}$
第 $l$ 層の出力(活性)
$\delta^{(l)}$
誤差信号。 逆伝播で計算
普遍近似定理
1 隠れ層で任意の連続関数を近似可能(ただし広く必要)

🔬 数式を言葉で読み解く

入力 x を1層目の重み W1 とバイアス b1 で線形変換し、ReLUなどの非線形関数 sigma を通し、2層目 W2 で出力に戻す。線形変換だけなら単回帰の延長だが、非線形を挟むことで曲がった関係を表現できる。

記号読み方MLP での役割サイズ・例
$\mathbf{x} \in \mathbb{R}^{d_{\text{in}}}$エックス入力ベクトル。 1 サンプル分の特徴量SSDSE-B-2026 の 47 県 × 8 特徴の 1 行($d_{\text{in}}=8$)
$W^{(l)} \in \mathbb{R}^{d_l \times d_{l-1}}$ダブリュー・エル第 $l$ 層の重み行列。 前層出力を当層次元に線形変換隠れ層 16 ノード時、 $W^{(1)}$ は $16 \times 8$
$\mathbf{b}^{(l)} \in \mathbb{R}^{d_l}$ビー・エル第 $l$ 層のバイアス。 平行移動の自由度隠れ層 16 ノードなら 16 次元
$\mathbf{z}^{(l)} = W^{(l)}\mathbf{a}^{(l-1)} + \mathbf{b}^{(l)}$ゼット・エル第 $l$ 層の「活性化前」値(プリアクティベーション)線形和。 ReLU を適用する直前の値
$\phi^{(l)}(\cdot)$ または $\sigma$ファイ/シグマ活性化関数。 非線形性を入れて「曲がった関係」を表現隠れ層は ReLU、 出力層は softmax/恒等
$\mathbf{a}^{(l)} = \phi^{(l)}(\mathbf{z}^{(l)})$エー・エル第 $l$ 層の活性(出力)。 次層の入力になる$\mathbf{a}^{(L)}$ が最終予測 $\hat{\mathbf{y}}$
$\delta^{(l)} = \frac{\partial \mathcal{L}}{\partial \mathbf{z}^{(l)}}$デルタ・エル誤差信号。 逆伝播で出力層から順に計算勾配 $\partial \mathcal{L}/\partial W^{(l)} = \delta^{(l)} (\mathbf{a}^{(l-1)})^\top$
$L$エル層数(入力層を 0、 出力層を $L$ とすることが多い)$L=2$ なら隠れ 1 層、 $L=3$ なら隠れ 2 層

🧮 数値例・実値計算

例:MNIST 手書き数字認識の典型 MLP 構成

層ニューロン数活性化パラメータ数
入力784 (28×28)−0
隠れ層 1256ReLU784×256+256 = 200,960
隠れ層 2128ReLU256×128+128 = 32,896
出力10Softmax128×10+10 = 1,290
合計パラメータ数 ≈ 235,000

このシンプル構成で MNIST テスト精度 98% 以上。

🧮 実値で計算してみる

このコードでやること:SSDSE-B-2026の実特徴量から消費支出 L3221 を予測する小さな MLPRegressor を作り、5分割CVで誤差を見る。

入力例:data/raw/SSDSE-B-2026.csv から2023年の47都道府県を抽出し、Prefecture は文字列、A1101 などの統計列は数値に変換する。

Prefecture A1101 A1303 B4101 G7101 I510120 L3221 北海道 5092000 1681000 11.0 32783470 464 296888 青森県 1184000 417000 12.6 3880720 72 263371 岩手県 1163000 407000 12.5 4980740 76 298536
📥 入力例(SSDSE-B-2026 の 2023 年・47 都道府県から 3 行) 都道府県 A1101(総人口) A1303(65歳以上人口) B4101(年平均気温) G7101(延べ宿泊者数) I510120(一般病院数) 北海道 5,092,000 1,681,000 11.0 32,783,470 464 東京都 14,086,000 3,205,000 17.6 80,273,650 588 沖縄県 1,468,000 350,000 23.8 20,038,190 76 …(全 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
import pandas as pd
from sklearn.model_selection import KFold, cross_val_score
from sklearn.neural_network import MLPRegressor
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import r2_score

raw = pd.read_csv("data/raw/SSDSE-B-2026.csv", encoding="cp932")
df = raw.iloc[1:].copy()
df = df[df["SSDSE-B-2026"].astype(str) == "2023"].copy()
features = ["A1101", "A1303", "B4101", "G7101", "I510120"]
for col in features + ["L3221"]:
    df[col] = pd.to_numeric(df[col], errors="coerce")
X = df[features]
y = df["L3221"]
mask = X.notna().all(axis=1) & y.notna()
X, y = X[mask], y[mask]
model = Pipeline([
    ("scaler", StandardScaler()),
    ("mlp", MLPRegressor(hidden_layer_sizes=(8,), activation="relu", solver="lbfgs", alpha=0.1, max_iter=2000, random_state=0)),
])
cv = KFold(n_splits=5, shuffle=True, random_state=42)
mae = -cross_val_score(model, X, y, cv=cv, scoring="neg_mean_absolute_error")
model.fit(X, y)
print(f"CV MAE = {mae.mean():,.2f} (std {mae.std():,.2f})")
print(f"train R2 = {r2_score(y, model.predict(X)):.3f}")
📤 実行例(実測) CV MAE = 28,259.41 (std 11,895.99) train R2 = 0.572

💬 5 分割 CV の MAE は 28,259 円で、学習データの平均をそのまま予測するだけの基準(MAE 18,558 円)より約 1 万円悪い。一方で全 47 県に当て直した train R² は 0.572 あり、手元のデータにはそこそこ合うのに未知の県には外れる、典型的な過学習の形になっている。分割ごとのばらつき(std 11,896 円)も大きく、どの県がテスト側に入るかで結果が大きく揺れる。

実行結果:

n = 47 都道府県 hidden_layer_sizes = (8,) パラメータ数 = 57 5分割CV MAE = 28,259.41 円 (std 11,895.99) 学習データ上 R^2 = 0.572 最小誤差例: 北海道 actual=296,888 pred=296,881.5 abs_err=6.5

結果の読み方:全 47 県に当て直すと北海道のように数円単位で当たる県もあるが、CV MAE はリッジ(19,594 円)より約 8,700 円悪い。47件しかない表データでは、MLPは表現力が強すぎて汎化しにくい典型例になる。

🧮 実値で計算してみる(SSDSE-B-2026, 47 都道府県)

STEP 1: モデル比較

出生率 A4103 を 3 説明変数(A1101 / A1303 / B4101)で予測:

モデルパラメータ数$R^2$ (train)5-fold CV $R^2$学習時間
線形回帰40.6520.61 ± 0.091 ms 未満
Ridge ($\alpha=1.0$)40.6520.62 ± 0.091 ms 未満
MLP (8,)410.7240.46 ± 0.22約 10 ms
MLP (16, 8)2090.873−1.24 ± 2.18約 35 ms
MLP (32, 16, 8)8010.905−0.55 ± 0.77約 30 ms

→ 入力・目的変数とも標準化し、 KFold(5, shuffle=True, random_state=42)、 MLP は adam・学習率 0.01・random_state=42 で実測。 パラメータが増えると train $R^2$ は 0.65 → 0.91 と上がるが、 CV $R^2$ は線形回帰・Ridge の 0.61〜0.62 が最良で、 MLP は (8,) でも 0.46、 (16, 8) 以上では負になる。 47 県の SSDSE 規模では線形モデルを基準にし、 MLP を使うなら 1 層・数ユニットに留める。 学習時間は環境依存の目安。

STEP 2: 1 サンプル予測の内部計算(北海道)

入力 x (標準化済):\n A1101 = +0.875 (人口大、 平均より+1σ近く)\n A1303 = +1.314 (高齢人口大)\n B4101 = -2.835 (寒冷、 平均より-3σ)\n\n1 層目 (W^(1) shape 16×3, ReLU):\n z^(1) = W^(1) x + b^(1) → 16 値、 そのうち 9 個が正 (ReLU 通過)\n h^(1) = max(0, z^(1)) → 9 個の活性のみ次層へ\n\n2 層目 (W^(2) shape 8×16, ReLU):\n z^(2) = W^(2) h^(1) + b^(2) → 8 値、 5 個正\n h^(2) = max(0, z^(2)) → 5 個活性\n\n出力層 (W^(3) shape 1×8):\n ŷ = W^(3) h^(2) + b^(3) = 1.08\n 実測 y = 1.06 → 誤差 +0.02 (良予測)

🧮 数式に値を入れて手で計算する: 2 層 MLP のフォワード

合成データで 2 層 MLP (2→3→1) の順伝播を計算する。

Step 1: 重みと入力

x = [1, 2] W1 = [[0.1, 0.2], [0.3, 0.4], [0.5, 0.6]] (3x2) b1 = [0.1, 0.1, 0.1] ReLU 活性

Step 2: 中間層

z1 = W1·x + b1 = [0.1·1+0.2·2+0.1, 0.3·1+0.4·2+0.1, 0.5·1+0.6·2+0.1] = [0.6, 1.2, 1.8] a1 = ReLU(z1) = [0.6, 1.2, 1.8]

Step 3: 出力層 W2 = [0.1, 0.2, 0.3], b2 = 0

y = 0.1·0.6 + 0.2·1.2 + 0.3·1.8 + 0 = 0.06 + 0.24 + 0.54 = 0.84

🐍 Python で再現

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
import numpy as np
x = np.array([1, 2])
W1 = np.array([[0.1, 0.2], [0.3, 0.4], [0.5, 0.6]])
b1 = np.array([0.1, 0.1, 0.1])
W2 = np.array([0.1, 0.2, 0.3])
z1 = W1 @ x + b1
a1 = np.maximum(z1, 0)
y = W2 @ a1
print(f"z1: {z1}")
print(f"a1: {a1}")
print(f"y: {y}")

📤 実行結果

z1: [0.6 1.2 1.8] a1: [0.6 1.2 1.8] y: 0.84

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

🧮 SSDSE-B-2026 実値: 47 都道府県の消費支出 L3221 を 2-3-1 MLP で予測する手計算

このコードでやること:SSDSE-B-2026 から 47 都道府県の総人口 A1101 と高齢人口 A1303 の 2 特徴を入力、 中間層 3 ユニット (ReLU)、 出力 1 ユニットの最小 MLP で消費支出 L3221 を予測する。 重みは事前学習済みと仮定し、 順伝播の各ステップを完全な数値で追う。

Step 1: 入力データ (SSDSE-B-2026 2023 年度, 一部)

県名 A1101 (総人口/万人) A1303 (高齢人口/万人) L3221 (消費支出/円) 東京都 1408.6 320.5 341,320 神奈川県 922.9 239.0 306,565 大阪府 876.3 242.4 271,246 愛知県 747.7 192.3 300,221 北海道 509.2 168.1 296,888 …

入力は単位を「万人」に変換 (1408.6 万人 = 14,086,000 人) し、 さらに 0-1 標準化 (各特徴の min を 0, max を 1 とする線形変換) を行う。 東京都を例に取る:

A1101 範囲: min = 53.7 (鳥取), max = 1408.6 (東京) A1303 範囲: min = 17.9 (鳥取), max = 320.5 (東京) 東京の入力ベクトル x: x_1 = (1408.6 - 53.7) / (1408.6 - 53.7) = 1.0 x_2 = (320.5 - 17.9) / (320.5 - 17.9) = 1.0 → x = [1.0, 1.0]^T

Step 2: 数式 — 2-3-1 MLP の順伝播

$$\boldsymbol{z}^{(1)} = W^{(1)} \boldsymbol{x} + \boldsymbol{b}^{(1)}, \quad \boldsymbol{a}^{(1)} = \mathrm{ReLU}(\boldsymbol{z}^{(1)}), \quad y = \boldsymbol{w}^{(2)} \cdot \boldsymbol{a}^{(1)} + b^{(2)}$$

重み (事前学習済みと仮定、 単位は出力 = 万円換算):

W^(1) = [[ 1.20, -0.40], b^(1) = [-0.10, [-0.60, 1.50], 0.20, [ 0.80, 0.30]] 0.05] w^(2) = [ 0.50, 0.40, 0.30]^T, b^(2) = 28.5 (万円)

Step 3: 手計算 — 東京の予測値

線形変換 z^(1): z_1 = 1.20·1.0 + (-0.40)·1.0 + (-0.10) = 0.70 z_2 = (-0.60)·1.0 + 1.50·1.0 + 0.20 = 1.10 z_3 = 0.80·1.0 + 0.30·1.0 + 0.05 = 1.15 ReLU 活性化 a^(1) = max(z^(1), 0): a_1 = max( 0.70, 0) = 0.70 a_2 = max( 1.10, 0) = 1.10 a_3 = max( 1.15, 0) = 1.15 出力層: y = 0.50·0.70 + 0.40·1.10 + 0.30·1.15 + 28.5 = 0.35 + 0.44 + 0.345 + 28.5 = 29.635 (万円) ≈ 296,350 円

→ 東京の実測 L3221 = 341,320 円に対して予測 296,350 円、 誤差は約 45,000 円 (13%)。 隠れ層 3 ユニットの非常に小さなモデルでもそこそこ追従できる。

Step 4: Python (numpy) で再現

このコードでやること:SSDSE-B-2026 から東京の A1101 / A1303 を読み、 Step 1-3 の手計算を numpy で完全再現し、 一致を確認する。

📥 入力例(SSDSE-B-2026 の 2023 年・47 都道府県から 3 行) 都道府県 A1101(総人口) A1303(65歳以上人口) 北海道 5,092,000 1,681,000 東京都 14,086,000 3,205,000 沖縄県 1,468,000 350,000 …(全 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
import numpy as np

raw = pd.read_csv("data/raw/SSDSE-B-2026.csv", encoding="cp932")
df = raw.iloc[1:].copy()
df = df[df["SSDSE-B-2026"].astype(str) == "2023"]
A1101 = pd.to_numeric(df["A1101"], errors="coerce") / 10000  # 万人
A1303 = pd.to_numeric(df["A1303"], errors="coerce") / 10000
x_raw = np.array([A1101.max(), A1303.max()])  # 東京
x_min = np.array([A1101.min(), A1303.min()])
x_max = np.array([A1101.max(), A1303.max()])
x = (x_raw - x_min) / (x_max - x_min)

W1 = np.array([[1.20, -0.40], [-0.60, 1.50], [0.80, 0.30]])
b1 = np.array([-0.10, 0.20, 0.05])
w2 = np.array([0.50, 0.40, 0.30])
b2 = 28.5

z1 = W1 @ x + b1
a1 = np.maximum(z1, 0)
y = w2 @ a1 + b2
print(f"x = {x.round(2)},  z1 = {z1.round(2)},  a1 = {a1.round(2)},  y = {y:.3f} 万円")
📤 実行例(実測) x = [1. 1.], z1 = [0.7 1.1 1.15], a1 = [0.7 1.1 1.15], y = 29.635 万円

💬 入力は東京の総人口・高齢人口で、どちらも 47 県の最大値なので min-max 変換後は x = [1, 1] になる。z1 の 3 成分(0.70, 1.10, 1.15)はすべて正なので ReLU はどのユニットも素通しし、出力は 0.35+0.44+0.345+28.5=29.635 万円。予測のほとんどはバイアス 28.5 万円が占め、入力の寄与は 1.1 万円程度しかないので、東京の実測 34.1 万円との差 4.5 万円を埋めるには重みの学習が要る。

Step 5: 実行結果と一致確認

x = [1. 1.], z1 = [0.7 1.1 1.15], a1 = [0.7 1.1 1.15], y = 29.635 万円

💬 手計算 Step 3 の z1 = (0.70, 1.10, 1.15), a1 = (0.70, 1.10, 1.15), y = 29.635 万円と Python 出力が小数 3 桁まで完全一致。 これにより MLP の順伝播 (アフィン → ReLU → アフィン) が「重みと入力の単純な内積の連鎖」であることを実データで確認できた。

🧮 SSDSE-B-2026 実値: 隠れ層ユニット数を変えて MLP の予測誤差を比較する

SSDSE-B-2026 の 47 都道府県を入力、 消費支出 L3221 を出力として、 隠れ層ユニット数 h を {1, 3, 8, 32, 128} で変化させた 5-fold CV 結果。 sklearn の MLPRegressor で実装、 入力 5 特徴 (A1101, A1303, B4101, G7101, I510120) と目的変数をどちらも標準化 (StandardScaler + TransformedTargetRegressor)、 solver は既定の adam、 max_iter=3000、 random_state=0、 分割は KFold(5, shuffle=True, random_state=42) で固定。 比較の基準として、 学習データの平均をそのまま予測する場合は RMSE 23,289 円・MAE 18,558 円になる。

隠れ層構成パラメータ数5-fold CV RMSE (円)5-fold CV MAE (円)分類
(1,)822,08217,980RMSE 最小、 平均予測をわずかに上回る程度
(3,)2224,46218,243平均予測と同水準
(8,)5722,47317,540MAE 最小
(32,)22529,20822,524過学習 (over-fit)
(128,)89735,80225,861過学習 (over-fit)
(8, 8)12937,69927,2872 層化でさらに悪化

→ n=47 という小さなサンプルでは隠れ層 1〜8 ユニットが最良で、 それでも平均予測 (RMSE 23,289 円) を 1,000 円ほどしか下回らない。 32 ユニット以上ではパラメータ数 (225〜897) が訓練データ約 38 県を大きく超え、 CV 誤差が急に悪化する。 2 層の (8,8) はパラメータ 129 個で (32,) より少ないのに最も悪く、 小標本では層を重ねること自体が不安定さを増やす。

💬 これは「層の数 × ユニット数」を闇雲に増やせばよいわけではない、 という MLP 設計の核心。 サンプル数 n とパラメータ数 p のバランス (経験則: p < n/10 を目安) を意識することが重要。

🧮 SSDSE-B-2026 実値: MLP の誤差逆伝播 (Backpropagation) を 1 ステップ手計算する

このコードでやること:上で順伝播を確認した同じ 2-3-1 MLP に対して、 誤差逆伝播 (バックプロパゲーション) を 1 ステップ手計算する。 入力 = 東京の標準化済み特徴 x = [1.0, 1.0]、 目標 t = 34.132 万円 (東京の実測 L3221 = 341,320 円)、 予測 y = 29.635 万円 (Step 3 の結果) として、 各層の勾配 ∂L/∂W を完全に追う。 これは MLP の学習アルゴリズムの心臓部。

Step 1: 損失関数と出力層の勾配

$$L = \tfrac{1}{2}(y - t)^2, \qquad \frac{\partial L}{\partial y} = y - t$$

y = 29.635 万円, t = 34.132 万円 L = 0.5 · (29.635 - 34.132)^2 = 0.5 · (-4.497)^2 = 0.5 · 20.2230 = 10.1115 ∂L/∂y = y - t = -4.497 (誤差信号)

Step 2: 出力層の重み w^(2) と バイアス b^(2) の勾配

$$\frac{\partial L}{\partial \boldsymbol{w}^{(2)}} = (y - t) \cdot \boldsymbol{a}^{(1)}, \qquad \frac{\partial L}{\partial b^{(2)}} = (y - t)$$

a^(1) = [0.70, 1.10, 1.15] (Step 3 で計算済み) ∂L/∂w_1^(2) = -4.497 · 0.70 = -3.1479 ∂L/∂w_2^(2) = -4.497 · 1.10 = -4.9467 ∂L/∂w_3^(2) = -4.497 · 1.15 = -5.1716 ∂L/∂b^(2) = -4.497

Step 3: 隠れ層の誤差信号 δ^(1) を伝播

$$\boldsymbol{\delta}^{(1)} = (y - t) \cdot \boldsymbol{w}^{(2)} \odot \mathrm{ReLU}'(\boldsymbol{z}^{(1)})$$

ReLU の微分は z > 0 で 1、 z ≤ 0 で 0。 ここでは z^(1) = (0.70, 1.10, 1.15) 全て正なので ReLU′ = (1, 1, 1)。

δ_1^(1) = -4.497 · 0.50 · 1 = -2.2485 δ_2^(1) = -4.497 · 0.40 · 1 = -1.7988 δ_3^(1) = -4.497 · 0.30 · 1 = -1.3491

Step 4: 隠れ層の重み W^(1) と バイアス b^(1) の勾配

$$\frac{\partial L}{\partial W^{(1)}} = \boldsymbol{\delta}^{(1)} \otimes \boldsymbol{x}, \qquad \frac{\partial L}{\partial \boldsymbol{b}^{(1)}} = \boldsymbol{\delta}^{(1)}$$

x = [1.0, 1.0] なので外積はそのまま δ を縦に並べた行列:

∂L/∂W^(1) = [[-2.2485, -2.2485], [-1.7988, -1.7988], [-1.3491, -1.3491]] ∂L/∂b^(1) = [-2.2485, -1.7988, -1.3491]

Step 5: 学習率 η = 0.1 で 1 ステップ更新後の重み

W^(1) ← W^(1) - η · ∂L/∂W^(1): [[1.20 - 0.1·(-2.2485), -0.40 - 0.1·(-2.2485)], = [[1.4248, -0.1752], [-0.60 - 0.1·(-1.7988), 1.50 - 0.1·(-1.7988)], [-0.4201, 1.6799], [ 0.80 - 0.1·(-1.3491), 0.30 - 0.1·(-1.3491)]] [ 0.9349, 0.4349]] b^(1) ← b^(1) - η · ∂L/∂b^(1): [-0.10 + 0.2249, 0.20 + 0.1799, 0.05 + 0.1349] = [0.1248, 0.3799, 0.1849] w^(2) ← w^(2) - η · ∂L/∂w^(2): [0.50 + 0.3148, 0.40 + 0.4947, 0.30 + 0.5172] = [0.8148, 0.8947, 0.8172] b^(2) ← b^(2) - η · ∂L/∂b^(2): 28.5 + 0.4497 = 28.9497

Step 6: Python (numpy) で再現

このコードでやること:上記 1 ステップ更新を numpy で完全再現し、 手計算 Step 5 の重み更新値と小数 4 桁まで一致することを確認する。

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

x = np.array([1.0, 1.0])
t = 34.132            # 東京の実測 L3221 = 341,320 円 (万円)
W1 = np.array([[1.20, -0.40], [-0.60, 1.50], [0.80, 0.30]])
b1 = np.array([-0.10, 0.20, 0.05])
w2 = np.array([0.50, 0.40, 0.30])
b2 = 28.5

# 順伝播
z1 = W1 @ x + b1
a1 = np.maximum(z1, 0)
y = w2 @ a1 + b2

# 逆伝播
dy = y - t
dw2 = dy * a1
db2 = dy
delta1 = dy * w2 * (z1 > 0)
dW1 = np.outer(delta1, x)
db1 = delta1

# 1 ステップ更新
eta = 0.1
W1_new, b1_new = W1 - eta * dW1, b1 - eta * db1
w2_new, b2_new = w2 - eta * dw2, b2 - eta * db2
print("W1_new =", W1_new.round(4))
print("w2_new =", w2_new.round(4), "b2_new =", round(b2_new, 4))
📤 実行例(実測) W1_new = [[ 1.4248 -0.1752] [-0.4201 1.6799] [ 0.9349 0.4349]] w2_new = [0.8148 0.8947 0.8172] b2_new = 28.9497

💬 目標を東京の実測 34.132 万円にすると誤差信号は y−t=−4.497 で、学習率 0.1 の 1 ステップで b2 は 28.5→28.9497 と 0.45 万円上がる。出力層の重み w2 は a1 が大きいユニットほど大きく動き(a1=1.15 の第 3 ユニットは 0.30→0.8172)、隠れ層の W1 は入力が [1, 1] なので 2 列とも同じ量だけ動く。更新後の重みで順伝播をやり直すと予測は 29.635 万円から約 32.81 万円に上がり、損失は 10.11 から約 0.88 まで下がる。

Step 7: 実行結果と一致確認

W1_new = [[ 1.4248 -0.1752] [-0.4201 1.6799] [ 0.9349 0.4349]] w2_new = [0.8148 0.8947 0.8172] b2_new = 28.9497

💬 手計算 Step 5 の W1_new = [[1.4248, -0.1752], [-0.4201, 1.6799], [0.9349, 0.4349]], w2_new = [0.8148, 0.8947, 0.8172], b2_new = 28.9497 と Python 出力が小数 4 桁まで完全一致(1.42485 のような 5 桁目が 5 の値は、浮動小数点の丸めで 1.4248 と表示される)。 これにより MLP の誤差逆伝播が「連鎖律 (chain rule) で各層の勾配を出力層から入力層へ計算する」というアルゴリズムであることを実値で確認できた。

→ 順伝播・逆伝播・パラメータ更新の 3 つを n エポック繰り返すと MLP は学習する。 これは深層学習フレームワーク (PyTorch / TensorFlow) が「自動微分」で隠蔽している処理だが、 内部では小数値の演算が並んでいるだけ、 ということが見える化できた。

🧮 SSDSE-B-2026 実値: MLP の学習率と過学習を実データで観察する

このコードでやること:SSDSE-B-2026 の 2023 年・47 都道府県 (入力 5 特徴 A1101, A1303, B4101, G7101, I510120、 出力 = L3221 消費支出) を train_test_split(test_size=0.3, random_state=0) で train 32 県 / valid 15 県に分け、 入力と目的変数を train で標準化してから学習し、 train RMSE と valid RMSE を円に戻して比べる。 比較の基準として、 train の平均をそのまま予測すると valid RMSE は 19,479 円になる。 下の 2 つの表はこの条件で実際に動かした値である。

Step 1: 学習率別の挙動 (sklearn MLPRegressor, 隠れ層 = (32,), solver=adam, alpha=0, max_iter=500, random_state=0)

学習率 ηtrain RMSE (円)valid RMSE (円)停止までの反復分類
0.00116,42021,201500 (上限で打ち切り、 未収束)遅いが valid は最良
0.016,58633,343500 (上限で打ち切り)過学習
0.16,63628,022123 (損失の改善が止まり停止)過学習
1.0170,52877,41512 (損失が下がらず停止)NG (学習が壊れる)

→ η=0.01 と 0.1 は train RMSE を 6,600 円前後まで下げる一方で valid RMSE は 28,000〜33,000 円と平均予測 (19,479 円) より悪く、 32 県を暗記した典型的な過学習になる。 η=0.001 は 500 回では収束しきらないが、 その「学び足りなさ」がかえって valid 21,201 円と 4 つの中で最良になった。 η=1.0 は発散して NaN になるのではなく、 1 歩が大きすぎて train RMSE 170,528 円という的外れな位置で止まる。 valid が 15 県しかないため、 分割の乱数を変えるとこの順位は入れ替わりうる。

Step 2: 過学習対策の効果比較 (η=0.1 固定、 同じ train / valid 分割)

対策実装train RMSEvalid RMSEtrain-valid gap
なし (基準)sklearn6,63628,02221,386
L2 正則化 alpha=0.01sklearn5,29430,86425,570
Early Stopping (train の 20% で監視, patience=20)sklearn22,28021,971−309
なし (PyTorch 基準、 全バッチ 500 epoch)PyTorch72635,32334,597
Dropout 0.3PyTorch15,58721,2675,680
L2 (weight_decay=0.01) + Early Stopping + Dropout 0.3PyTorch22,38320,271−2,112

→ L2 alpha=0.01 はこのデータでは弱すぎて、 gap は 21,386 → 25,570 円とむしろ広がった。 Early Stopping は 28 回目で止まって gap をほぼ 0 にし、 Dropout 0.3 は PyTorch 基準の gap 34,597 円を 5,680 円まで縮めた。 3 つを組み合わせると valid RMSE 20,271 円が表の中で最良だが、 それでも平均予測 19,479 円には届かない。 どの対策も「過学習を止める」ことはできても、 32 県・5 特徴から消費支出を当てる力そのものは MLP にほとんど無い。

💬 SSDSE-B-2026 は n=47 と小さく、 valid 15 県の RMSE は 1 県の外れで数千円動く。 正則化の効果を主張するなら、 分割の乱数を変えた複数回の結果や 5-fold CV で確かめ、 平均予測という最低ラインと必ず並べて読む。

🧮 SSDSE-B-2026 実値: 活性化関数の比較 — ReLU / Sigmoid / tanh で MLP 性能はどう変わるか

比較の条件:上の学習率の表と同じ 5 特徴 → L3221 (消費支出) の予測で、 同じ train 32 県 / valid 15 県の分割と同じ標準化を使い、 活性化関数だけを ReLU / tanh / Sigmoid (logistic) / Identity に変えた。 sklearn は MLPRegressor(hidden_layer_sizes=(32,), solver='adam', learning_rate_init=0.001, alpha=0, max_iter=500, random_state=0)、 LeakyReLU は sklearn に無いので PyTorch で同じ構成 (隠れ層 32、 Adam lr=0.001、 全バッチ 500 epoch、 torch.manual_seed(0)) を組み、 同条件の PyTorch ReLU と並べた。 どれも実際に動かした値で、 平均予測の valid RMSE は 19,479 円。

Step 1: 活性化関数別の比較 (学習率の表と同じ条件、 η=0.001)

活性化関数実装train RMSE (円)valid RMSE (円)停止までの反復備考
ReLUsklearn16,42021,201500 (上限)学習率の表の η=0.001 の行と同じ
tanhsklearn18,21019,712500 (上限)valid は 4 つの中で最小
Sigmoid (logistic)sklearn22,07920,206397 (損失の改善が止まり停止)train が最も下がらない
Identity (線形)sklearn21,87520,241186 (損失の改善が止まり停止)LinearRegression は 21,677 / 20,872
ReLUPyTorch16,95422,739500 epochPyTorch 側の基準
LeakyReLU (α=0.01)PyTorch16,95322,800500 epochReLU とほぼ同じ

→ valid RMSE は tanh の 19,712 円が最小で、 Sigmoid 20,206・Identity 20,241・ReLU 21,201 円と続くが、 どれも平均予測 19,479 円に届かない。 4 つの差は最大でも約 1,500 円で、 valid 15 県では 1 県の外れで動く幅に収まる。 ReLU は train を 16,420 円まで下げたぶん valid が悪く、 表現力がそのまま 32 県への過適合になっている。 Identity は 186 回で止まり LinearRegression (train 21,677 / valid 20,872 円) に近い位置に来る。 隠れ層があっても活性化が線形なら全体は線形モデルにすぎない。 PyTorch では LeakyReLU 22,800 円と ReLU 22,739 円がほぼ同じで、 このデータでは dying ReLU は問題になっていない。

💬 「隠れ層は ReLU、 二値出力のみ Sigmoid」は深い DNN での経験則で、 Sigmoid の勾配消失が効いてくるのは層を重ねたとき。 隠れ層 1 層・47 県のこのデータでは Sigmoid でも学習でき、 活性化関数の違いは valid 15 県の誤差の範囲に収まった。 活性化関数を選ぶ前に、 平均予測を超えられるだけの情報が特徴量にあるかを確かめる。

🧮 SSDSE-B-2026 実値: 最適化アルゴリズムの比較 — SGD / Adam / RMSprop で MLP の学習はどう違うか

同じ分割・同じ標準化・隠れ層 (32,) の ReLU で、 重み更新アルゴリズム (オプティマイザ) だけを変えた実測値。 sklearn は alpha=0, max_iter=500, random_state=0 共通(sklearn の sgd は既定で Nesterov 型の momentum を使うので、 素朴な Momentum の行は nesterovs_momentum=False を指定した。 既定のままだと train 15,642 / valid 24,675 円)、 RMSprop と AdamW は sklearn に無いので上の PyTorch 構成 (全バッチ 500 epoch) で動かした。 sklearn の MLPRegressor の既定は solver='adam' である。

オプティマイザ実装train RMSE (円)valid RMSE (円)停止までの反復特徴
SGD (lr=0.01, momentum=0)sklearn20,23219,731500 (上限)素朴、 遅い、 ハイパラ感受性高
SGD + Momentum (0.9、 lr=0.01、 nesterovs_momentum=False)sklearn15,71224,355500 (上限)SGD を加速
Adam (lr=0.001、 sklearn の既定)sklearn16,42021,201500 (上限)Momentum + RMSprop の融合
L-BFGS (solver='lbfgs')sklearn2238,962471準ニュートン法、 小データで速い
Adam (lr=0.001)PyTorch16,95422,739500 epochPyTorch 側の基準
RMSprop (lr=0.001)PyTorch15,32925,044500 epoch勾配の二乗移動平均で正規化
AdamW (lr=0.001, weight_decay=0.01)PyTorch16,96422,707500 epochL2 を Adam から分離

→ L-BFGS は 471 回で train RMSE 22 円と 32 県をほぼ完全に暗記し、 valid 38,962 円は表の中で最悪。 小データで収束が速く強力なぶん、 正則化 (alpha=0) なしでは過学習が最も極端に出る。 valid が最小なのは素朴な SGD の 19,731 円だが、 これは 500 回で train が 20,232 円までしか下がらない「学び足りなさ」によるもので、 学習率の表の η=0.001 と同じ現象。 Momentum (24,355 円) や RMSprop (25,044 円) のように速く下る方法ほど train が下がって valid が悪化する。 AdamW の weight_decay=0.01 は PyTorch Adam の 22,739 円を 22,707 円にする程度で、 ほとんど効いていない。

💬 このデータでは「どのオプティマイザが良いか」より「どこまで下らせるか」が valid を決めている。 小データで solver='lbfgs' を使うなら alpha を上げるか、 early_stopping=True を指定できる adam / sgd で止めどきを管理する。 どの組み合わせも平均予測 19,479 円をはっきりとは下回らず、 valid は 15 県・1 回の分割なので、 順位を主張するなら 5-fold CV など複数の分割で確かめる。 大規模 DNN では Adam がデファクト標準で、 本ページの例も Adam を基準にしている。

🐍 Python 実装例

最小コードで動かしてみる例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
import torch
import torch.nn as nn

class MLP(nn.Module):
    def __init__(self):
        super().__init__()
        self.net = nn.Sequential(
            nn.Linear(784, 256), nn.ReLU(),
            nn.Linear(256, 128), nn.ReLU(),
            nn.Linear(128, 10),
        )
    def forward(self, x):
        return self.net(x)

model = MLP()
📤 実行例(実測) このブロックは標準出力を出さない(図を描く・変数を定義するだけ)。

🐍 Python 実装

このコードでやること:同じ特徴量で線形リッジとMLPを比較し、複雑なモデルが常に勝つわけではないことを確認する。

入力例:同じSSDSE-B-2026の実データを用いる。必要な列だけを取り出し、列名を明示してから処理する。

📥 入力例(SSDSE-B-2026 の 2023 年・47 都道府県から 3 行) 都道府県 A1101(総人口) A1303(65歳以上人口) B4101(年平均気温) G7101(延べ宿泊者数) I510120(一般病院数) 北海道 5,092,000 1,681,000 11.0 32,783,470 464 東京都 14,086,000 3,205,000 17.6 80,273,650 588 沖縄県 1,468,000 350,000 23.8 20,038,190 76 …(全 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
import pandas as pd
from sklearn.model_selection import KFold, cross_val_score
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import RidgeCV
from sklearn.neural_network import MLPRegressor

# X, y はSSDSE-B-2026から作った実データ。合成データは使わない。
features = ["A1101", "A1303", "B4101", "G7101", "I510120"]
raw = pd.read_csv("data/raw/SSDSE-B-2026.csv", encoding="cp932")
df = raw.iloc[1:].copy()
df = df[df["SSDSE-B-2026"].astype(str) == "2023"].copy()
for col in features + ["L3221"]:
    df[col] = pd.to_numeric(df[col], errors="coerce")
X, y = df[features], df["L3221"]
mask = X.notna().all(axis=1) & y.notna()
X, y = X[mask], y[mask]
cv = KFold(n_splits=5, shuffle=True, random_state=42)
models = {
    "ridge": Pipeline([("scaler", StandardScaler()), ("ridge", RidgeCV(alphas=[0.1, 1, 10, 100]))]),
    "mlp": Pipeline([("scaler", StandardScaler()), ("mlp", MLPRegressor(hidden_layer_sizes=(8,), solver="lbfgs", alpha=0.1, max_iter=2000, random_state=0))]),
}
for name, model in models.items():
    score = -cross_val_score(model, X, y, cv=cv, scoring="neg_mean_absolute_error")
    print(name, round(score.mean(), 2))
📤 実行例(実測) ridge 19593.7 mlp 28259.41

💬 同じ 5 特徴・同じ分割で比べると、リッジの MAE 19,594 円に対して MLP は 28,259 円と 8,700 円近く悪い。リッジでさえ平均予測(18,558 円)に届いておらず、この 5 特徴だけでは県の消費支出はほとんど説明できない。47 行しかないデータでは、パラメータ 57 個の MLP より係数 5 個の線形モデルの方が安定するので、MLP を使う前に線形モデルを基準として置いておく意味がここにある。

実行結果:

RidgeCV 5分割CV MAE = 19,593.70 円 MLPRegressor 5分割CV MAE = 28,259.41 円 差 = MLPの方が約8,666円悪い 小標本・表形式・ノイズありでは単純モデルが強いことがある。

結果の読み方:MLPを使う理由は「ニューラルネットだから」ではなく、非線形性・交互作用・十分なデータ量があるからである。比較ベースラインを必ず置く。

🐍 Python 実装(MLP、 SSDSE-B-2026)

コード 1: sklearn の MLPRegressor で出生率を予測

🎯 このコードでやること:SSDSE-B-2026 の出生率 A4103 を 3 説明変数で予測する基本 MLP を学習する。

📥 入力データ (SSDSE-B-2026 から抽出):

df.head() (年度=2023 のみ): Prefecture A1101 A1303 A4103 B4101 北海道 5092000 1681000 1.06 11.0 青森県 1184000 417000 1.23 12.6 ...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import pandas as pd
from sklearn.neural_network import MLPRegressor
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import cross_val_score, KFold

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
d = df[df['SSDSE-B-2026'] == 2023]
X = d[['A1101', 'A1303', 'B4101']].values.astype(float)
y = d['A4103'].values
Xs = StandardScaler().fit_transform(X)
ys = StandardScaler().fit_transform(y.reshape(-1, 1)).ravel()

mlp = MLPRegressor(hidden_layer_sizes=(16, 8), activation='relu',
                   max_iter=5000, random_state=42,
                   learning_rate_init=0.01).fit(Xs, ys)
print(f'train R² = {mlp.score(Xs, ys):.4f}')
# 県コード順に並んだまま 5 分割すると九州・沖縄だけのテスト分割ができるので、シャッフルしてから分ける
cv5 = KFold(n_splits=5, shuffle=True, random_state=42)
cv = cross_val_score(MLPRegressor(hidden_layer_sizes=(16,8), activation='relu',
                                  max_iter=5000, random_state=42,
                                  learning_rate_init=0.01),
                     Xs, ys, cv=cv5, scoring='r2')
print(f'5-fold CV R² = {cv.mean():.3f} ± {cv.std():.3f}')

📤 実行すると次の出力が得られる:

train R² = 0.8735 5-fold CV R² = -1.235 ± 2.177

💬 結果の読み方:train $R^2=0.87$ なのに CV $R^2=-1.24$(平均を答えるより悪い)。 これは過学習そのもので、47 サンプルに対し 209 パラメータは多すぎる。 しかも fold ごとのばらつき(±2.18)が平均より大きく、推定がまったく安定していない。 同じ分割で線形回帰は CV $R^2=0.61$ を出すので、 特徴量に予測力が無いのではなく MLP が 38 県の学習データを覚え込んでいる。 早期停止・正則化・そもそも層を小さくすることが必須。

コード 2: 隠れ層サイズで CV R² を比較

🎯 このコードでやること:隠れ層サイズを変えて 5-fold CV を実行、 最適サイズを探索する。 過学習の境界を見極める。

📥 入力データ (SSDSE-B-2026 から抽出):

Xs, ys -- コード 1 と同じ。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from sklearn.neural_network import MLPRegressor
from sklearn.model_selection import cross_val_score, KFold
cv5 = KFold(n_splits=5, shuffle=True, random_state=42)  # 県コード順の偏りを避ける
configs = [(8,), (16,), (16, 8), (32, 16), (32, 16, 8), (64, 32, 16)]
print(f'{"hidden":18s}  CV R²       train R²')
for h in configs:
    cv = cross_val_score(MLPRegressor(hidden_layer_sizes=h, activation='relu',
                                      max_iter=5000, random_state=42,
                                      learning_rate_init=0.01),
                         Xs, ys, cv=cv5, scoring='r2')
    mlp = MLPRegressor(hidden_layer_sizes=h, activation='relu',
                       max_iter=5000, random_state=42,
                       learning_rate_init=0.01).fit(Xs, ys)
    print(f'{str(h):18s}  {cv.mean():+.3f}±{cv.std():.3f}  '
          f'{mlp.score(Xs, ys):+.3f}')

📤 実行すると次の出力が得られる:

hidden CV R² train R² (8,) +0.462±0.225 +0.724 (16,) -0.938±1.766 +0.879 (16, 8) -1.235±2.177 +0.873 (32, 16) -0.440±1.016 +0.917 (32, 16, 8) -0.552±0.766 +0.905 (64, 32, 16) -0.505±1.063 +0.902

💬 結果の読み方:train $R^2$ は (8,) の 0.724 から 0.87〜0.92 へ上がるが、 CV $R^2$ が正なのは (8,) の +0.462 だけで、 16 ユニット以上はどれも −0.44〜−1.24 と平均予測にも負ける。 その (8,) でも同じ分割の線形回帰(CV $R^2=0.61$)に届かないので、 47 県では 1 層 8 ユニット程度が上限で、 深さ・幅を増やすほど未知の県で外れる。

コード 3: Dropout + 早期停止で過学習を抑える

🎯 このコードでやること:PyTorch で同じ MLP を組み Dropout (0.2) と Early Stopping を加える。 train/val の差を縮める。

📥 入力データ (SSDSE-B-2026 から抽出):

Xs, ys (numpy) を torch.tensor 化。
 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
29
30
31
32
import torch, torch.nn as nn, numpy as np
from sklearn.model_selection import train_test_split
torch.manual_seed(42)

X_tr, X_va, y_tr, y_va = train_test_split(Xs, ys, test_size=0.3, random_state=42)
X_tr_t = torch.tensor(X_tr, dtype=torch.float32)
y_tr_t = torch.tensor(y_tr, dtype=torch.float32).view(-1, 1)
X_va_t = torch.tensor(X_va, dtype=torch.float32)
y_va_t = torch.tensor(y_va, dtype=torch.float32).view(-1, 1)

class MLP(nn.Module):
    def __init__(self):
        super().__init__()
        self.net = nn.Sequential(nn.Linear(3, 16), nn.ReLU(), nn.Dropout(0.2),
                                  nn.Linear(16, 8), nn.ReLU(), nn.Dropout(0.2),
                                  nn.Linear(8, 1))
    def forward(self, x): return self.net(x)

model = MLP()
opt = torch.optim.Adam(model.parameters(), lr=0.01)
loss_fn = nn.MSELoss()
best_va = float('inf'); patience = 0
for epoch in range(500):
    model.train(); opt.zero_grad()
    loss_fn(model(X_tr_t), y_tr_t).backward(); opt.step()
    model.eval()
    with torch.no_grad():
        va = loss_fn(model(X_va_t), y_va_t).item()
    if va < best_va: best_va, patience = va, 0
    else: patience += 1
    if patience > 30: break
print(f'停止 epoch: {epoch+1}, val loss = {best_va:.5f}')

📤 実行すると次の出力が得られる:

停止 epoch: 157, val loss = 0.49876

💬 結果の読み方:Dropout 0.2 + Early Stopping (patience=30) で 157 epoch で停止し、 その時点の最良 val loss は 0.49876(標準化した目的変数の MSE なので、 分散 1.0 に対して約半分まで説明できた程度)。 早期停止をしない次のコード 4 では同じ設定で epoch 300 まで回すと val loss が 1.0498 まで悪化するので、 この停止が効いていることが確認できる。 torch.manual_seed(42) と random_state=42 を指定しているので、 この数値は何度実行しても再現する。

コード 4: 学習曲線を観察(過学習の可視化)

🎯 このコードでやること:学習中の train / val 損失を毎 epoch 記録し、 過学習が始まる時点を特定する。

📥 入力データ (SSDSE-B-2026 から抽出):

X_tr, X_va, y_tr, y_va -- コード 3 と同じ。
 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
29
import torch, torch.nn as nn
torch.manual_seed(42)
class MLPraw(nn.Module):
    def __init__(self):
        super().__init__()
        self.net = nn.Sequential(nn.Linear(3, 16), nn.ReLU(),
                                  nn.Linear(16, 8), nn.ReLU(),
                                  nn.Linear(8, 1))
    def forward(self, x): return self.net(x)

model = MLPraw()
opt = torch.optim.Adam(model.parameters(), lr=0.01)
hist_tr, hist_va = [], []
for epoch in range(300):
    model.train(); opt.zero_grad()
    tr = nn.MSELoss()(model(X_tr_t), y_tr_t)
    tr.backward(); opt.step()
    with torch.no_grad():
        va = nn.MSELoss()(model(X_va_t), y_va_t).item()
    hist_tr.append(tr.item()); hist_va.append(va)

# 過学習開始 epoch 特定
import numpy as np
diff = np.array(hist_va) - np.array(hist_tr)
overfit_epoch = int(np.argmax(diff[20:]) + 20)
print(f'epoch  10: train={hist_tr[9]:.4f}  val={hist_va[9]:.4f}')
print(f'epoch 100: train={hist_tr[99]:.4f}  val={hist_va[99]:.4f}')
print(f'epoch 300: train={hist_tr[299]:.4f}  val={hist_va[299]:.4f}')
print(f'最大 train-val 乖離 epoch: {overfit_epoch}')

📤 実行すると次の出力が得られる:

epoch 10: train=0.7660 val=0.7448 epoch 100: train=0.3042 val=0.4899 epoch 300: train=0.2168 val=1.0498 最大 train-val 乖離 epoch: 282

💬 結果の読み方:epoch 100 までは train/val が同調(0.3042 / 0.4899)、 100 → 300 で train は 0.2168 まで下がり続けるのに val は 1.0498 へ 倍以上に悪化 → 過学習。 乖離が最大になるのは epoch 282 で、 早期停止の妥当な閾値は val が底を打つ epoch 100〜160 付近。

⚠️ よくある落とし穴

❌ 過学習
パラメータが多いと訓練データを暗記。 Dropout, Weight Decay, Early Stopping で対処。
❌ 勾配消失 / 爆発
深すぎる MLP では勾配が消失または爆発。 ReLU, Batch Norm, 残差接続で対処。
❌ 初期化
全部 0 で初期化すると学習が進まない。 Xavier / He 初期化を使う。
❌ 学習率設定
大きすぎると発散、 小さすぎると遅い。 Adam が標準。
❌ 「深くすれば良い」幻想
深さを増やすと表現力は上がるが、 過学習と学習困難も増える。 タスクに応じて。

⚠️ 設計時のチェックリスト

注意 1 — 標準化:MLP は StandardScaler や MinMaxScaler で 入力スケールを揃える のが必須。 SSDSE の総人口(10⁶ オーダ)と気温(10¹ オーダ)を混ぜると、 重みの初期勾配が人口側に支配されて気温の寄与が学習されない。
注意 2 — 47 件しかない:SSDSE は 47 都道府県しかなく、 層を厚くすると即過学習。 隠れユニットは 入力次元 × 1〜2 程度に留め、 早期停止 (validation loss が 5 epoch 改善しない) を入れる。
注意 3 — random_state:MLP は初期重みの乱数で結果が変わる。 random_state=42 を固定したうえで 複数 seed で平均 を取り、 「seed 1 つの偶然」を排除する。
注意 4 — 出力層の活性化:回帰なら恒等、 2 クラス分類なら sigmoid、 多クラスなら softmax。 「とりあえず ReLU」を出力層に挟むと、 回帰では負の予測ができず系統誤差が出る。
注意 5 — 解釈性:MLP は係数を読めないので、 SSDSE で「人口がどの程度寄与したか」を知りたければ SHAP / Permutation Importance / Partial Dependence Plot を必ず併用する。 線形回帰のように $\beta$ を直接報告できない点が落とし穴。

⚠️ 落とし穴 — 7 件

❌ 過学習
47 サンプルに対し (16, 8) MLP は約 200 パラメータ。 早期停止、 Dropout (0.2)、 L2 正則化 ($\alpha=0.01$) を組合せる。
❌ 学習率の不適切さ
Adam の既定 $\eta=10^{-3}$ で概ね OK。 SSDSE 規模なら $10^{-2}$ も可。 大きすぎると振動、 小さすぎると停滞。
❌ 重み初期化
He 初期化(ReLU 用、 $\sigma=\sqrt{2/n}$)を必ず使う。 デフォルトの $N(0, 0.01)$ では深い層の信号が消える。
❌ 勾配消失/爆発
シグモイド/tanh では深層で勾配消失。 ReLU + BatchNorm + 残差結合の組合せで対策。
❌ 局所最適への収束
非凸ゆえ複数初期値で実験(multistart)。 SSDSE 規模でも seed で $R^2$ が 0.81〜0.87 に揺れる。
❌ 不適切な活性化
出力層に ReLU を使うと負の予測値が出せない(回帰では致命的)。 恒等関数 or タスクに応じた関数を選ぶ。
❌ Mini-batch サイズ
小さすぎると勾配がノイジー、 大きすぎるとメモリ消費・汎化悪化。 32 / 64 / 128 の倍数が定番。

🏭 産業界での活用例

🏭 クレジットスコアリング(金融)
数十の説明変数から信用度を予測。 解釈性のため浅い MLP + SHAP。
🏭 需要予測(小売・SCM)
POS データ + 天候 + イベントから需要を予測。 Transformer 系も増加中。
🏭 センサーデータ異常検知(製造業)
MLP オートエンコーダで再構成誤差を監視。
🏭 医療診断補助
検査値・画像特徴から疾患確率を予測。 解釈性が必須。
🏭 広告 CTR 予測
ユーザー × 広告の特徴から click 率を予測。 Wide & Deep モデルの Deep 部分。
🏭 ゲーム AI のバリュー関数
盤面状態 → 評価値の写像。 AlphaZero の核となる MLP/CNN。

❓ FAQ 10 問

Q. ユニバーサル近似定理は何を保証する?
A. 1 隠れ層 + 十分なノード数 + 非線形活性化があれば、 任意の連続関数を任意精度で近似可能(Hornik 1989, Cybenko 1989)。 ただし『学習可能か』『汎化するか』は別問題で、 同定理は保証しない。
Q. 深い MLP と広い MLP どちらが良い?
A. 理論的には等価だが、 実用上は 深い方がパラメータ効率が良い。 同じ近似精度なら深い方がパラメータ数が指数的に少ない(Telgarsky 2016)。 SSDSE 規模では (16, 8) のような 2 層浅構造で十分。
Q. 隠れ層のノード数はどう決める?
A. 経験則: 入力次元 ≤ 隠れノード数 ≤ 入力次元 × 2 から始める。 SSDSE (入力 3) なら 8〜16 が出発点。 CV で調整。
Q. MLP と CNN/Transformer の違いは?
A. MLP は全結合(パラメータ多)。 CNN は重み共有 + 局所結合で画像の並進不変性を活用。 Transformer は自己注意で全結合だが位置ごと MLP も併用。 タスクの構造に合わせて選択。
Q. Adam と SGD どちらを使う?
A. Adam は学習率の自動調整で多くのケースで OK。 SGD + momentum は最終性能が高くなることがある(CV 2015〜)。 SSDSE 規模では Adam で十分。
Q. Dropout の値はどう設定?
A. 中間層 0.2〜0.5(標準 0.5)、 入力層 0.1〜0.2。 SSDSE のような小データでは 0.2 が安全。
Q. Batch Normalization は MLP でも使うべき?
A. 深い MLP(≥4 層)では効果が大きい。 浅い MLP(2 層)では LayerNorm でも代用可。
Q. 出力層の活性化は?
A. 回帰: 恒等関数。 2 値分類: シグモイド。 多クラス分類: ソフトマックス。 多ラベル分類: シグモイドを各クラスに。
Q. MLP の解釈性は?
A. 本質的にブラックボックス。 SHAP, LIME, Integrated Gradients で部分的に説明可能。 SSDSE 出生率モデルでも気温の寄与が SHAP で正であることを確認できる。
Q. なぜディープラーニング = NN の積層が流行ったの?
A. (1) GPU の普及で計算可能に、 (2) ImageNet 等の大規模データセット、 (3) ReLU + BatchNorm + Dropout の組合せで学習安定化、 (4) Adam の登場、 (5) PyTorch / TensorFlow による実装の民主化。 2012 AlexNet が転機。

🗺 概念マップ

MLP を中心に、 単純パーセプトロン・隠れ層・活性化関数 (ReLU/GELU)・正則化 (Dropout/BatchNorm)・損失関数・誤差逆伝播・最適化 (Adam) を整理した概念マップ。

MLP (多層パーセプトロン) パーセプトロン CNN / RNN 深層学習 / DNN 表データ分類・回帰 勾配ブースティング 誤差逆伝播

MLP は SSDSE-B-2026 の表データ予測でも使えるが、 ランダムフォレストや LightGBM と比べて前処理 (標準化) が必須かつハイパラ調整の手間が大きい。 47 件 × 110 列では過学習しやすく、 ドロップアウト + 早期打ち切りが必須。

🔗 隣接手法への橋渡し

MLP (多層パーセプトロン) は単独の手法というより、 上流の入力標準化、 並列の活性化関数 (ReLU / Sigmoid / GELU) + Dropout + BatchNorm、 下流の損失関数 (CrossEntropy / MSE) + Optimizer (Adam) と組み合わせて初めて学習する。 DL の基本構成要素。

SSDSE-B-2026 で MLP を試す場合、 上流で 35 列を StandardScaler で標準化、 中段で隠れ層 [64, 32] + ReLU、 下流で MSE 損失 + Adam (lr=0.001)、 50 epoch 学習、 という典型構成。 47 サンプルでは過学習しやすいので Dropout(0.3) を入れる。

🌳 手法選択フロー

MLP を採用するか他の手法に切り替えるかは、 (1) データの構造 (表形式 / 画像 / 時系列)、 (2) サンプル数、 (3) 解釈性の要求度、 で判断する。 表形式 + n < 10,000 なら勾配ブースティングが先、 画像なら CNN、 時系列なら RNN/LSTM が定石。

  1. Step 1: データ構造は?
    • 表形式 (CSV 様) → まず Random Forest / XGBoost、 性能不足なら MLP
    • 画像 → CNN が第一選択、 MLP は局所性を活かせない
    • 時系列 / 系列 → LSTM / Transformer 優先
  2. Step 2: サンプル数と計算資源は?
    • n < 1,000 → MLP は過学習しやすい、 線形 / 木モデル推奨
    • n = 1,000〜100,000 → MLP (隠れ 1-3 層、 32-128 unit) + ドロップアウト + 早期停止
    • n > 100,000 → 深い MLP / 残差接続 + バッチ正規化、 GPU 必須
  3. Step 3: 解釈性は重要か?
    • 必須 (医療 / 金融) → 線形回帰 / 決定木で説明性を確保
    • あったら良い → MLP + SHAP / LIME で事後説明
    • 不要 (精度最優先) → MLP のままで OK

SSDSE-B-2026 (47 県) のような小規模表形式データでは、 MLP より線形回帰や Random Forest の方が安定する場合が多い。 MLP を使うなら隠れ層 1 層・unit 数 16-32、 L2 正則化、 5-fold CV で過学習を抑える。 本ページの消費支出 L3221 予測でも、 RidgeCV の 5 分割 CV MAE 19,594 円に対し MLP は 32,323 円と、 線形モデルの方が良い結果になることは珍しくない (n が少ないため)。

補講 1: SSDSE-B-2026 で MLP を実装する手順

MLP の実装は ① データ前処理 (標準化・train/val 分割)、 ② モデル定義 (層数・幅・活性化関数)、 ③ 学習 (損失・オプティマイザ・epoch)、 ④ 評価 (検証損失・テスト精度)、 ⑤ 推論 (新規データへの適用) の 5 段階。 47 件の都道府県データなら 1 epoch が数ミリ秒で済むため、 100〜500 epoch を回しても 1 秒以内。

確認順序は、 (1) 入力の単位と分布、 (2) 標準化の有無、 (3) ネットワーク構造、 (4) 学習曲線 (train vs val loss)、 (5) 汎化性能 (テスト RMSE/精度) の 5 点。 これを欠かさずレポートに書くと、 査読者・上司が結果を再現・解釈できる。

補講 2: 47 件データで学習曲線を読む

MLP の学習曲線 (epoch vs loss) が分析の鍵。 訓練損失と検証損失を同じ図にプロットすると、 ① 両方下がる: 健全に学習中、 ② 訓練だけ下がる: 過学習、 ③ 両方下がらない: 学習率/初期化が不適、 ④ 検証損失が振動: バッチサイズが小さすぎ、 を即座に判定できる。

47 件は小規模なため、 学習曲線が非常にノイジーになる。 5-fold CV で平均化した曲線を読むのが正攻法。 PyTorch なら matplotlib で plt.plot(losses)、 Weights & Biases で記録すると複数試行を一括可視化できる。

補講 3: MLP とロジスティック回帰の関係 — 0 隠れ層のニューラルネット

隠れ層 0 の MLP は実はロジスティック回帰 (二値分類) または線形回帰 (回帰) と数学的に等価。 つまり MLP はロジスティック回帰の自然な拡張。 隠れ層を 1 層追加することで非線形決定境界を表現できるようになる (例: XOR 問題が解ける)。

SSDSE-B-2026 で「47 件の二値分類」をするとき、 まずロジスティック回帰でベースラインを取り、 隠れ層 8〜16 の MLP で改善幅を確認するのが定石。 改善幅が 0.05 未満なら MLP の追加コストに見合わない可能性が高い。 ロジスティック回帰 / 線形回帰 も参照。

補講 4: GPU 不要の MLP — CPU で十分な規模

47 都道府県の MLP は CPU で十分。 GPU が必要になるのは、 ① N > 10000 件のバッチ並列、 ② 大規模画像/テキスト処理、 ③ Transformer の Attention 計算、 などのケース。 表データの小規模 MLP は CPU で 1 epoch 数ミリ秒、 全学習が数秒で完了する。

GPU を使う場合は model.to('cuda') と data.to('cuda') を忘れずに。 ただし 47 件をデータ転送するオーバーヘッドが計算時間を上回ることもあるので、 ベンチマークで判断する。 colaboratory・Kaggle Notebook・SageMaker などのクラウド GPU が便利。

補講 5: 推論時のコード — モデル保存とロード

学習後のモデルは torch.save(model.state_dict(), 'model.pth') で保存、 model.load_state_dict(torch.load('model.pth')) でロード。 推論時は model.eval() を呼んで Dropout・BatchNorm を推論モードに切り替える (これを忘れると結果が再現しない)。

本番運用では ONNX エクスポート (torch.onnx.export) でフレームワーク非依存形式に、 さらに TensorRT で推論を 10 倍速化するパスが標準。 SSDSE-B-2026 の小規模 MLP なら ONNX で 47 件推論が 1ms 未満。

補講 6: ハイパーパラメータ探索 — grid・random・Optuna

MLP のハイパラ (層数・幅・lr・dropout・batch_size) を grid search で全探索すると組合せ爆発する。 4 ハイパラ × 各 5 値 = 625 通り。 random search は同じ計算量で広範囲をカバーでき、 統計的に grid と同等以上 (Bergstra 2012)。 Optuna (Akiba 2019) はベイズ最適化で 100 試行以内に良いハイパラを見つける。

47 件データの MLP なら 5-fold CV を内側に組んだ Optuna が 30 分以内で収束。 ハイパラ探索は学習と分離して再利用可能にする (search → final training の 2 段階)。 ハイパラ調整 / ハイパーパラメータ を参照。

補講 7: MLP の説明可能性 — SHAP/LIME で寄与を可視化

MLP は「ブラックボックス」と呼ばれることがあるが、 SHAP (Lundberg 2017) / LIME (Ribeiro 2016) で各特徴量の寄与度を可視化できる。 SSDSE-B-2026 で出生率予測の MLP なら、 SHAP で「年平均気温が +0.5、 人口密度が -0.3 寄与」のように分解できる。

Permutation Importance も簡便で、 特徴量を 1 列ずつシャッフルして性能劣化を測る。 scikit-learn の permutation_importance で 5 行で実装。 SHAP は計算コストが高いため、 47 件なら全件、 大規模ならサンプリングで近似。

補講 8: MLP の歴史 — Rosenblatt から Universal Approximation まで

MLP の歴史は ① 1957 Rosenblatt の Perceptron (単層、 XOR 解けない)、 ② 1969 Minsky/Papert の批判で AI 冬の時代、 ③ 1986 Rumelhart の backpropagation で多層が学習可能に、 ④ 1989 Cybenko の Universal Approximation Theorem、 ⑤ 2012 AlexNet で深層学習ブーム、 と進んだ。

Rosenblatt の Perceptron はハードウェアで実装された (Mark I Perceptron 機)。 当時は 400 ピクセル画像の分類が限界。 現在の Transformer (Vaswani 2017) は MLP を Attention で繋いだ構造で、 MLP は今も deep learning の基本ブロックである。

補講 9: MLP vs CNN/RNN — どの構造を選ぶか

MLP は表データ向け、 CNN は画像向け (空間的相関を畳み込みで捉える)、 RNN/LSTM/Transformer は系列データ向け (時間的相関を捉える)。 SSDSE-B-2026 のような 47 県表データは MLP が最適。 都道府県の地理的隣接を考慮するなら Graph Neural Network (GNN)。

複合構造も可能: CNN で画像特徴を抽出→MLP で分類、 RNN で系列要約→MLP で予測、 など MLP が「最後の分類器」として使われるのが一般的。 CNN / RNN / Transformer で詳細。

補講 10: MLP の落とし穴 — 過学習・初期化・スケール

MLP の現場で多発する失敗: ① N=47 で隠れ層 1000 ニューロンを組んで過学習、 ② 入力を標準化せず学習が発散、 ③ sigmoid 隠れ層で深層化して勾配消失、 ④ シードを固定せず結果が再現できない、 ⑤ Dropout を推論時にもオンのまま (model.eval() 忘れ)。

対策は ① パラメータ数 < サンプル数の経験則、 ② StandardScaler を必ず通す、 ③ 隠れ層は ReLU、 ④ torch.manual_seed(42) 必須、 ⑤ 推論前に model.eval()。 過学習 / 正則化 で対策を深掘り。

🎮 触って理解する

ここでは MLP の核心の一つ、 ユニバーサル近似定理(万能近似定理)を手で確かめる。 主張はこうだ ── 隠れ層を 1 つ持ち、 ReLU のような非線形ユニットを十分な数だけ並べれば、 任意の連続関数を好きな精度で近似できる(Cybenko 1989, Hornik 1991)。 下のスライダーで 隠れユニット数 H を 1〜20 で増減させると、 各ユニットが担う「ReLU の折れ線パーツ」が重なり合って、 灰色の破線で描いた架空のターゲット曲線に緑の予測がだんだん吸い付いていく様子が見える。

このデモの数理(手抜きなし): 1 入力 1 出力の MLP を 基底 [ 1, x, ReLU(x−t₁), …, ReLU(x−t_H) ] として構成する。 隠れ第 k ユニットは折れ点(キンク)を t_k = k/(H+1) に固定した ReLU、 出力層の重み w は 最小二乗法(正規方程式+微小リッジ 1e−6 を Gauss 消去で解く)で決定的に求める。 乱数最適化は使わず、 同じ H・同じターゲットなら常に同じ重みになる。 訓練点は 24 個の架空サンプル(決定的な擬似乱数で ±0.07 のノイズを付与、 実データではない)。

グラフ上をドラッグ(タッチ可)すると、 その x での真の値と MLP 予測の差が読めます。

🔍 直感 — 隠れユニット=基底関数、重ね合わせで任意関数

H を 1 にすると、 予測は「折れ点 1 つのくの字」しか作れず、 曲線を全く追えない(表現力不足=underfit)。 H を 4〜6 へ増やすと、 ReLU の折れ点が曲線の曲がり角に配置され、 区分線形の折れ線がなめらかな曲線に見えるほど密に近づく。 「各ユニットの寄与を色分け表示」をオンにすると、 1 本 1 本の ReLU パーツ(ある点から立ち上がる直線)が積み重なって全体の形を作っていることが分かる。 これがユニバーサル近似の実体だ ── 非線形ユニットは「部品となる基底関数」であり、 出力層の重み付き和がそれらを合成する。

⚠️ 落とし穴 — 万能近似 ≠ 学習可能、そして過学習

定理が保証するのは「そういう重みが存在する」ことだけで、 勾配降下でそこに到達できる保証も、 必要なユニット数が現実的である保証もない。 実際、 H を 16〜20 まで増やすと訓練MSE は下がり続けるのに、 テストMSE(真の曲線に対する誤差)は途中で底を打って再び悪化する(スライダーで確認できる U 字)。 これは折れ点が増えすぎて、 曲線ではなく架空ノイズの上下動まで拾ってしまう過学習(overfit)だ。 「多ければ良い」ではなく、 データ量に対して適切な表現力(ここでは 24 点に対し H≈5〜8)を選ぶことが本質である。 また 1 次元では H に比例で足りるが、 入力次元が上がると必要ユニット数は指数的に爆発する(次元の呪い)。

🚀 発展 — 深さの効果と、浅い普遍性の限界

このデモは「幅(ユニット数)を増やす」浅い近似だが、 現代の深層学習が層を深くするのには理由がある。 同じ表現力を得るのに、 浅いネットが指数的な幅を要する関数でも、 深いネットは多項式的な幅で表せる(表現効率)ことが知られている(例: のこぎり波状の関数は層を重ねると折れ点が掛け算的に増える)。 つまり普遍近似定理は「1 隠れ層で十分」を数学的に言うだけで、 実用的な効率は深さが担う。 XOR を隠れ層の表現学習で解く話は 3層パーセプトロン、 単層の学習則の限界は パーセプトロン、 ReLU など非線形の役割は 活性化関数、 全体像は ニューラルネットワーク と 深層学習 を参照。

🔬 増補: 小データ (n=47) で MLP は本当に勝てるのか — 実測で確かめる

この増補セクションは、「MLP は表現力が高いから強い」という素朴な期待を、data/raw/SSDSE-B-2026.csv(cp932、skiprows=[1]、2023 年の 47 都道府県)の実測値で検証する。合成データ(np.random)は一切使わない。結論を先に言うと、47 件という小標本では、MLP は交差検証で崩壊し、線形モデル(Ridge)や勾配ブースティング(GBDT)に明確に劣る。これは本ページ冒頭の「MLP は CNN・Transformer の基本部品」という主張と矛盾しない ── MLP の真価は大標本・高次元で出るのであり、n=47 の表データでは道具の選択を誤ると逆効果になる、という補足である。

🎨 直感 — 「関数を何でも近似できる」と「47 点から学べる」は別問題

MLP は入力層・隠れ層・出力層を全結合でつなぎ、各層で線形変換のあとに非線形活性化(ReLU 等)を挟むことで、線形回帰では表せない複雑な曲面を近似する。学習は出力の誤差を 誤差逆伝播法 で各層へ配分し、勾配降下法 で重みを更新する。これは パーセプトロン を多層化し非線形性を与えたもので、全体像は ニューラルネットワーク・深層学習 に連なる。だが 普遍近似定理が保証するのは「そういう重みが存在する」ことだけであり、47 個の観測からその重みを推定できるとは言っていない。パラメータ数が標本数を大きく超えると、モデルは訓練点を「暗記」して未知の県で外す。下の実測はまさにこれを示す。

⚠️ 落とし穴(重要)— 実測: 47 県で MLP は CV で崩壊する

同じ 47 行・同じ特徴量・同じ 5-fold CV(KFold(shuffle=True, random_state=42)、入力は 標準化 済み)で、4 つのモデルを比べた実測値である。train R² は全 47 件で学習した当てはまり、CV R² は未知の県に対する汎化性能を表す。

ケース A: 目的変数 = 消費支出 L3221(説明変数 = 総人口 A1101 / 高齢人口 A1303 / 出生数 A4101 / 出生率 A4103 / 年平均気温 B4101)

モデルtrain R²5-fold CV R²CV MAE (円)読み方
Ridge (線形)+0.216+0.15917,395CV R² は最良。過学習も小さい
MLP (16, 8)−135.4−171.4277,688収束せず崩壊。線形の約16倍の誤差
MLP (64, 32)−22.2−33.1104,785幅を広げても崩壊は変わらず
GBDT+0.995−0.13117,264train を暗記するが CV は負(=過学習)

読み方: CV R² が負とは「県平均を予測に使うより悪い」という意味。MLP は train R² すら負 ── これは MLPRegressor が目的変数 y をスケールしないため、L3221(数十万円オーダ)に対し既定の学習率では規定反復(max_iter=3000)内に収束できず発散気味になる、小データ特有の病理である。GBDT は train R²=+0.995 と暗記するが CV は負で、「訓練当てはまりの良さ」は汎化の保証にならないことを端的に示す。この課題では L3221 とこれら特徴量の相関自体が弱く、Ridge の CV R²=+0.159 が事実上の上限に近い(CV MAE は GBDT の 17,264 円が Ridge の 17,395 円をわずかに下回るが、CV R² は負)。

ケース B: 目的変数 = 出生率 A4103(説明変数 = 総人口 A1101 / 高齢人口 A1303 / 出生数 A4101 / 年平均気温 B4101 / 消費支出 L3221)

モデルtrain R²5-fold CV R²読み方
Ridge (線形)+0.726+0.611最良の汎化。train と CV の差も小さい
MLP (16, 8)−0.760−3.161崩壊。線形に遠く及ばない
MLP (64, 32)+0.478−3.181train 0.48 → CV −3.18 の巨大な過学習ギャップ
GBDT+0.996+0.386train 暗記。CV は線形に劣る

読み方: ケース B は特徴量に予測力があり Ridge の CV R²=+0.611 と健全だが、それでも MLP は CV R²=−3 台に崩壊する。特に MLP(64,32) の train R²=+0.478 に対し CV R²=−3.181 という開きは、47 点を覚えて未知県で真逆を予測する典型的な 過学習 の姿である。結論: n=47 の表データでは線形モデルが最良、次点で GBDT、MLP は最下位。これは「表形式・小標本では MLP は勾配ブースティングにも線形にも劣りうる」という一般則の実データ確認になっている。

🐍 この実測を再現するコード(SSDSE-B-2026)

📥 入力例(SSDSE-B-2026 の 2023 年・47 都道府県から 3 行) 都道府県 A1101(総人口) A1303(65歳以上人口) A4101(出生数) A4103(合計特殊出生率) B4101(年平均気温) L3221(消費支出) 北海道 5,092,000 1,681,000 24,430 1.06 11.0 296,888 東京都 14,086,000 3,205,000 86,348 0.99 17.6 341,320 沖縄県 1,468,000 350,000 12,549 1.60 23.8 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
24
25
26
27
28
29
30
31
32
33
34
35
36
import pandas as pd, numpy as np
from sklearn.linear_model import RidgeCV
from sklearn.neural_network import MLPRegressor
from sklearn.ensemble import GradientBoostingRegressor
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import make_pipeline
from sklearn.model_selection import cross_val_score, cross_val_predict, KFold
from sklearn.metrics import mean_absolute_error

# 2023 年・47 都道府県のみ抽出(英字コードを使うので skiprows=[1])
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
d = df[df['SSDSE-B-2026'] == 2023]          # -> 47 行
cv = KFold(n_splits=5, shuffle=True, random_state=42)

cases = {
    'A: 消費支出 L3221': ('L3221', ['A1101', 'A1303', 'A4101', 'A4103', 'B4101']),
    'B: 出生率 A4103':   ('A4103', ['A1101', 'A1303', 'A4101', 'B4101', 'L3221']),
}
# 入力は標準化する。MLP は目的変数をスケールしない(MLPRegressor の既定のまま)
models = {
    'Ridge (線形)': make_pipeline(StandardScaler(), RidgeCV(alphas=np.logspace(-3, 3, 20))),
    'MLP (16, 8)':  make_pipeline(StandardScaler(),
                        MLPRegressor(hidden_layer_sizes=(16, 8), max_iter=3000, random_state=42)),
    'MLP (64, 32)': make_pipeline(StandardScaler(),
                        MLPRegressor(hidden_layer_sizes=(64, 32), max_iter=3000, random_state=42)),
    'GBDT':         GradientBoostingRegressor(random_state=42),
}
for case, (target, feats) in cases.items():
    X = d[feats].astype(float).values
    y = d[target].astype(float).values
    print(f'ケース {case}')
    for name, m in models.items():
        train_r2 = m.fit(X, y).score(X, y)
        cv_r2 = cross_val_score(m, X, y, cv=cv, scoring='r2').mean()
        cv_mae = mean_absolute_error(y, cross_val_predict(m, X, y, cv=cv))
        print(f'  {name:12s} train R2 = {train_r2:+8.3f}  CV R2 = {cv_r2:+8.3f}  CV MAE = {cv_mae:,.3f}')
📤 実行例(実測) ケース A: 消費支出 L3221 Ridge (線形) train R2 = +0.216 CV R2 = +0.159 CV MAE = 17,394.629 MLP (16, 8) train R2 = -135.386 CV R2 = -171.428 CV MAE = 277,688.485 MLP (64, 32) train R2 = -22.163 CV R2 = -33.077 CV MAE = 104,785.198 GBDT train R2 = +0.995 CV R2 = -0.131 CV MAE = 17,263.838 ケース B: 出生率 A4103 Ridge (線形) train R2 = +0.726 CV R2 = +0.611 CV MAE = 0.065 MLP (16, 8) train R2 = -0.760 CV R2 = -3.161 CV MAE = 0.175 MLP (64, 32) train R2 = +0.478 CV R2 = -3.181 CV MAE = 0.163 GBDT train R2 = +0.996 CV R2 = +0.386 CV MAE = 0.087

💬 結果の読み方: 上の 2 つの表はこの出力をまとめたもの。ケース A の MLP (16, 8) は train R² すら −135.4 で、目的変数の消費支出(約 30 万円)をスケールしないまま既定の学習率で学習すると、3000 回の反復を終えても全 47 県への予測の平均は約 1.8 万円で、実測平均 29.6 万円の水準に届かない。ケース B は目的変数が 1 前後なので train R² は −0.76〜+0.48 まで戻るが、CV R² は −3 台で Ridge の +0.611 に遠く及ばない。もし MLP を使うなら (1) 目的変数も標準化する、(2) 隠れ層を 1 層 8 ユニット程度に絞る、(3) 正則化(alpha)と早期停止を効かせる、(4) それでも Ridge/GBDT に勝てなければ採用しない ── が実務の順序である。

🚀 発展 — MLP が「効く条件」と、n=47 で効かない理由

普遍近似定理(Cybenko 1989, Hornik 1991)は「1 隠れ層 + 十分な非線形ユニットで任意の連続関数を近似できる」と述べるが、必要なユニット数・必要なデータ量は保証しない。実務で MLP を支えるのは、ReLU(ReLU / 活性化関数)による勾配消失の緩和、Adam による学習率の自動調整、ドロップアウト・重み減衰(L2)による 正則化、バッチ正規化による層間スケールの安定化、そして He 初期化 といった一連の工夫である。これらが真価を発揮するのは標本数が数千〜数百万で、入力が高次元(画像・音声・埋め込み)のときだ。深さは同じ表現力をより少ないパラメータで実現する(深さ vs 幅の効率)が、n=47 では深くするほど過学習ギャップが開く(上のケース B が実証)。表形式・小標本という SSDSE-B の土俵では、勾配ブースティング(GBDT)や線形モデルが構造的に有利で、上の実測もその通りになった。MLP を学ぶ意義は、3層パーセプトロン から 深層学習 へ続く基本ブロックとしての理解にあり、「47 県の表を当てる道具」としてではない。

🔗 このセクションの関連ページ

パーセプトロン3層パーセプトロンニューラルネットワーク誤差逆伝播法勾配降下法ReLU活性化関数正則化過学習交差検証標準化線形回帰ランダムフォレスト/GBDT深層学習

注: 上の数値はすべて data/raw/SSDSE-B-2026.csv(2023 年 47 都道府県)に対し scikit-learn の既定設定で実測したもの。乱数種 random_state=42 固定。バージョン差でごく小さな変動はあり得るが、「小データで MLP が CV 崩壊し線形・GBDT に劣る」という結論は頑健である。