共计 1988 个字符,预计需要花费 5 分钟才能阅读完成。
前向传播的数学原理
神经网络的前向传播本质是加权求和与非线性变换的堆叠。以单隐层网络为例,设输入层为 $\mathbf{x}$,隐藏层输出为:

$$
\mathbf{h} = \sigma(\mathbf{W}_1\mathbf{x} + \mathbf{b}_1)
$$
输出层结果为:
$$
\mathbf{y} = \text{softmax}(\mathbf{W}_2\mathbf{h} + \mathbf{b}_2)
$$
其中 $\sigma$ 代表 sigmoid 激活函数:
$$
\sigma(z) = \frac{1}{1+e^{-z}}
$$
反向传播的链式法则
- 输出层梯度计算
使用交叉熵损失函数 $L$,对输出层权重求偏导:
$$
\frac{\partial L}{\partial \mathbf{W}_2} = \frac{\partial L}{\partial \mathbf{y}} \cdot \frac{\partial \mathbf{y}}{\partial (\mathbf{W}_2\mathbf{h})} \cdot \frac{\partial (\mathbf{W}_2\mathbf{h})}{\partial \mathbf{W}_2}
$$
- 隐藏层梯度计算
误差继续向隐藏层传播:
$$
\frac{\partial L}{\partial \mathbf{W}_1} = \frac{\partial L}{\partial \mathbf{h}} \cdot \frac{\partial \mathbf{h}}{\partial (\mathbf{W}_1\mathbf{x})} \cdot \frac{\partial (\mathbf{W}_1\mathbf{x})}{\partial \mathbf{W}_1}
$$
其中 sigmoid 导数有重要性质:
$$
\sigma'(z) = \sigma(z)(1-\sigma(z))
$$
Python 实现核心代码
import numpy as np
class NeuralNetwork:
def __init__(self, input_size, hidden_size):
# 初始化权重(避免过大值)self.W1 = np.random.randn(input_size, hidden_size) * 0.01
self.b1 = np.zeros(hidden_size)
self.W2 = np.random.randn(hidden_size, 1) * 0.01
self.b2 = np.zeros(1)
def forward(self, x):
self.z1 = np.dot(x, self.W1) + self.b1
self.h = 1 / (1 + np.exp(-self.z1)) # sigmoid 激活
self.z2 = np.dot(self.h, self.W2) + self.b2
return self.z2
def backward(self, x, y, lr=0.1):
# 输出层梯度
dL_dz2 = self.z2 - y # 假设使用 MSE 损失
dW2 = np.dot(self.h.T, dL_dz2)
# 隐藏层梯度
dL_dh = np.dot(dL_dz2, self.W2.T)
dh_dz1 = self.h * (1 - self.h) # sigmoid 导数
dW1 = np.dot(x.T, dL_dh * dh_dz1)
# 参数更新
self.W2 -= lr * dW2
self.W1 -= lr * dW1
常见问题与优化
- 梯度消失问题
- sigmoid 函数在输入绝对值较大时梯度接近 0
-
解决方案:使用 ReLU 激活函数 $\max(0,x)$
-
权重初始化技巧
- 过大初始化导致梯度爆炸
-
Xavier 初始化:$W \sim N(0, \sqrt{1/n_{in}})$
-
学习率衰减
lr = initial_lr / (1 + decay_rate * epoch)
梯度检查实现
def gradient_check(x, y, epsilon=1e-7):
# 计算数值梯度
original = network.W1[0,0]
network.W1[0,0] = original + epsilon
loss_plus = compute_loss(x, y)
network.W1[0,0] = original - epsilon
loss_minus = compute_loss(x, y)
numerical_grad = (loss_plus - loss_minus) / (2*epsilon)
# 与反向传播结果对比
print(f"数值梯度: {numerical_grad}, 解析梯度: {analytical_grad}")
延伸思考
- 如何修改代码实现带动量的随机梯度下降?
- 批量归一化 (BatchNorm) 层应该加在什么位置?
- 当训练集准确率高但测试集差时可能是什么原因?
通过这次推导实践,我深刻体会到神经网络本质就是复合函数的梯度计算。建议初学者一定要亲手推导一次 BP 算法,这会比看十篇教程更有收获。
