本ページは、 ニューラルネットワーク (NN) の基礎概念を統合的に解説します。 パーセプトロンから始まり、 多層パーセプトロン (MLP)、 活性化関数、 誤差逆伝播、 最適化器まで一気通貫で扱います。
近年の深層学習・LLM はすべてここから派生しています。 まずは「線形変換と非線形活性化を交互に重ねる」という基本構造を、 数式と Python で押さえます。
論文記事から各用語のリンクをクリックすると、 該当箇所が開きます:
| 章 | 内容 |
|---|---|
| 1. パーセプトロン | NN の最小単位 |
| 2. 多層化(MLP) | 隠れ層を持つ NN |
| 3. 活性化関数 | 非線形性の源 |
| 4. 損失関数 | 予測の良し悪しの尺度 |
| 5. 誤差逆伝播 | 勾配の計算 |
| 6. 最適化器 | SGD・Adam 等 |
| 7. 初期化と正規化 | 学習の安定化 |
| 8. 学習ループ | PyTorch 実装例 |
入力 $\mathbf{x} = (x_1, \dots, x_p)^\top$、 重み $\mathbf{w}$、 バイアス $b$、 活性化 $\phi$ に対し:
$$z = \mathbf{w}^\top \mathbf{x} + b, \qquad a = \phi(z)$$
記号読み:$z$ は「ゼット」プリアクティベーション(活性化前の値)、 $a$ は「エー」アクティベーション(活性化後)。 $\phi$ は「ファイ」活性化関数。
$\phi$ がステップ関数のとき。 線形分離可能なデータのみ完璧分類。 XOR が解けない(線形分離不能)ことが指摘され、 第一次 AI 冬の時代を招いた。
$\mathbf{x}=(1,2)$、 $\mathbf{w}=(0.5,-0.3)$、 $b=0.1$、 $\phi=$ ReLU のとき:
1953 年代後半に提案、 1986 年の Rumelhart の誤差逆伝播で実用化。 隠れ層を持つことで XOR も解ける。
$L$ 層 MLP において、 各層 $\ell$ で:
$$\mathbf{z}^{(\ell)} = \mathbf{W}^{(\ell)} \mathbf{a}^{(\ell-1)} + \mathbf{b}^{(\ell)}, \qquad \mathbf{a}^{(\ell)} = \phi^{(\ell)}(\mathbf{z}^{(\ell)})$$
記号読み:$\mathbf{W}^{(\ell)}$ は「ダブリュー上付きエル」、 第 $\ell$ 層の重み行列。 入力 $\mathbf{a}^{(0)} = \mathbf{x}$、 出力 $\hat{y} = \mathbf{a}^{(L)}$。
万能近似定理(Cybenko 1989、 Hornik 1991):1層の隠れ層を持つ MLP は十分な幅があれば任意の連続関数を任意の精度で近似できる。 ただし幅は指数的に大きくなりうるため、 実用では深くする。
NN に非線形性を入れる装置。 線形ばかりだと層を重ねても線形のまま。
$$\sigma(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}}$$
出力 (-1, 1)。 ゼロ中心で sigmoid より好まれる場合あり。
$$\mathrm{ReLU}(z) = \max(0, z)$$
$$\mathrm{softmax}(z)_k = \frac{e^{z_k}}{\sum_{j=1}^{K} e^{z_j}}$$
多クラス分類の出力層で、 ロジット $z_k$ を確率に変換。
| タスク | 損失関数 | 出力層 |
|---|---|---|
| 回帰 | MSE / MAE / Huber | 恒等関数 |
| 二値分類 | バイナリ交差エントロピー | Sigmoid |
| 多クラス分類 | 交差エントロピー | Softmax |
| 多ラベル | BCE × クラス数 | クラス別Sigmoid |
$$L_{\text{CE}} = -\sum_{k=1}^{K} y_k \log \hat{p}_k$$
順伝播で予測 → 損失計算 → 勾配を出力側から入力側に向かって計算する。
$$\frac{\partial L}{\partial W^{(\ell)}} = \frac{\partial L}{\partial \mathbf{a}^{(L)}} \cdot \frac{\partial \mathbf{a}^{(L)}}{\partial \mathbf{z}^{(L)}} \cdots \frac{\partial \mathbf{a}^{(\ell)}}{\partial \mathbf{z}^{(\ell)}} \cdot \frac{\partial \mathbf{z}^{(\ell)}}{\partial W^{(\ell)}}$$
各層の局所勾配を計算し、 連鎖律で合成する。 計算複雑度は順伝播と同じ $O(\text{パラメータ数})$。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | import pandas as pd from sklearn.neural_network import MLPRegressor from sklearn.preprocessing import StandardScaler from sklearn.pipeline import Pipeline 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['年度']==2023] X = df[['A1101', 'A4101', 'F2401']].values y = df['D1101'].values pipe = Pipeline([ ('scale', StandardScaler()), ('mlp', MLPRegressor(hidden_layer_sizes=(16, 8), activation='relu', solver='adam', learning_rate_init=0.001, max_iter=2000, early_stopping=True, random_state=42)) ]) scores = cross_val_score(pipe, X, y, cv=5, scoring='r2') print(f'R² (5-fold): {scores.mean():.3f} ± {scores.std():.3f}') |
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 | import torch import torch.nn as nn from torch.utils.data import DataLoader, TensorDataset ## 再現性のための seed 固定 torch.manual_seed(42) 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.Linear(8, 1) ) def forward(self, x): return self.net(x) model = MLP() optimizer = torch.optim.Adam(model.parameters(), lr=1e-3, weight_decay=1e-4) criterion = nn.MSELoss() ## 学習ループ + Early Stopping best_val_loss = float('inf') patience = 20 counter = 0 for epoch in range(200): model.train() for xb, yb in train_loader: pred = model(xb) loss = criterion(pred, yb) optimizer.zero_grad() loss.backward() torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0) ## 勾配クリッピング optimizer.step() ## Early Stopping 判定 model.eval() with torch.no_grad(): val_loss = criterion(model(X_val), y_val).item() if val_loss 0 else: counter += 1 if counter >= patience: print(f'Early stopping at epoch {epoch}') break |
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 | from tensorflow import keras from tensorflow.keras import layers, callbacks model = keras.Sequential([ layers.Input(shape=(3,)), layers.BatchNormalization(), layers.Dense(16, activation='relu', kernel_initializer='he_normal'), layers.Dropout(0.2), layers.Dense(8, activation='relu', kernel_initializer='he_normal'), layers.Dense(1) ]) model.compile(optimizer=keras.optimizers.Adam(1e-3), loss='mse', metrics=['mae']) es = callbacks.EarlyStopping(monitor='val_loss', patience=20, restore_best_weights=True) lr_sched = callbacks.ReduceLROnPlateau(monitor='val_loss', factor=0.5, patience=10) history = model.fit(X_tr, y_tr, validation_split=0.2, epochs=200, batch_size=8, callbacks=[es, lr_sched], verbose=0) ## 学習曲線のプロット import matplotlib.pyplot as plt plt.plot(history.history['loss'], label='train') plt.plot(history.history['val_loss'], label='val') plt.xlabel('Epoch'); plt.ylabel('MSE Loss'); plt.legend() |
SSDSE-B-2026 から「総人口・出生率・高齢化率・大学進学率」の 4 指標を入力に、 「合計特殊出生率」を当てるタスクを考えます。 線形回帰は「重みの 1 回掛け算 + 足し算」一段ですが、 ニューラルネットは「重み付け → 非線形変換 → また重み付け → 非線形変換 → 最後に出力」と何段も積み木を重ねます。 各層の非線形(ReLU や tanh)が「曲がり」を作り、 直線では表現できない複雑な都道府県パターン(例: 進学率が高すぎる都市部では出生率は逆に下がる、 などの非単調関係)を捉えられるようになります。
$$ h^{(l)} = \sigma\!\left( W^{(l)} h^{(l-1)} + b^{(l)} \right), \quad l = 1, 2, \ldots, L $$
$$ \hat{y} = W^{(L+1)} h^{(L)} + b^{(L+1)} $$
ここで $h^{(0)} = x$(入力ベクトル: 標準化された 47 都道府県指標)、 $\sigma$ は活性化関数(ReLU など)、 $W^{(l)}, b^{(l)}$ が層 $l$ の学習パラメータ。 損失 $\mathcal{L} = \frac{1}{n}\sum_i (y_i - \hat{y}_i)^2$ を勾配降下で最小化します。
| 記号 | 意味 | SSDSE-B-2026 での具体例 |
|---|---|---|
$x$ | 入力ベクトル | 標準化済みの 47 都道府県指標 (A1101 人口, A4101 出生率, F2401 小売額…) |
$W^{(l)}$ | 層 $l$ の重み行列 | 学習で更新する パラメータ。 1 層目は (16, 3) 形状 |
$\sigma$ | 活性化関数 | ReLU、 tanh、 sigmoid、 GELU など。 非線形性の源泉 |
$\hat{y}$ | 予測値 | D1101 県民所得(回帰)または合計特殊出生率の予測 |
$\mathcal{L}$ | 損失関数 | MSE = $\frac{1}{47}\sum(y_i-\hat{y}_i)^2$ |
3 都道府県だけ取り出して、 標準化済み入力 $x$ から 1 層目の中間出力 $h^{(1)}$ までを手計算してみます(重み $W^{(1)}$ は学習後の代表値を仮定)。
| 都道府県 | $x_1$ 人口 | $x_2$ 出生率 | $x_3$ 小売額 | $z = W \cdot x + b$ | $h = \text{ReLU}(z)$ |
|---|---|---|---|---|---|
| 東京都 | +2.84 | +0.41 | +2.62 | +1.87 | +1.87 |
| 北海道 | +0.62 | -0.59 | +0.31 | +0.18 | +0.18 |
| 沖縄県 | -0.66 | +1.92 | -0.74 | -0.42 | 0.00 |
ReLU で 負値は 0 にクリップ されるため、 沖縄県は 1 層目で「死んだニューロン」状態。 並列の別ニューロンが沖縄パターンを拾うように冗長設計するのがポイント。
import numpy as np
X = np.array([[2.84, 0.41, 2.62],
[0.62,-0.59, 0.31],
[-0.66, 1.92,-0.74]])
W = np.array([[0.4, 0.2, 0.3]])
b = np.array([-0.1])
z = X @ W.T + b
h = np.maximum(0, z) # ReLU
print(z.round(2)); print(h.round(2))