学習目標
- 順伝播における入力から出力までのデータフローを理解する
- 効率的なニューラルネットワーク計算のための行列演算をマスターする
- 重み行列とバイアスベクトルの役割を理解する
- 計算グラフと自動微分への重要性を学ぶ
- MSEとCross Entropy損失関数を理解し実装する
- NumPyとPyTorchで全結合層を実装する
1. 順伝播(Forward Propagation)
1.1 入力から出力までのデータフロー
順伝播とは、入力が与えられたときにニューラルネットワークの出力を計算するプロセスです。データは層ごとにネットワークを流れ、各層でデータが変換され、最終的な出力が生成されます。
各層での計算は以下のステップで行われます:
- 線形変換:入力に重みを掛けてバイアスを加える
- 活性化:非線形活性化関数を適用する
単一層の数式表現:
$$\mathbf{z} = \mathbf{W}\mathbf{x} + \mathbf{b}$$
$$\mathbf{a} = f(\mathbf{z})$$
ここで:
- $\mathbf{x}$:入力ベクトル(または前の層からの活性化)
- $\mathbf{W}$:重み行列
- $\mathbf{b}$:バイアスベクトル
- $\mathbf{z}$:活性化前の値(線形結合)
- $f$:活性化関数
- $\mathbf{a}$:活性化出力
1.2 行列演算による効率的な計算
ニューロンの出力を1つずつ計算する代わりに、行列演算を使用して層内のすべてのニューロンを同時に処理します。これはより簡潔なだけでなく、最適化された線形代数ライブラリにより大幅に高速です。
なぜ行列演算?
- 並列化:GPUは数千の行列演算を同時に実行可能
- 最適化:BLASなどのライブラリは行列乗算に高度に最適化
- バッチ処理:複数のサンプルを一度に処理可能
import numpy as np
def forward_layer(X, W, b, activation_fn):
"""
単一層の順伝播
Parameters:
-----------
X : ndarray, shape (batch_size, n_input)
入力データ
W : ndarray, shape (n_input, n_output)
重み行列
b : ndarray, shape (1, n_output)
バイアスベクトル
activation_fn : function
活性化関数
Returns:
--------
A : ndarray, shape (batch_size, n_output)
活性化出力
Z : ndarray, shape (batch_size, n_output)
活性化前の値(逆伝播用)
"""
# 線形変換: Z = X @ W + b
Z = np.dot(X, W) + b
# 活性化関数を適用
A = activation_fn(Z)
return A, Z
# 使用例
np.random.seed(42)
# 入力: 4サンプル、3特徴量
X = np.array([
[1.0, 2.0, 3.0],
[4.0, 5.0, 6.0],
[7.0, 8.0, 9.0],
[10.0, 11.0, 12.0]
])
# 重み: 3入力 -> 2出力
W = np.random.randn(3, 2) * 0.01
b = np.zeros((1, 2))
# ReLU活性化
relu = lambda x: np.maximum(0, x)
A, Z = forward_layer(X, W, b, relu)
print("入力形状:", X.shape)
print("出力形状:", A.shape)
2. 重み行列とバイアスベクトル
2.1 重みの役割
重み行列 $\mathbf{W}$ は、各入力特徴量が各出力ニューロンにどれだけ強く影響するかを決定します。
2.2 重みの初期化
適切な重みの初期化は効果的な学習に不可欠です。
| 方法 | 式 | 適用場面 |
|---|---|---|
| ゼロ初期化 | $W = 0$ | 使用禁止(対称性の問題) |
| 小さなランダム値 | $W \sim \mathcal{N}(0, 0.01)$ | 単純なネットワーク |
| Xavier/Glorot | $W \sim \mathcal{N}(0, \sqrt{\frac{2}{n_{in} + n_{out}}})$ | Sigmoid、tanh |
| He | $W \sim \mathcal{N}(0, \sqrt{\frac{2}{n_{in}}})$ | ReLU |
2.3 バイアスの重要性
バイアスベクトル $\mathbf{b}$ は活性化関数を水平方向にシフトさせます。バイアスがなければ、入力がゼロのときに活性化前の値 $z$ もゼロになり、ネットワークの表現力が制限されます。
3. 層の接続と計算グラフ
3.1 計算グラフの概念
計算グラフは、ニューラルネットワークにおける演算の順序を表す有向グラフです。各ノードは演算(加算、乗算、活性化関数)を表し、エッジはデータフローを表します。
3.2 自動微分の基礎
計算グラフの主な利点は自動微分を可能にすることです。中間値を保存し連鎖律を適用することで、すべてのパラメータに対する勾配を効率的に計算できます。
連鎖律(Chain Rule):$y = f(g(x))$ のとき
$$\frac{dy}{dx} = \frac{dy}{dg} \cdot \frac{dg}{dx}$$
これにより、出力から入力へ層ごとに勾配を計算できます。
4. 損失関数(MSE、Cross Entropy)
損失関数(コスト関数、目的関数とも呼ばれる)は、ネットワークの予測が真の値とどれだけ一致しているかを測定します。学習の目標はこの損失を最小化することです。
4.1 回帰問題:MSE(Mean Squared Error)
MSEは回帰問題の標準的な損失関数です。
式:
$$\mathcal{L}_{MSE} = \frac{1}{n} \sum_{i=1}^{n} (y_i - \hat{y}_i)^2$$
import numpy as np
def mse_loss(y_true, y_pred):
"""
平均二乗誤差損失
Parameters:
-----------
y_true : ndarray
真の値
y_pred : ndarray
予測値
Returns:
--------
loss : float
MSE損失値
"""
return np.mean((y_true - y_pred) ** 2)
# 使用例
y_true = np.array([1.0, 2.0, 3.0, 4.0, 5.0])
y_pred = np.array([1.2, 1.8, 3.1, 3.9, 5.2])
loss = mse_loss(y_true, y_pred)
print(f"MSE損失: {loss:.4f}")
4.2 分類問題:Cross Entropy Loss
Cross Entropyは分類問題の標準的な損失関数です。
多クラス分類のCategorical Cross Entropy
$$\mathcal{L}_{CE} = -\frac{1}{n} \sum_{i=1}^{n} \sum_{c=1}^{C} y_{i,c} \log(\hat{y}_{i,c})$$
import numpy as np
def categorical_cross_entropy(y_true, y_pred, epsilon=1e-15):
"""
Categorical Cross Entropy損失
Parameters:
-----------
y_true : ndarray, shape (n_samples, n_classes)
真のラベル(one-hot encoded)
y_pred : ndarray, shape (n_samples, n_classes)
予測確率
Returns:
--------
loss : float
CCE損失値
"""
y_pred = np.clip(y_pred, epsilon, 1 - epsilon)
return -np.mean(np.sum(y_true * np.log(y_pred), axis=1))
# 使用例
y_true = np.array([
[1, 0, 0, 0], # クラス0
[0, 1, 0, 0], # クラス1
[0, 0, 0, 1] # クラス3
])
y_pred_good = np.array([
[0.9, 0.05, 0.03, 0.02],
[0.1, 0.7, 0.1, 0.1],
[0.05, 0.05, 0.1, 0.8]
])
loss = categorical_cross_entropy(y_true, y_pred_good)
print(f"Cross Entropy損失: {loss:.4f}")
4.3 損失関数の選択基準
| タスク | 出力活性化 | 損失関数 |
|---|---|---|
| 回帰 | 線形(なし) | MSE、MAE、Huber |
| 二値分類 | Sigmoid | Binary Cross Entropy |
| 多クラス分類 | Softmax | Categorical Cross Entropy |
5. 完全結合層の実装
5.1 NumPyによる実装
import numpy as np
class DenseLayer:
"""
全結合(Dense)層の実装
"""
def __init__(self, n_input, n_output, activation='relu'):
self.n_input = n_input
self.n_output = n_output
self.activation = activation
# He初期化
self.W = np.random.randn(n_input, n_output) * np.sqrt(2.0 / n_input)
self.b = np.zeros((1, n_output))
self.cache = {}
def _activate(self, Z):
if self.activation == 'relu':
return np.maximum(0, Z)
elif self.activation == 'softmax':
exp_Z = np.exp(Z - np.max(Z, axis=1, keepdims=True))
return exp_Z / np.sum(exp_Z, axis=1, keepdims=True)
else:
return Z
def forward(self, X):
self.cache['X'] = X
Z = np.dot(X, self.W) + self.b
self.cache['Z'] = Z
A = self._activate(Z)
self.cache['A'] = A
return A
# ネットワーク構築
np.random.seed(42)
layer1 = DenseLayer(4, 8, activation='relu')
layer2 = DenseLayer(8, 3, activation='softmax')
X = np.array([[5.1, 3.5, 1.4, 0.2]])
A1 = layer1.forward(X)
output = layer2.forward(A1)
print("入力形状:", X.shape)
print("出力形状:", output.shape)
print("予測確率:", output)
5.2 PyTorchによる実装
import torch
import torch.nn as nn
class SimpleNetwork(nn.Module):
"""
PyTorchによるシンプルな全結合ネットワーク
"""
def __init__(self, input_size, hidden_sizes, output_size):
super(SimpleNetwork, self).__init__()
layers = []
prev_size = input_size
for hidden_size in hidden_sizes:
layers.append(nn.Linear(prev_size, hidden_size))
layers.append(nn.ReLU())
prev_size = hidden_size
layers.append(nn.Linear(prev_size, output_size))
self.network = nn.Sequential(*layers)
def forward(self, x):
return self.network(x)
# モデル作成
model = SimpleNetwork(input_size=4, hidden_sizes=[8, 4], output_size=3)
print("モデルアーキテクチャ:")
print(model)
演習問題
演習1:手動での順伝播計算
問題:2入力、2隠れニューロン、2出力のネットワークで、与えられた重みとバイアスを使って出力を手計算し、コードで検証してください。
演習2:損失関数の比較
問題:二値分類問題で、MSEとBinary Cross Entropyの損失がどのように変化するかを比較し、なぜ分類にはCross Entropyが好まれるか説明してください。
演習3:Huber損失の実装
問題:MSEとMAEを組み合わせたHuber損失を実装し、外れ値に対する挙動をMSE、MAEと比較してください。
演習4:バッチ処理
問題:ミニバッチでデータを処理する関数を実装し、バッチサイズ1、32、128、1000での処理時間を測定してください。
演習5:重み初期化の実験
問題:5層ネットワークで、ゼロ、ランダム小、Xavier、He初期化を比較し、各層での活性化の統計量をプロットしてください。
演習6:計算グラフの可視化
問題:2層ネットワークの計算グラフを描き、すべての演算をノードとして、エッジにテンソル形状をラベル付けしてください。
まとめ
この章では、ニューラルネットワークがデータを処理する仕組みを学びました:
- 順伝播:データは層ごとに流れ、各層で線形変換と活性化が行われる
- 行列演算:効率的なバッチ処理と並列化を可能にする
- 重み行列:接続強度を制御し、適切な初期化(Xavier、He)が重要
- バイアスベクトル:活性化関数のシフトを可能にする
- 計算グラフ:演算をグラフとして表現し、自動微分を可能にする
- 損失関数:回帰にはMSE、分類にはCross Entropy
次章の予告:第3章では、学習アルゴリズム—勾配降下法と誤差逆伝播法を使ってニューラルネットワークが損失を最小化するために重みをどのように調整するかを学びます。