論文一覧に戻る 📚 用語集トップ 🗺 概念マップ
📚 用語解説
📚 用語解説
三層パーセプトロン
Three-layer Perceptron
深層学習

🔖 キーワード索引

三層パーセプトロンMLP入力層隠れ層出力層万能近似シグモイドfeedforward

三層パーセプトロン (MLP) は入力層・隠れ層・出力層の 3 層構造で、 隠れ層の非線形活性 (ReLU/Sigmoid) によって XOR 等の非線形分離を可能にしたニューラルネットの最小単位。 SSDSE-B-2026 の人口・出生率から「人口減少県か否か」を二値分類する MLP を sklearn.neural_network.MLPClassifier で学習する。

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

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

💡 30秒で分かる結論

🍰 まずはやさしく

脳の仕組みをまねたシンプルな計算機です。

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

スマホのアプリなどで、写真の種類を分ける時に役立ちます。

この章では、三層パーセプトロンの基本について読みます。

入力・隠れ・出力からなる最も基本的なNN

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

🍰 まずはやさしく

AIが進化するための第一歩となる仕組みです。

ニューラルネットワークの基礎を学ぶために使います。

都道府県のデータを分けて、地域の特徴を調べる例で考えます。

この章では、実際にプログラムを作って正解率を確かめます。

深層学習の歴史的・概念的出発点。 「NNとは何か」を理解するにはまず3層MLPを実装してみるのが早道です。

本ページは 三層パーセプトロン を、 SSDSE-B-2026 の社会指標で 47 都道府県の二値分類タスクとして実装する。 隠れ層ニューロン数を 4→8→16 と変えて訓練精度・検証精度を比較し、 単層パーセプトロンが分離できなかった非線形境界を MLP がどう近似するかを観察する。

「perceptron three layer」は統計・データサイエンスの体系における重要概念のひとつ。 本ページは「定義・直感・数式・実装・落とし穴・関連手法」の 6 視点で構成され、 各視点は独立して読めるが順序通り読むと体系的な理解が得られる。

🎨 直感で掴む

🍰 まずはやさしく

情報を整理して判断する3つのステップのようなものです。

直線では分けられない難しい問題を解くために使います。

部活のメンバーを、複数の条件でグループ分けする時に似ています。

この章では、隠れ層(中間の層)がどう役立つかを読みます。

3層の役割:

  • 入力層:データをそのまま受け取る
  • 隠れ層:非線形変換で特徴を作る(中間表現)
  • 出力層:最終的な予測値を出す

1層では直線(XOR不能)→ 2層あれば曲線分離 → 任意の関数を近似できる、 という大ジャンプがここにあります。

🎨 XOR 問題を 3 層で解く ─ 直感の宝庫

XOR は 1 層パーセプトロン(線形分離器)が解けないことを示し、 1969 年に Minsky & Papert が Perceptrons で指摘した。 3 層なら下記の重みで解ける(手作り解)。

$x_1$$x_2$$h_1=\sigma(x_1+x_2-0.5)$$h_2=\sigma(x_1+x_2-1.5)$$y=\sigma(h_1-h_2-0.5)$期待
00σ(-0.5) ≈ 0.378σ(-1.5) ≈ 0.182σ(-0.304) ≈ 0.425 → 00 ✓
01σ(0.5) ≈ 0.622σ(-0.5) ≈ 0.378σ(-0.256) ≈ 0.436 → 01 ※

※ sigmoid は連続値なので、 「学習」して理想重みに収束させると XOR がきれいに解ける。 ステップ関数なら手作り重みでも厳密に成立。 重要なのは 1 隠れ層を入れることで非線形決定境界が表現可能になる事実。

📌 直感:隠れ層は「入力空間を新しい座標系(特徴空間)に写す」装置。 XOR の場合、 隠れ層が「片方だけが 1」という非線形特徴を作るので、 出力層がそれを線形分離できる。

🎮 触って理解する

このページ独自の切り口は「隠れ層が XOR(線形分離不能問題)をどう解くか」です。 姉妹ページ(ニューラルネットワーク=スライダーと決定境界、 NN の表現力=同心円データ、 活性化関数=関数の重ね描き)とは違い、 ここでは入力空間 (x₁, x₂) と隠れ空間 (h₁, h₂) を並べて表示し、 「隠れ層=座標変換」を直接目で確かめます。 順伝播(sigmoid / step)はすべてブラウザ内で正確に計算しており、 外部通信はありません。

XOR の 4 点(● 出力 0=(0,0)・(1,1)、 ● 出力 1=(0,1)・(1,0))を 1 本の直線で分けてみてください。 直線の端の○ハンドルをドラッグ(タッチ可)で回転、 線の中ほどをドラッグで平行移動。 どちら側を「1」と読むかは自動で有利な方を選びます。 どう頑張っても最大 3/4 にしかならないことを体感してください。

これが 1969 年に Minsky & Papert が『Perceptrons』で証明した単層の限界。 (0,1) と (1,0) を通る側と (0,0)・(1,1) を通る側は、 どんな直線でも必ず混ざります。

💡 直感 ─ 隠れ層は「座標変換装置」であり「特徴抽出器」

学習済み解をセットして隠れ空間を見ると、 入力空間で対角線上に混ざっていた 4 点が、 隠れ空間では一直線で切れる配置に「引っ越し」していることが分かります。 h₁ は「x₁ か x₂ の少なくとも一方が 1(OR 的特徴)」、 h₂ は「両方が 1(AND 的特徴)」を検出しており、 出力層は「OR であって AND でない」という線形分離可能な新しい問題を解いているだけです。 生の座標では解けない問題を、 解ける座標系に写し替える──これが表現学習(representation learning)の最小例であり、 深層学習が画像や言語で行っていることの原型です。

⚠️ 落とし穴 ─ 単層の限界と「線形活性化の罠」

🚀 発展 ─ 万能近似定理と「それでも深くする」理由

XOR に必要な隠れユニットは 2 個ですが、 これを一般化したのが万能近似定理(Cybenko 1989 / Hornik 1991):非線形活性化を持つ隠れ層 1 枚の MLP は、 隠れユニット数を増やせば任意の連続関数を任意精度で近似できます。 ウィジェットで見た「直線の折れ曲がり」を無数に重ねるイメージです。 ただし浅いまま近似しようとすると必要ユニット数が爆発することがあり、 実用では層を重ねて「特徴の特徴」を段階的に作る方が効率的──これが深層学習への動機です。 なお本ウィジェットは重みを手で与えましたが、 実際にこの解へ自動でたどり着く仕組みが誤差逆伝播勾配降下法です。 単層の原型は パーセプトロン を参照。

📐 定義/数式

🍰 まずはやさしく

3つの層を重ねた数学的なモデルです。

入力から答えを出すまでの計算ルールを決めるために使います。

買い物で、予算と好みのバランスを計算して商品を選ぶ時に似ています。

この章では、重みや活性化関数(計算の切り替え役)の式を読みます。

三層パーセプトロンThree-layer Perceptron):入力・隠れ・出力からなる最も基本的なNN

【3層パーセプトロンの式】
$$ \hat{y} = g\Big(W_2 \, \sigma(W_1 \mathbf{x} + \mathbf{b}_1) + \mathbf{b}_2\Big) $$
$W_1, W_2$=層間の重み、 $\sigma$=隠れ層の活性化、 $g$=出力層の活性化(分類ならsoftmax)。

📐 3 層パーセプトロンの完全な式 ─ 前向き計算と勾配

入力 $\mathbf{x} \in \mathbb{R}^{d_0}$、 隠れ層 $\mathbf{h} \in \mathbb{R}^{d_1}$、 出力 $\mathbf{y} \in \mathbb{R}^{d_2}$ とする。 隠れ層活性化を $\sigma_1$、 出力活性化を $\sigma_2$(分類なら softmax)とおく:

$$ \mathbf{z}^{(1)} = W^{(1)} \mathbf{x} + \mathbf{b}^{(1)},\quad \mathbf{h} = \sigma_1(\mathbf{z}^{(1)}) $$ $$ \mathbf{z}^{(2)} = W^{(2)} \mathbf{h} + \mathbf{b}^{(2)},\quad \mathbf{y} = \sigma_2(\mathbf{z}^{(2)}) $$

🔬 数式を言葉で読み解く

勾配 ─ 連鎖律を 1 行で

損失 $L$ の重みに対する勾配は、 出力側から順に伝播する(backpropagation):

$$ \boldsymbol{\delta}^{(2)} = \nabla_{\mathbf{z}^{(2)}} L \;=\; (\mathbf{y} - \mathbf{t}) $$ $$ \boldsymbol{\delta}^{(1)} = (W^{(2)})^\top \boldsymbol{\delta}^{(2)} \odot \sigma_1'(\mathbf{z}^{(1)}) $$ $$ \frac{\partial L}{\partial W^{(2)}} = \boldsymbol{\delta}^{(2)} \mathbf{h}^\top,\quad \frac{\partial L}{\partial W^{(1)}} = \boldsymbol{\delta}^{(1)} \mathbf{x}^\top $$

🔬 数式を言葉で読み解く

万能近似定理(Universal Approximation Theorem)

Cybenko (1989) / Hornik (1991) — 「sigmoid 等の非線形活性化を持つ 1 隠れ層 MLP は、 任意の連続関数を任意精度で近似できる」

$$ \forall f \in C(K),\ \forall \varepsilon > 0,\ \exists N,\ \exists \{W^{(1)},\mathbf{b}^{(1)},W^{(2)},\mathbf{b}^{(2)}\}:\ \sup_{\mathbf{x} \in K} | g(\mathbf{x}) - f(\mathbf{x}) | < \varepsilon $$

$g(\mathbf{x}) = W^{(2)} \sigma(W^{(1)} \mathbf{x} + \mathbf{b}^{(1)}) + \mathbf{b}^{(2)}$ という 3 層 MLP の形。 つまり「層を深くしなくても表現力はある」のが理論。 ただし必要な隠れユニット数 $N$ が指数的に大きくなることがあるため、 実用では深くする。

📐 活性化関数の比較 ─ なぜ Sigmoid だけでは足りないか

3 層 MLP の表現力は活性化関数 $\sigma_1$ で決まる。 代表 4 種の式と勾配は次のとおり。

$$ \mathrm{sigmoid}(z) = \frac{1}{1+e^{-z}},\quad \sigma'(z) = \sigma(z)(1-\sigma(z)) $$ $$ \tanh(z) = \frac{e^z - e^{-z}}{e^z + e^{-z}},\quad \tanh'(z) = 1 - \tanh^2(z) $$ $$ \mathrm{ReLU}(z) = \max(0, z),\quad \mathrm{ReLU}'(z) = \mathbb{1}[z > 0] $$ $$ \mathrm{GELU}(z) = z \cdot \Phi(z),\quad \Phi: \text{標準正規 CDF} $$

🔬 数式を言葉で読み解く

活性化範囲最大勾配代表用途
sigmoid(0, 1)0.252 値分類出力、 歴史的 MLP
tanh(-1, 1)1.0RNN, 小規模 MLP
ReLU[0, ∞)1.0CNN, 深層 MLP の標準
GELU(≈0, ∞)≈ 1.0Transformer (BERT, GPT)

📐 隠れユニット数とパラメータ数の数え方

入力次元 $d_0$、 隠れ $d_1$、 出力 $d_2$ の 3 層 MLP の総パラメータ数:

$$ N_{\mathrm{param}} = \underbrace{d_0 \cdot d_1}_{W^{(1)}} + \underbrace{d_1}_{\mathbf{b}^{(1)}} + \underbrace{d_1 \cdot d_2}_{W^{(2)}} + \underbrace{d_2}_{\mathbf{b}^{(2)}} $$

🔬 数式を言葉で読み解く

構成 (d0, d1, d2)パラメータ数想定タスク
(2, 3, 3)2·3+3+3·3+3 = 21SSDSE-B 人口分類(最小)
(2, 8, 3)2·8+8+8·3+3 = 51本記事の中盤例
(784, 64, 10)784·64+64+64·10+10 = 50,890MNIST 文字認識
(3072, 512, 10)3072·512+512+512·10+10 = 1,578,506CIFAR-10 (3072 = 32×32×3)

📌 47 サンプル(SSDSE-B-2026)に対し (2,8,3)=51 パラメータは サンプル/パラメータ比 ≈ 0.9 で危険水準。 経験則は「サンプル数 ≥ 10 × パラメータ数」。 だから本記事は隠れ 3-4 に抑えた。

📐 勾配消失と Xavier / He 初期化

3 層は浅いので勾配消失は限定的だが、 隠れ層を深くする入口として理解しておく価値がある。 重み $W^{(\ell)}$ の分散を入力次元 $d_{\ell-1}$ で調整するのが Xavier / He 初期化:

$$ \mathrm{Xavier}:\ W \sim \mathcal{N}\!\left(0,\ \frac{2}{d_{\ell-1} + d_{\ell}}\right) $$ $$ \mathrm{He}:\ W \sim \mathcal{N}\!\left(0,\ \frac{2}{d_{\ell-1}}\right) \quad \text{(ReLU 用)} $$

🔬 数式を言葉で読み解く

PyTorch / Keras では nn.Linear の初期値が自動でこれに準じる(PyTorch は kaiming_uniform_)。 自前 numpy 実装する場合は明示的に:

# He 初期化(ReLU 用) W1 = np.random.randn(d0, d1) * np.sqrt(2.0 / d0) W2 = np.random.randn(d1, d2) * np.sqrt(2.0 / d1)

📐 正則化 ─ L2、 ドロップアウト、 早期終了

小データほど正則化が効く。 3 層 MLP で使う代表 3 手法:

① L2 正則化(weight decay)

$$ L_{\mathrm{reg}} = L_{\mathrm{ce}} + \frac{\lambda}{2} \sum_{\ell} \| W^{(\ell)} \|_F^2 $$

🔬 数式を言葉で読み解く

② ドロップアウト

学習時、 各隠れユニットを独立確率 $p$ で 0 に「黒塗り」する。 推論時は全ユニットを使い、 出力を $(1-p)$ 倍してスケール調整。

$$ \tilde{h}_j = \begin{cases} 0 & \text{確率 } p \\ h_j / (1-p) & \text{確率 } 1-p \end{cases} $$

🔬 数式を言葉で読み解く

③ 早期終了 (Early Stopping)

検証損失 $L_{\mathrm{val}}$ が patience エポック連続で改善しなければ訓練停止:

$$ \text{stop if } L_{\mathrm{val}}^{(t)} \ge \min_{t' \le t - \text{patience}} L_{\mathrm{val}}^{(t')} $$

sklearn では early_stopping=True, validation_fraction=0.1, n_iter_no_change=10

📐 損失関数 ─ 課題に応じた選択

分類:cross-entropy

$$ L_{\mathrm{ce}} = -\frac{1}{N} \sum_{i=1}^{N} \sum_{c=1}^{C} y_{i,c} \log p_{i,c} $$

回帰:MSE / MAE

$$ L_{\mathrm{mse}} = \frac{1}{N} \sum_{i=1}^{N} (y_i - \hat{y}_i)^2,\quad L_{\mathrm{mae}} = \frac{1}{N} \sum_{i=1}^{N} | y_i - \hat{y}_i | $$

🔬 数式を言葉で読み解く

📐 最適化手法(オプティマイザ)の式

3 層 MLP の学習で使う主要オプティマイザを 1 行式で並べる(時刻 $t$、 学習率 $\eta$、 勾配 $\mathbf{g}_t = \nabla L(\theta_t)$)。

SGD

$$ \theta_{t+1} = \theta_t - \eta \, \mathbf{g}_t $$

SGD + Momentum

$$ \mathbf{v}_{t+1} = \beta \mathbf{v}_t + \mathbf{g}_t,\quad \theta_{t+1} = \theta_t - \eta \, \mathbf{v}_{t+1} $$

Adam

$$ \mathbf{m}_t = \beta_1 \mathbf{m}_{t-1} + (1-\beta_1) \mathbf{g}_t,\quad \mathbf{v}_t = \beta_2 \mathbf{v}_{t-1} + (1-\beta_2) \mathbf{g}_t^2 $$ $$ \theta_{t+1} = \theta_t - \eta \cdot \frac{\hat{\mathbf{m}}_t}{\sqrt{\hat{\mathbf{v}}_t} + \epsilon} $$

🔬 数式を言葉で読み解く

🔬 記号・用語の読み解き(数式を言葉で読み解く)

三層パーセプトロンの順伝播 $\hat{\mathbf{y}} = W_2 \sigma(W_1 \mathbf{x} + \mathbf{b}_1) + \mathbf{b}_2$ は、 「線形変換 → 非線形活性化 → もう一度線形変換」という 3 段階の合成関数として読み解けます。 まず入力ベクトル $\mathbf{x}$(たとえば SSDSE-B-2026 から取った 47 都道府県の特徴量 5 次元)に重み行列 $W_1$ を掛けて隠れ層の入力 $\mathbf{z}_1 = W_1\mathbf{x} + \mathbf{b}_1$ を作る。 これは「入力特徴の重み付き和を、 隠れ層の各ニューロン分計算する」操作で、 内積の集まりです。 次に活性化関数 $\sigma$(ReLU、 sigmoid、 tanh など)が非線形性を導入。 もしこのステップがなければ、 「線形変換 × 線形変換 = 線形変換」となり、 表現力が線形回帰と同じに退化します。 だから「活性化関数こそがニューラルネットの心臓」と言われるのです。 隠れ層の出力 $\mathbf{h} = \sigma(\mathbf{z}_1)$ をさらに $W_2$ で変換し、 出力 $\hat{\mathbf{y}} = W_2\mathbf{h} + \mathbf{b}_2$ を得る。 この構造は、 1989 年の Cybenko の 普遍近似定理(Universal Approximation Theorem)により、 「隠れ層が十分大きければ、 任意の連続関数を任意精度で近似可能」が証明されています。 つまり三層パーセプトロンは「あらゆる入出力関係を学習可能な万能関数近似器」なのです。 学習はバックプロパゲーション(誤差逆伝播)で行われ、 各重みを「予測誤差を減らす方向」に少しずつ更新します。 SSDSE-B-2026 で「都道府県の人口」を 5 つの特徴量から予測するタスクに使えば、 線形回帰では表現できない非線形パターン(例:人口が小さい県と大きい県で進学率の効果が違う、 という交互作用)を捉えられるのが三層パーセプトロンの強み。 三層という構造は最小限の深さでありながら、 「多くの実問題に対して十分な表現力を持つ」中庸の選択肢として、 1980 年代以降のニューラルネットの標準形になりました。

記号・用語意味と読み方
$\mathbf{x}$入力ベクトル(特徴量、 SSDSE の場合は標準化された数値)
$W_1$入力層→隠れ層の重み行列。 サイズ = (隠れ層ユニット数 × 入力次元)
$\mathbf{b}_1$隠れ層のバイアスベクトル。 シフトを表す
$\sigma$活性化関数。 ReLU $\max(0, x)$、 sigmoid $1/(1+e^{-x})$、 tanh など
$\mathbf{h}$隠れ層の出力。 $\mathbf{h} = \sigma(W_1\mathbf{x} + \mathbf{b}_1)$
$W_2$隠れ層→出力層の重み行列
$\hat{\mathbf{y}}$予測出力。 回帰では実数、 分類では確率(softmax で正規化)
$L$損失関数(MSE、 クロスエントロピー等)
バックプロパゲーション誤差逆伝播。 連鎖律で各重みの勾配を効率計算
勾配降下法$W \leftarrow W - \eta \nabla_W L$ で重みを更新
学習率 $\eta$更新の大きさ。 小さすぎ→収束遅い、 大きすぎ→発散
エポック全データを 1 周する単位。 通常 50〜200 エポックで学習
普遍近似定理Cybenko (1989)。 隠れ層が十分大きければ任意連続関数を近似可能

🔬 詳細な解説(深掘り)

概念の本質

三層パーセプトロン(Three-layer Perceptron)は、 単に用語の定義を覚えるだけでは本当には理解できません。 なぜこの概念が生まれたのかどんな問題を解決するために導入されたのか類似の手法とどう違うのか — これらを意識することで、 初めて「使える知識」になります。

数式や Python コードはあくまで 道具。 道具の使い方を覚える前に、 その道具で何をしたいか(目的) を明確にすることが、 データサイエンス学習の鉄則です。

他の概念との関係

この用語は、 単独で存在するわけではなく、 多くの関連概念とネットワークを形成しています。 上の「関連用語」セクションに挙げたリンク先を1つずつ辿ると、 全体像が見えてきます。 特に:

実務で気をつけるポイント

理論を学ぶことと、 実務で使えることは別物です。 公的統計(SSDSE、 e-Stat 等)の実データで実装・実験することで、 教科書だけでは見えない罠 に気付けます。 たとえば:

これらは 三層パーセプトロン に限った話ではなく、 データサイエンス全般に共通する作法です。 「落とし穴」セクションの内容と合わせて、 自分なりのチェックリストを作るとよいでしょう。

📊 評価・検証の視点

三層パーセプトロン を使った分析の 正しさを担保する ためには、 以下の観点で検証するのが定番です。

確認する点三層パーセプトロン で何を見るか
層を増やせばよい、 と思う深層化は別の課題(勾配消失)を生む。 ResNet 等の工夫が必要。
正規化の欠如入力スケールがバラバラだと学習しない。 標準化を。
活性化関数の選択Sigmoid は飽和して学習止まる → ReLU が現代の標準。
ユニット数の決め方「特徴量の2倍」程度から試して、 検証で調整。
再現性同じデータ・同じコードで同じ結果が出るか。このページの ▶ 実行ボタンで確かめられます

💼 業界別の使われ方

三層パーセプトロン は分野横断で活躍する概念です。 業界別に見ると以下のような使われ方があります。

🏥 医療・ヘルスケア
疾病予測、 診断支援、 治療効果の評価、 公衆衛生指標の分析(高齢化率、 罹患率、 医療費等)
🏛️ 行政・公共政策
EBPM(エビデンスに基づく政策立案)、 地域経済分析、 RESAS/e-Stat の活用、 政策効果測定
🏪 マーケティング・小売
顧客分析、 需要予測、 価格弾力性、 RFM分析、 A/Bテスト、 LTV予測
🏭 製造・品質管理
品質管理、 故障予知、 異常検知、 生産最適化、 サプライチェーン分析
💰 金融・保険
信用スコア、 リスク評価、 不正検知、 アルゴリズムトレーディング、 保険料設定
🎓 教育・研究
教育効果の測定、 学習分析、 研究データ解析、 統計教育、 データサイエンス人材育成

📈 公的統計データ(SSDSE)での具体例

三層パーセプトロン を実際のデータで学ぶときは、 SSDSE(教育用標準データセット、 総務省統計局)が便利です。

これらは 統計センターの SSDSE ページ から CSV で直接ダウンロードできます。 上の Python コード例で data/raw/SSDSE-B-2026.csv としているのが、 まさにこれです。

実データで動かすことで、 教科書の例題では見えない 実務的な気づき(欠損のパターン、 単位の混在、 都道府県名の表記揺れ等)が得られます。

🔧 よくあるトラブルと対処

🐍 Python コードが動かない
→ Python 3.10+ と必要ライブラリ(pandas、 numpy、 scikit-learn 等)がインストール済みか確認。 pip install pandas numpy scikit-learn matplotlib で揃います。
📁 CSVファイルが読み込めない
→ ファイルパスを確認。 文字コードが utf-8 ではなく shift_jiscp932 の場合がある(古い日本の公的統計に多い)。 encoding='cp932' を試してください。
📐 数式が表示されない
→ ページが KaTeX を読み込んでいるはずです。 ブラウザのキャッシュをクリアするか、 開発者ツールで JavaScript エラーを確認。
🔢 数値計算結果が教科書と違う
→ 不偏推定(n-1)と標本推定(n)の違い、 浮動小数点誤差、 ライブラリのデフォルト引数の違いなどが原因。 ドキュメントを確認。
📊 グラフが描画されない
→ Jupyter Notebook なら %matplotlib inline、 スクリプト実行なら plt.show() を忘れずに。 日本語フォントは matplotlib 用に別途設定(japanize-matplotlib 等)が必要。

🧮 実値で計算してみる

例:入力10次元、 隠れ層50ユニット、 出力1 → パラメータ数 = (10×50 + 50) + (50×1 + 1) = 601。 これでも非線形回帰が可能。

🧮 SSDSE-B-2026 で人口階層を 3 層 MLP で分類

47 都道府県の A1101(総人口)を 3 クラスに分け、 2 つの入力特徴(高齢人口 A1303、 出生数 A4101)から人口階層を予測する。

事前情報(実データ検証済)

隠れ 3 ユニットで手計算(東京 1 県分)

適当な初期重みを与えると、 東京(標準化後の最大値 x1=3.55, x2=4.18)に対し:

W1 = [[0.4, -0.3, 0.2], [0.5, 0.6, -0.1]] b1 = [0.1, -0.1, 0.05] z1 = x @ W1 + b1 = [3.55*0.4 + 4.18*0.5 + 0.1, 3.55*(-0.3) + 4.18*0.6 + (-0.1), 3.55*0.2 + 4.18*(-0.1) + 0.05] = [3.610, 1.343, 0.342] h = σ(z1) = sigmoid([3.610, 1.343, 0.342]) ≈ [0.974, 0.793, 0.585]

続けて出力層 (3→3) に通すと 3 クラス確率になる。 softmax を当てて argmax を取れば予測クラスが決まる。

🧮 数式に値を入れて手で計算する: 3 層パーセプトロンの順伝播

合成データ x=[1, 2] で 3 層ネットの出力を計算する。

Step 1: 重み

入力 x = [1, 2] 隠れ層重み W1 = [[0.5, -0.3], [0.2, 0.7]]、 b1 = [0.1, -0.2] 出力層重み W2 = [0.6, 0.4]、 b2 = 0.05

Step 2: 隠れ層 (ReLU)

z1 = W1 · x + b1 = [0.5·1 + (-0.3)·2 + 0.1, 0.2·1 + 0.7·2 + (-0.2)] = [0.0, 1.4] h1 = ReLU(z1) = [0, 1.4]

Step 3: 出力

y = W2 · h1 + b2 = 0.6·0 + 0.4·1.4 + 0.05 = 0.61

🐍 Python で再現

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

📤 実行結果

y: 0.61

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

🐍 Python での実装例

SSDSE-B-2026 などの実データを使った最小コード(12行):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
import torch
import torch.nn as nn
# 3層MLP(入力10 → 隠れ50 → 出力1)
mlp = nn.Sequential(
    nn.Linear(10, 50),
    nn.ReLU(),
    nn.Linear(50, 1)
)
x = torch.randn(32, 10)
y = mlp(x)
print('入力:', x.shape, '隠れ:', mlp[0](x).shape, '出力:', y.shape)
print('パラメータ数:', sum(p.numel() for p in mlp.parameters()))

data/raw/SSDSE-B-2026.csve-Stat SSDSE から取得した実データを想定。

🐍 Python 実装①:numpy だけで 3 層 MLP を 1 から書く

🎯 このコードでやること:sklearn を使わず、 numpy だけで前向き計算と勾配降下を実装する。 SSDSE-B-2026 47 都道府県を 3 クラスに分類する。

📥 入力データ:SSDSE-B-2026 (2023 年)。 入力 = (A1303 高齢人口, A4101 出生数) を標準化、 ラベル = A1101 の三分位クラス。

 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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
import numpy as np
import pandas as pd

df = pd.read_csv('data/raw/SSDSE-B-2026.csv',
                 encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026'] == 2023].copy()
pop = df['A1101'].astype(float).values
eld = df['A1303'].astype(float).values
bir = df['A4101'].astype(float).values

q = np.quantile(pop, [1/3, 2/3])
y = np.digitize(pop, q)              # 0,1,2

X = np.column_stack([eld, bir])
X = (X - X.mean(0)) / X.std(0)       # 標準化
Y = np.eye(3)[y]                     # ワンホット

np.random.seed(0)
W1 = np.random.randn(2, 4) * 0.3
b1 = np.zeros(4)
W2 = np.random.randn(4, 3) * 0.3
b2 = np.zeros(3)

def sigmoid(z): return 1 / (1 + np.exp(-z))
def softmax(z):
    e = np.exp(z - z.max(1, keepdims=True))
    return e / e.sum(1, keepdims=True)

lr = 0.5
for epoch in range(2000):
    # forward
    H = sigmoid(X @ W1 + b1)
    P = softmax(H @ W2 + b2)
    # cross-entropy loss
    loss = -(Y * np.log(P + 1e-12)).sum() / len(X)
    # backward
    d2 = (P - Y) / len(X)
    dW2 = H.T @ d2
    db2 = d2.sum(0)
    d1 = (d2 @ W2.T) * H * (1 - H)
    dW1 = X.T @ d1
    db1 = d1.sum(0)
    # update
    W1 -= lr * dW1; b1 -= lr * db1
    W2 -= lr * dW2; b2 -= lr * db2
    if epoch % 500 == 0:
        print(f'epoch {epoch:4d}  loss={loss:.4f}')

pred = P.argmax(1)
acc = (pred == y).mean()
print('-' * 40)
print(f'最終訓練精度 = {acc:.4f}')
print('東京 pred =', pred[df['Code'].values == 'R13000'])
print('島根 pred =', pred[df['Code'].values == 'R32000'])

📤 実行結果

epoch 0 loss=1.0937 epoch 500 loss=0.2264 epoch 1000 loss=0.1436 epoch 1500 loss=0.1063 ---------------------------------------- 最終訓練精度 = 0.9787 東京 pred = [2] 島根 pred = [0]

💬 47 都道府県という小データでも、 2 特徴 → 隠れ 4 → 出力 3 の 3 層 MLP で 98% 精度。 東京は大クラス(2)、 島根は小クラス(0)と正しく判定。 cross-entropy 損失が 1.10 → 0.11 へ減衰、 勾配降下が正しく機能している。

🐍 Python 実装②:sklearn MLPClassifier で同じ問題

🎯 このコードでやること:自前 numpy 実装と同じデータを sklearn.neural_network.MLPClassifier に通し、 精度・係数次元・収束を確認する。

📥 入力データ:上と同じ SSDSE-B-2026 (2023 年, 47 行) の標準化 2 特徴。

 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
37
import numpy as np
import pandas as pd
from sklearn.neural_network import MLPClassifier
from sklearn.preprocessing import StandardScaler

df = pd.read_csv('data/raw/SSDSE-B-2026.csv',
                 encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026'] == 2023].copy()
pop = df['A1101'].astype(float).values
eld = df['A1303'].astype(float).values
bir = df['A4101'].astype(float).values

q = np.quantile(pop, [1/3, 2/3])
y = np.digitize(pop, q)

X = StandardScaler().fit_transform(
    np.column_stack([eld, bir])
)

mlp = MLPClassifier(
    hidden_layer_sizes=(3,),     # 隠れ層 = 1 つ、 3 ユニット
    activation='logistic',        # sigmoid
    solver='lbfgs',
    max_iter=2000,
    random_state=0,
)
mlp.fit(X, y)
print(f'訓練精度 = {mlp.score(X, y):.4f}')
print(f'W1 (入力→隠れ) shape: {mlp.coefs_[0].shape}')
print(f'W2 (隠れ→出力) shape: {mlp.coefs_[1].shape}')
print(f'損失収束: {mlp.loss_:.4f}')

# 各都道府県の予測を確認
df['pred'] = mlp.predict(X)
df['true'] = y
print('-' * 40)
print(df[['Code','Prefecture','pred','true']].head(5).to_string(index=False))

📤 実行結果

訓練精度 = 1.0000 W1 (入力→隠れ) shape: (2, 3) W2 (隠れ→出力) shape: (3, 3) 損失収束: 0.0068 ---------------------------------------- Code Prefecture pred true R01000 北海道 2 2 R02000 青森県 1 1 R03000 岩手県 0 0 R04000 宮城県 2 2 R05000 秋田県 0 0

💬 sklearn は 100% 精度(自前実装の 98% と近い)。 W1 が (2,3) は「入力 2 次元 → 隠れ 3 ユニット」、 W2 が (3,3) は「隠れ 3 → 出力 3 クラス」。 北海道・宮城が「大クラス(2)」、 青森が「中クラス(1)」、 岩手・秋田が「小クラス(0)」と整合している。

🐍 Python 実装③:決定境界を可視化(線形回帰との比較)

🎯 このコードでやること:3 層 MLP と多項ロジスティック回帰(隠れ層なし=線形)で同じデータを学習し、 決定境界を 2 次元空間に描いて比較する。 MLP がなぜ「曲がる」のかを目で確認。

📥 入力データ:SSDSE-B-2026 47 都道府県、 標準化済の (高齢人口, 出生数) 2 特徴。

 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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.neural_network import MLPClassifier
from sklearn.linear_model   import LogisticRegression
from sklearn.preprocessing  import StandardScaler

# 英字の項目コード(A1101 など)を使うので、2 行目の日本語名を読み飛ばす
df = pd.read_csv('data/raw/SSDSE-B-2026.csv',
                 encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026'] == 2023].copy()
pop = df['A1101'].values
eld = df['A1303'].values
bir = df['A4101'].values
q = np.quantile(pop, [1/3, 2/3])
y = np.digitize(pop, q)

X = StandardScaler().fit_transform(
    np.column_stack([eld, bir])
)

mlp = MLPClassifier(hidden_layer_sizes=(8,),
                    activation='tanh',
                    max_iter=3000, random_state=0).fit(X, y)
# scikit-learn 1.5 以降、multi_class 引数は廃止(3 クラス以上なら自動で多項ロジット)
lr  = LogisticRegression(max_iter=500).fit(X, y)

# グリッドで決定境界
gx, gy = np.meshgrid(
    np.linspace(X[:,0].min()-1, X[:,0].max()+1, 200),
    np.linspace(X[:,1].min()-1, X[:,1].max()+1, 200),
)
grid = np.column_stack([gx.ravel(), gy.ravel()])
Zmlp = mlp.predict(grid).reshape(gx.shape)
Zlr  = lr.predict(grid).reshape(gx.shape)

fig, ax = plt.subplots(1, 2, figsize=(11, 4.5))
for a, Z, name in zip(ax, [Zlr, Zmlp],
                       ['Logistic (線形)', 'MLP 3 層 (非線形)']):
    a.contourf(gx, gy, Z, alpha=0.3, cmap='viridis')
    a.scatter(X[:,0], X[:,1], c=y, edgecolor='k', s=60, cmap='viridis')
    a.set_title(name)
    a.set_xlabel('高齢人口 (標準化)')
    a.set_ylabel('出生数 (標準化)')

plt.tight_layout()
plt.savefig('mlp_boundary.png', dpi=110)
print('図を mlp_boundary.png に出力')
print(f'logistic 精度 = {lr.score(X,y):.3f}')
print(f'MLP 精度     = {mlp.score(X,y):.3f}')

📤 実行結果

図を mlp_boundary.png に出力 logistic 精度 = 0.830 MLP 精度 = 0.957

💬 ロジスティック 0.830 → MLP(隠れ 8)0.957。 図では Logistic が直線の決定境界、 MLP が曲がった境界になり、 47 県の特殊な分布に追従する。 過学習にも近いため、 cross-validation を併用すべき。

🐍 Python 実装④:活性化関数を変えて精度を比較

🎯 このコードでやること:SSDSE-B-2026 の人口分類タスクで、 logistic / tanh / relu の 3 種類を回し、 訓練精度・収束エポック・損失を比較する。

📥 入力データ:SSDSE-B-2026 (2023 年, 47 行)、 高齢人口 / 出生数の 2 特徴。

 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
import numpy as np
import pandas as pd
from sklearn.neural_network import MLPClassifier
from sklearn.preprocessing  import StandardScaler

df = pd.read_csv('data/raw/SSDSE-B-2026.csv',
                 encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026'] == 2023].copy()
pop = df['A1101'].values
X = StandardScaler().fit_transform(np.column_stack([
    df['A1303'].values, df['A4101'].values
]))
q = np.quantile(pop, [1/3, 2/3])
y = np.digitize(pop, q)

results = []
for act in ['logistic', 'tanh', 'relu']:
    mlp = MLPClassifier(
        hidden_layer_sizes=(4,),
        activation=act,
        solver='lbfgs',
        max_iter=2000, random_state=0,
    ).fit(X, y)
    results.append({
        'activation': act,
        'train_acc': round(mlp.score(X, y), 4),
        'loss':      round(mlp.loss_, 4),
        'n_iter':    mlp.n_iter_,
    })
print(pd.DataFrame(results).to_string(index=False))

📤 実行結果

activation train_acc loss n_iter logistic 1.0 0.0044 108 tanh 1.0 0.0023 22 relu 1.0 0.0016 56

💬 3 つとも訓練精度 1.0——47 県 2 特徴という小さな問題では、 活性化関数を変えても「解けるかどうか」は変わらない。 差が出るのは収束の速さで、 tanh が 22 iter と最速、 relu 56 iter、 logistic は飽和で勾配が小さく 108 iter かかる。 「現代 MLP は ReLU 系」という経験則は深いネットワークで勾配消失を避けるための話であり、 隠れ 1 層 4 ユニットのこの規模では逆に tanh が有利になることもある。 なお 1.0 はあくまで学習に使った 47 件で測った訓練精度で、 汎化性能は次の実装⑤・⑥の交差検証で見る。

🐍 Python 実装⑤:隠れユニット数を交差検証で決める

🎯 このコードでやること:隠れユニット数を 2, 4, 8, 16, 32 と振り、 5 分割 CV で平均精度を取って 過学習の発生点を見つける。 SSDSE-B-2026 の小データで「層を増やすほど良いとは限らない」を実証。

📥 入力データ:SSDSE-B-2026 (2023 年, 47 行)、 2 特徴 + 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 numpy as np
import pandas as pd
from sklearn.neural_network import MLPClassifier
from sklearn.preprocessing  import StandardScaler
from sklearn.model_selection import cross_val_score

df = pd.read_csv('data/raw/SSDSE-B-2026.csv',
                 encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026'] == 2023].copy()
pop = df['A1101'].values
X = StandardScaler().fit_transform(np.column_stack([
    df['A1303'].values, df['A4101'].values
]))
q = np.quantile(pop, [1/3, 2/3])
y = np.digitize(pop, q)

rows = []
for h in [2, 4, 8, 16, 32]:
    mlp = MLPClassifier(
        hidden_layer_sizes=(h,),
        activation='tanh', solver='lbfgs',
        max_iter=3000, random_state=0,
    )
    cv = cross_val_score(mlp, X, y, cv=5)
    rows.append({'hidden': h,
                 'cv_mean': round(cv.mean(), 4),
                 'cv_std':  round(cv.std(),  4)})

print(pd.DataFrame(rows).to_string(index=False))

📤 実行結果

hidden cv_mean cv_std 2 0.9578 0.0518 4 1.0000 0.0000 8 0.9800 0.0400 16 0.9800 0.0400 32 0.9800 0.0400

💬 ピークは 隠れ 4 ユニットで CV 平均 1.0000(std も 0)。 隠れ 2 は 0.9578 とわずかに足りず、 隠れ 8 以上は 0.9800 で頭打ち——増やしても良くならない。 「高齢人口と出生数から人口三分位を当てる」という問題がほぼ線形分離可能なので、 4 ユニットで十分足りているためである。 過学習で CV が崩れるほどではないが、 得るものも無いので、 「隠れ層は最小限から始めて、 CV が改善しなくなったら止める」が王道。 なお 47 件を 5 分割すると 1 fold が 9〜10 件しかなく、 CV 値は 0.02 刻みでしか動かない。 この粒度の差を「有意な改善」と読まないこと。

🐍 Python 実装⑥:L2 強度を変えて汎化性能を見る

🎯 このコードでやること:α(L2 強度)を 6 段階に振り、 CV 精度の変化を見る。 47 県という小データで「正則化なし」がいかに危ないかを定量化する。

📥 入力データ:SSDSE-B-2026 (2023 年, 47 行)、 2 特徴。

 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 numpy as np
import pandas as pd
from sklearn.neural_network import MLPClassifier
from sklearn.preprocessing  import StandardScaler
from sklearn.model_selection import cross_val_score

df = pd.read_csv('data/raw/SSDSE-B-2026.csv',
                 encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026'] == 2023].copy()
pop = df['A1101'].values
X = StandardScaler().fit_transform(np.column_stack([
    df['A1303'].values, df['A4101'].values
]))
q = np.quantile(pop, [1/3, 2/3])
y = np.digitize(pop, q)

rows = []
for alpha in [1e-6, 1e-4, 1e-3, 1e-2, 1e-1, 1.0]:
    mlp = MLPClassifier(
        hidden_layer_sizes=(8,),
        activation='tanh', solver='lbfgs',
        alpha=alpha, max_iter=3000, random_state=0,
    )
    cv = cross_val_score(mlp, X, y, cv=5)
    train_acc = mlp.fit(X, y).score(X, y)
    rows.append({
        'alpha':     alpha,
        'train_acc': round(train_acc, 4),
        'cv_mean':   round(cv.mean(), 4),
        'gap':       round(train_acc - cv.mean(), 4),
    })
print(pd.DataFrame(rows).to_string(index=False))

📤 実行結果

alpha train_acc cv_mean gap 0.000001 1.0000 0.9800 0.0200 0.000100 1.0000 0.9800 0.0200 0.001000 1.0000 0.9800 0.0200 0.010000 1.0000 0.9800 0.0200 0.100000 1.0000 0.9578 0.0422 1.000000 0.9574 0.8711 0.0863

💬 このデータでは α を 10⁻⁶ から 10⁻² まで振っても結果が動かない(train 1.0000 / CV 0.9800 / gap 0.0200 で一定)。 高齢人口と出生数から人口三分位を当てる問題が易しく、 正則化を効かせるまでもなく解けてしまうためである。 α=0.1 で CV が 0.9578 に、 α=1.0 では訓練精度すら 0.9574、 CV 0.8711、 gap 0.0863 まで落ちる——こちらは過正則化で、 罰則が強すぎて必要な非線形性まで潰している。 つまりこの例で観察できるのは「正則化なしが危ない」ではなく 「効かせすぎると壊れる」側で、 いずれにせよ α は CV で選ぶという結論は変わらない。 なお gap が常に 0.02 前後に留まるのは、 47 件 5 分割で 1 fold あたり 9〜10 件しかなく CV の分解能が粗いことも効いている。

🐍 Python 実装⑦:PyTorch で同じ 3 層 MLP を書く

🎯 このコードでやること:sklearn を卒業して PyTorch でモデルを定義する。 SSDSE-B-2026 の人口分類タスクを nn.Linear + F.relu + Adam で学習する。 現代の深層学習の標準テンプレート。

📥 入力データ:これまでと同じ SSDSE-B-2026 (2023 年)、 2 特徴 + 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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import numpy as np
import pandas as pd
import torch
import torch.nn as nn
import torch.nn.functional as F
from sklearn.preprocessing import StandardScaler

df = pd.read_csv('data/raw/SSDSE-B-2026.csv',
                 encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026'] == 2023].copy()
pop = df['A1101'].values
X = StandardScaler().fit_transform(np.column_stack([
    df['A1303'].values, df['A4101'].values
]))
q = np.quantile(pop, [1/3, 2/3])
y = np.digitize(pop, q)

X_t = torch.tensor(X, dtype=torch.float32)
y_t = torch.tensor(y, dtype=torch.long)

class MLP3(nn.Module):
    def __init__(self, d_in=2, d_h=8, d_out=3):
        super().__init__()
        self.fc1 = nn.Linear(d_in, d_h)
        self.fc2 = nn.Linear(d_h, d_out)
    def forward(self, x):
        h = F.relu(self.fc1(x))
        return self.fc2(h)  # logits

torch.manual_seed(0)
net = MLP3()
opt = torch.optim.Adam(net.parameters(), lr=0.05)

for epoch in range(500):
    logits = net(X_t)
    loss = F.cross_entropy(logits, y_t)
    opt.zero_grad(); loss.backward(); opt.step()
    if epoch % 100 == 0:
        acc = (logits.argmax(1) == y_t).float().mean().item()
        print(f'epoch {epoch:3d}  loss={loss.item():.4f}  acc={acc:.4f}')

with torch.no_grad():
    pred = net(X_t).argmax(1)
    print(f'最終 acc = {(pred==y_t).float().mean():.4f}')
    print(f'パラメータ総数 = {sum(p.numel() for p in net.parameters())}')

📤 実行結果

epoch 0 loss=1.0709 acc=0.5957 epoch 100 loss=0.0487 acc=0.9787 epoch 200 loss=0.0239 acc=1.0000 epoch 300 loss=0.0158 acc=1.0000 epoch 400 loss=0.0115 acc=1.0000 最終 acc = 1.0000 パラメータ総数 = 51

💬 PyTorch で 51 パラメータの (2,8,3) MLP を Adam(lr=0.05) で 500 epoch 回すと、 200 epoch で acc=1.0000——47 県すべてを正解する。 ただしこれは学習に使ったのと同じ 47 件で測った訓練精度であり、 汎化性能ではない。 n=47 に対しパラメータ 51 個とサンプル数を上回っているので、 丸暗記できてしまうのは当然である(同じ 2 特徴・同じラベルで交差検証した実装⑤・⑥でも CV 平均は 0.96〜1.00 で、 この問題自体がそもそも易しいことも効いている)。 PyTorch は研究・本番両方で標準で、 forward を Python で書ける柔軟性が利点だが、 「正則化も分割も自分で書かないと誰も止めてくれない」のが裏返しの注意点。 torch.manual_seed(0) を置いてあるので、 この数値は手元でもそのまま再現する。

⚠️ よくある落とし穴

❌ 層を増やせばよい、 と思う
深層化は別の課題(勾配消失)を生む。 ResNet 等の工夫が必要。
❌ 正規化の欠如
入力スケールがバラバラだと学習しない。 標準化を。
❌ 活性化関数の選択
Sigmoid は飽和して学習止まる → ReLU が現代の標準。
❌ ユニット数の決め方
「特徴量の2倍」程度から試して、 検証で調整。

⚠️ 落とし穴を深く ─ 6 つの典型的失敗

  1. 標準化を忘れる ─ SSDSE-B-2026 の人口(M 単位)と就業者(K 単位)を生で入力すると、 sigmoid が即飽和して学習しない。 StandardScaler は必須
  2. 勾配消失 ─ sigmoid を深く積むと初期段階で勾配が 10⁻⁵ 以下に。 解決策は ReLU 系・残差接続・He 初期化。
  3. 学習率の選び損ね ─ 0.01〜0.001 が標準域。 大きすぎると振動、 小さすぎると収束遅延。 solver='adam' は適応的に調整するので失敗が少ない。
  4. 万能近似定理を「層数の言い訳」にする ─ 「1 隠れ層でも近似できる」は理論。 実用上は層を深くする方が同じ精度に少ないユニットで到達。 深さは効率の問題。
  5. 隠れユニット数を増やせば良いと誤解 ─ 47 県データでは隠れ 4 の CV 1.0000 に対し隠れ 8/16/32 はいずれも 0.9800 で、 増やしても改善しない(前述の実験)。 サンプル ≥ 10 × パラメータが目安。
  6. 確率出力を 0/1 と読み違える ─ softmax の出力は確率。 閾値 0.5 で 0/1 化する前に、 「自信」と「精度」の関係(calibration)を ECE 等で確認すべき。

🗺 概念マップ

3 層パーセプトロン (入力層 → 隠れ層 → 出力層) を中心に、 上位概念 (ニューラルネット族)、 並列概念 (単純パーセプトロン・多層パーセプトロン・畳み込みネット)、 構成要素 (活性化関数・誤差逆伝播・確率的勾配降下法)、 応用 (MNIST・回帰・二値分類) を関係づけて整理する。

perceptron three layer 標準化 ベースライン サンプル数 ≥ 10 × パ CV で隠れユニット数決定 正則化(L2 / dropo 初期化

三層パーセプトロン (入力 → 隠れ → 出力) は、 単層の線形分離限界 (XOR が解けない) を中間層の非線形活性化で突破した最小単位のニューラルネットで、 universal approximator として「十分な隠れユニット数があれば任意の連続関数を近似できる」ことが理論保証されている。 上の概念図は、 隠れ層のユニット数を交差検証で決め、 L2 正則化や dropout で過学習を抑え、 初期化(Xavier/He)で勾配消失を防ぐという「汎化性能のための三本柱」を示している。 SSDSE-B-2026 の都道府県データで「人口・面積・産業構造 → 出生率」を予測するように、 線形回帰では捉えられない交互作用を中間層が自動抽出する点が三層モデルの本質である。

🔗 隣接手法への橋渡し

三層パーセプトロンは「単層パーセプトロン → 多層化 → 深層学習」という進化軸の中継点で、 前後の手法と連続的に繋がる。

三層 MLP を理解すると、 sklearn の MLPClassifier から PyTorch の任意の深層ネットまで「中間層が何枚あるか」のスケール差として連続的に把握できる。

🌳 手法選択フロー

「3 層パーセプトロン」を実際に使うとき、 何をどう選ぶかを順に判断する。 上から順に答えていくと、 使うべき手法と評価の仕方が決まる。

  1. 線形では足りないのか
    2 層(入力→出力)では直線でしか分けられない。 XOR のように直線で分けられない問題に出会って初めて、 中間層が要る。 線形モデルで足りるなら、 そちらのほうが解釈しやすい。
  2. 中間層のユニット数をいくつにするか
    多いほど表現力は上がるが過学習も早い。 n=47 の都道府県データなら数個で十分で、 数十にすると訓練データを丸暗記する。
  3. 活性化関数は何にするか
    中間層は ReLU 系が基本。 Sigmoid を深く積むと勾配消失で学習が進まない。 出力層はタスクに合わせ、 回帰なら恒等、 2 値分類なら Sigmoid、 多クラスなら Softmax。
  4. 入力を標準化したか
    尺度の違う列をそのまま入れると、 値の大きい列だけで重みが決まる。 総人口(百万単位)と出生率(1 前後)を混ぜるなら必ず標準化する。

3 層パーセプトロンは「非線形を扱える最小構成」。 これで足りない場合に初めて層を増やすと、 どこで効いたのかが分かる。

🧭 解説を深める ─ 「折れ線の本数」で三層の表現力を測る

姉妹ページ(単層パーセプトロンMLPDNN)では「座標変換」「特徴抽出」「万能近似」という視点を扱いました。 ここでは別角度、 決定境界を何本の折れ線で区切れるか(線形領域数)という幾何で三層 MLP の力と限界を捉え直します。 ウィジェットで見た「直線が折れ曲がる」現象を、 数えられる量にするのが狙いです。

💡 直感 ─ 隠れユニット 1 個 = 入力空間に引く 1 本の折り目

ReLU を使う三層 MLP では、 隠れユニット $j$ の出力 $h_j=\max(0,\,w_j^\top x + b_j)$ は「直線 $w_j^\top x + b_j = 0$ で片側だけ折り曲げる」操作です。 隠れユニットを $H$ 個並べると、 入力空間には $H$ 本の直線(一般には超平面)が引かれ、 平面はいくつもの凸な小領域(線形領域)に分割されます。 出力層はその領域ごとに一枚の平面を貼るので、 領域が多いほど複雑な決定境界を描ける──これが表現力の正体です。

2 次元入力で $H$ 本の直線が一般の配置にあるとき、 分割される領域数の上限は超平面配置の公式(Zaslavsky)で $$R(H)=\binom{H}{0}+\binom{H}{1}+\binom{H}{2}=1+H+\frac{H(H-1)}{2}$$ となります(幾何的上限・理論値、 実データではありません)。

隠れユニット数 H → 2次元入力での最大線形領域数 R(H) (幾何的上限) H = 2 → R = 4 ← XOR に必要な最小構成(2 本で 4 象限を切る) H = 3 → R = 7 H = 5 → R = 16 H = 10 → R = 56 H = 20 → R = 211

XOR が「隠れ 2 ユニット」で解けるのは、 4 つの点を分けるのに直線 2 本=4 領域あれば足りるからだと読み替えられます。 下のデモで、 折り目の本数 $H$ を動かすと領域上限がどう伸びるか確かめられます。

🧪 架空データのデモ(乱数でランダムな直線を引くだけ・シード固定/キャンバスをタップで配置替え)
折り目の本数 H = 3
→ 2 次元での最大線形領域数 R(H) = 7(幾何的上限)

⚠️ 落とし穴(重要)─ 領域数が標本数を超えた瞬間に「丸暗記」ができてしまう

表現力が高いことは、 小標本では過学習の火種そのものです。 領域数 $R(H)$ が学習に使えるデータ点数 $N$ を超えると、 理屈上は各データ点を専用の領域に隔離でき、 訓練誤差ゼロ=丸暗記が可能になります。 SSDSE-B-2026 で実測すると次のとおりです(pd.read_csv(encoding='cp932', skiprows=[1])df[df['SSDSE-B-2026']==2023])。

# SSDSE-B-2026(実測・2023年) 都道府県数 N = 47 指標(数値)列 p = 109 ← 特徴が標本の 2 倍以上(p > N) 総人口 A1101 最大 = 14,086,000(東京都) 総人口 A1101 最小 = 537,000(鳥取県) 総人口 A1101 中央値 = 1,549,000(上回る 23 県/以下 24 県) # 幾何的上限(理論値) 2次元射影で R(H) >= 47 を満たす最小 H = 10 (R(10) = 56 >= 47)

つまり 2 次元に落とした 47 県は、 隠れ 10 ユニットもあれば 1 県 1 領域に隔離しきれる——訓練精度は 100% でも、 これは学習ではなく暗記です。 さらに元データは指標が $p=109$ で標本 $N=47$ を上回る($p>N$)ため、 全指標を放り込むと過学習は一段と加速します。 対策は姉妹ページと共通で、 隠れユニット数を交差検証で絞る/L2・ドロップアウト/特徴を数個に厳選、 の三点です。 「近似できる」と「47 点から学べる」は別問題という点を、 領域数という具体的な数で押さえておいてください。

🚀 発展 ─ 深さは領域数を「掛け算」で増やす

三層(隠れ 1 枚)で領域数を稼ぐには $H$ を大きくするしかなく、 上の公式のとおり増え方は $H^2$ どまり(足し算的)です。 ところが層を重ねると、 前の層が作った折れ目にさらに折れ目が掛かるため、 領域数は層ごとに掛け算的に増えます。 同じ表現力を、 深いネットは指数的に少ないユニットで達成できる──これが「三層で足りるのに、 なぜ深くするのか」への幾何的な答えです。 詳しくは ディープニューラルネット、 学習の仕組みは 誤差逆伝播法活性化関数 を参照してください。

🔗 関連ページ