BP神经网络误差反向传播:从数学原理到Python实现

1次阅读
没有评论

共计 1970 个字符,预计需要花费 5 分钟才能阅读完成。

image.webp

为什么需要多层网络?

单层感知机(Perceptron)只能解决线性可分问题,连简单的异或(XOR)分类都无法实现。1986 年 Rumelhart 等人提出的误差反向传播算法(Backpropagation),让多层神经网络具备了学习非线性特征的能力。

BP 神经网络误差反向传播:从数学原理到 Python 实现

举个直观例子:识别手写数字时,第一层可能学习边缘特征,第二层组合成笔画,第三层就能识别完整字符。这种层次化特征提取正是深度学习的基础。

数学推导:梯度是如何计算的?

以三层网络(输入层 - 隐藏层 - 输出层)为例,设:

  • $W^{(1)}$ 为输入到隐藏层的权重矩阵
  • $W^{(2)}$ 为隐藏到输出层的权重矩阵
  • $\sigma$ 为 Sigmoid 激活函数
  • $L$ 为均方误差损失函数

根据链式法则,输出层权重的梯度计算如下:

$$
\frac{\partial L}{\partial W^{(2)}} = \underbrace{(y – \hat{y})}{\text{误差项}} \cdot \underbrace{\sigma'(z^{(2)})}
$$}} \cdot \underbrace{a^{(1)}}_{\text{隐藏层输出}

而隐藏层权重的梯度需要继续反向传播:

$$
\frac{\partial L}{\partial W^{(1)}} = \left(\frac{\partial L}{\partial W^{(2)}} \cdot W^{(2)}\right) \cdot \sigma'(z^{(1)}) \cdot x
$$

这个逐层回溯的过程就像把误差信号从输出端「分配」回各层权重,因此得名「反向传播」。

Python 实现关键步骤

import numpy as np

class NeuralNetwork:
    def __init__(self, input_size, hidden_size, output_size):
        self.W1 = np.random.randn(input_size, hidden_size) * 0.01
        self.W2 = np.random.randn(hidden_size, output_size) * 0.01

    def sigmoid(self, z):
        return 1 / (1 + np.exp(-z))

    def sigmoid_derivative(self, z):
        return self.sigmoid(z) * (1 - self.sigmoid(z))

    def forward(self, X):
        self.z1 = np.dot(X, self.W1)
        self.a1 = self.sigmoid(self.z1)
        self.z2 = np.dot(self.a1, self.W2)
        self.a2 = self.sigmoid(self.z2)
        return self.a2

    def backward(self, X, y, lr=0.1):
        # 输出层梯度
        error = self.a2 - y
        dW2 = np.dot(self.a1.T, error * self.sigmoid_derivative(self.z2))

        # 隐藏层梯度
        hidden_error = np.dot(error, self.W2.T)
        dW1 = np.dot(X.T, hidden_error * self.sigmoid_derivative(self.z1))

        # 更新权重
        self.W2 -= lr * dW2
        self.W1 -= lr * dW1

实战经验与调参技巧

  1. 学习率选择:
  2. 太大(如 >0.5)会导致震荡甚至发散
  3. 太小(如 <0.001)训练速度极慢
  4. 建议从 0.1 开始尝试,配合学习率衰减

  5. 梯度消失问题:

  6. Sigmoid 函数的导数最大仅 0.25,深层网络梯度会指数级减小
  7. 解决方案:改用 ReLU 激活函数、残差连接、批归一化

  8. 初始化技巧:

  9. 权重初始值过大会导致神经元饱和
  10. 推荐 Xavier 初始化:$W \sim N(0, \sqrt{1/n_{in}})$

常见错误排查

  • 维度不匹配:
  • 检查每层输入的 feature 维度是否与权重矩阵匹配
  • 典型错误:(batch_size, 10)与 (20, 30) 矩阵相乘会报错

  • 激活函数选择:

  • 输出层用 Sigmoid 做二分类,Softmax 做多分类
  • 隐藏层优先选用 ReLU 避免梯度消失

  • 数值不稳定:

  • 遇到 NaN 值时检查是否出现除零或 log(0)
  • 对交叉熵损失增加 epsilon=1e- 7 的偏移量

思考题

  1. 如何改进代码支持 mini-batch 训练?
  2. 修改 forward/backward 支持矩阵输入
  3. 每个 epoch 随机打乱数据并分 batch

  4. ReLU 激活函数对梯度传播的影响?

  5. 正向传播时缓解梯度消失
  6. 负输入导致「神经元死亡」问题

  7. 二阶优化算法相比 SGD 的优势与代价?

  8. Adam/Adagrad 自适应学习率更快收敛
  9. 需要存储更多参数(如动量、二阶矩估计)

通过这个实现,你会发现神经网络本质上是在用梯度下降寻找一个高维空间中的最优曲面。理解反向传播的数学本质,才能更好地应对现代深度学习中的各种变体。

正文完
 0
评论(没有评论)