深入解析bp反向传播算法计算题:从数学原理到代码实现

1次阅读
没有评论

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

image.webp

背景与原理

反向传播(Backpropagation,简称 BP)是训练神经网络的核心算法,通过计算损失函数对网络参数的梯度,指导参数更新方向。理解 BP 算法的关键在于掌握链式求导法则的应用,以及如何在计算图中高效地传递梯度。

深入解析 bp 反向传播算法计算题:从数学原理到代码实现

对于初学者来说,手动推导 BP 过程不仅能加深理解,还能帮助避免常见的实现错误。下面我们从最简单的单神经元开始,逐步扩展到全连接网络。

数学推导

单神经元示例

考虑一个具有 sigmoid 激活函数的神经元:

z = w*x + b
a = σ(z) = 1/(1+e^{-z})

假设损失函数为 L =(y-a)^2/2,我们需要计算∂L/∂w 和∂L/∂b:

  1. 计算输出层梯度:∂L/∂a = -(y-a)
  2. 通过激活函数:∂a/∂z = σ(z)(1-σ(z)) = a(1-a)
  3. 组合得到:∂L/∂z = (∂L/∂a)(∂a/∂z) = -(y-a)a(1-a)
  4. 最终参数梯度:
  5. ∂L/∂w = (∂L/∂z)*x
  6. ∂L/∂b = ∂L/∂z

全连接层推广

对于多层网络,梯度计算遵循相同的链式法则,但需要从输出层向输入层反向传递。关键公式:

δ^l = (W^{l+1}^T δ^{l+1}) ⊙ σ'(z^l)
∂L/∂W^l = δ^l a^{l-1}^T
∂L/∂b^l = δ^l

代码实现

import numpy as np

class NeuralNetwork:
    def __init__(self, layer_sizes):
        # 初始化权重和偏置
        self.weights = [np.random.randn(y, x)/np.sqrt(x) 
                        for x, y in zip(layer_sizes[:-1], layer_sizes[1:])]
        self.biases = [np.zeros((y, 1)) for y in layer_sizes[1:]]

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

    def sigmoid_prime(self, z):
        s = self.sigmoid(z)
        return s*(1-s)

    def forward(self, x):
        """前向传播计算网络输出"""
        activation = x
        for w, b in zip(self.weights, self.biases):
            z = np.dot(w, activation) + b
            activation = self.sigmoid(z)
        return activation

    def backward(self, x, y):
        """反向传播计算梯度"""
        # 前向传播并保存中间值
        zs, activations = [], [x]
        for w, b in zip(self.weights, self.biases):
            z = np.dot(w, activations[-1]) + b
            zs.append(z)
            activations.append(self.sigmoid(z))

        # 反向传播
        nabla_w = [np.zeros(w.shape) for w in self.weights]
        nabla_b = [np.zeros(b.shape) for b in self.biases]

        # 输出层误差
        delta = (activations[-1] - y) * self.sigmoid_prime(zs[-1])
        nabla_b[-1] = delta
        nabla_w[-1] = np.dot(delta, activations[-2].T)

        # 隐藏层误差
        for l in range(2, len(self.weights)+1):
            z = zs[-l]
            sp = self.sigmoid_prime(z)
            delta = np.dot(self.weights[-l+1].T, delta) * sp
            nabla_b[-l] = delta
            nabla_w[-l] = np.dot(delta, activations[-l-1].T)

        return nabla_w, nabla_b

案例演示:XOR 问题

让我们用 2 -2- 1 网络解决 XOR 问题:

  1. 输入数据:

    X = [[0,0], [0,1], [1,0], [1,1]]
    y = [0, 1, 1, 0]

  2. 第一次前向传播(随机权重):

  3. 输入[0,1],隐藏层输出[0.6, 0.4]
  4. 最终输出 0.55(期望 1)

  5. 反向传播计算:

  6. 输出层误差:0.55(1-0.55)(0.55-1) ≈ -0.11
  7. 隐藏层误差:[w11δ_out, w21δ_out] ⊙ [a1(1-a1), a2(1-a2)]

  8. 参数更新(学习率 0.5):

  9. 输出层权重变化:-0.11 * [0.6, 0.4]
  10. 隐藏层权重变化:按相同原理计算

常见问题分析

  1. 梯度消失:深层网络中,小梯度连乘会导致早期层更新缓慢。解决方案:
  2. 使用 ReLU 等非饱和激活函数
  3. 残差连接

  4. 梯度爆炸:大梯度连乘可能导致数值溢出。解决方案:

  5. 梯度裁剪
  6. 权重正则化

  7. 学习率选择:

  8. 太大:震荡或不收敛
  9. 太小:训练缓慢
  10. 建议:使用自适应优化器 (Adam) 或学习率衰减

最佳实践

  1. 梯度检查:用数值梯度验证反向传播实现

    def gradient_check(network, x, y, epsilon=1e-7):
        # 实现数值梯度计算并与反向传播结果比较

  2. 批量归一化:加速训练并减少对初始化的敏感度

  3. 早停法:监控验证集性能防止过拟合

延伸思考

  1. 如何将 BP 算法扩展到卷积神经网络(CNN)?计算图会有哪些变化?

  2. 在循环神经网络 (RNN) 中,BPTT(随时间反向传播)与标准 BP 有何异同?

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