共计 1965 个字符,预计需要花费 5 分钟才能阅读完成。
拓扑结构图解
先看一个经典的三层 BP 网络结构(输入层 - 隐藏层 - 输出层):

输入层 隐藏层 输出层
x1───────╮ h1───────╮ y1
\ │ \ │
\ │ \ │
x2──┼────── h2──┼──── y2
/ │ / │
/ │ / │
x3───────╯ h3───────╯
- 输入层维度:n_samples × n_features(如 1000×3)
- 隐藏层维度:n_samples × n_hidden(如 1000×4)
- 输出层维度:n_samples × n_outputs(如 1000×2)
数学推导
前向传播
-
隐藏层计算:
$$h = \sigma(W_{xh}X + b_h)$$
其中 $\sigma$ 为 sigmoid 函数:
$$\sigma(z) = \frac{1}{1+e^{-z}}$$ -
输出层计算:
$$\hat{y} = softmax(W_{hy}h + b_y)$$
softmax 导数特性:
$$\frac{\partial \hat{y}i}{\partial z_j} = \hat{y}_i(\delta_j)$$}-\hat{y
反向传播(链式法则图解)
关键梯度计算流程:
损失 L ← ∂L/∂ŷ ← ∂ŷ/∂z ← ∂z/∂h ← ∂h/∂z'← ∂z'/∂W
CE 损失 softmax Why sigmoid X
-
输出层梯度:
$$\frac{\partial L}{\partial W_{hy}} = (\hat{y} – y) \cdot h^T$$ -
隐藏层梯度:
$$\frac{\partial L}{\partial W_{xh}} = (W_{hy}^T(\hat{y}-y)) \odot \sigma'(z’) \cdot X^T$$
Python 实现
核心代码框架(完整实现见文末 GitHub 链接):
import numpy as np
class BPNetwork:
def __init__(self, input_size, hidden_size, output_size):
# 初始化权重(Xavier 初始化)self.Wxh = np.random.randn(input_size, hidden_size) * np.sqrt(2/input_size)
self.Why = np.random.randn(hidden_size, output_size) * np.sqrt(2/hidden_size)
def forward(self, X):
# 前向传播
self.h = 1/(1+np.exp(-(X @ self.Wxh))) # sigmoid 激活
self.y_hat = np.exp(self.h @ self.Why) / np.sum(np.exp(self.h @ self.Why), axis=1, keepdims=True)
return self.y_hat
def backward(self, X, y, lr=0.01):
# 反向传播
grad_y = self.y_hat - y
grad_Why = self.h.T @ grad_y
grad_h = (grad_y @ self.Why.T) * (self.h * (1-self.h)) # sigmoid 导数
grad_Wxh = X.T @ grad_h
# 梯度裁剪(防止爆炸)for grad in [grad_Wxh, grad_Why]:
np.clip(grad, -1, 1, out=grad)
# 参数更新
self.Wxh -= lr * grad_Wxh
self.Why -= lr * grad_Why
实战建议
学习率衰减
Cosine 退火示例:
def cosine_annealing(lr_min, lr_max, T_max, epoch):
return lr_min + 0.5*(lr_max-lr_min)*(1 + np.cos(epoch*np.pi/T_max))
隐层神经元数量
Andrew Ng 经验公式:
$$n_{hidden} = \sqrt{n_{input} \cdot n_{output}} + \alpha$$
其中 $\alpha$ 为 10~100 间的调节参数
性能优化
- 批量归一化(BatchNorm):
- 将每层输入标准化为 N(0,1) 分布
-
缓解梯度消失问题,允许使用更大学习率
-
激活函数对比 :
- ReLU 收敛速度通常比 Sigmoid 快 3 - 5 倍
- LeakyReLU 可解决神经元 ” 死亡 ” 问题
扩展思考
- 回归任务改造 :
- 输出层去掉 softmax
- 使用 MSE 损失函数
-
最后一层可尝试线性激活
-
优化器内存对比 :
| 优化器 | 内存占用 | 特点 |
|——–|———|——|
| SGD | O(1) | 需手动调学习率 |
| Adam | O(n) | 存储一阶 / 二阶动量 |
完整代码和 TensorBoard 可视化示例已上传 GitHub:https://github.com/example/bpnn-demo
在实际项目中,建议先用小批量数据验证网络结构有效性,再逐步增加复杂度。遇到梯度异常时,优先检查激活函数导数计算是否正确。
