BP算法反向传播原理详解与Python实现:从数学推导到代码实战

1次阅读
没有评论

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

image.webp

一、从单层感知机到多层网络

反向传播 (Backpropagation, BP) 算法是神经网络训练的核心。我们先从单层感知机的梯度计算开始理解基本原理。

BP 算法反向传播原理详解与 Python 实现:从数学推导到代码实战

对于单个神经元,其输出可以表示为:

$$a = \sigma(w^Tx + b)$$

其中 $\sigma$ 是激活函数,$w$ 是权重(weights),$b$ 是偏置(bias)。

  1. 单层网络的损失函数对权重的梯度为:
    $$\frac{\partial L}{\partial w} = \frac{\partial L}{\partial a} \cdot \frac{\partial a}{\partial z} \cdot \frac{\partial z}{\partial w}$$
    这就是链式法则 (Chain Rule) 的体现

  2. 扩展到多层网络时,误差需要从输出层逐层反向传播:
    $$\delta^{(l)} = ((w^{(l+1)})^T \delta^{(l+1)}) \odot \sigma'(z^{(l)})$$
    其中 $\odot$ 表示逐元素相乘,$\delta$ 是误差项

二、关键数学推导

以三层网络为例,完整的反向传播过程:

  1. 输出层误差:
    $$\delta^{(3)} = \nabla_a L \odot \sigma'(z^{(3)})$$

  2. 隐藏层误差:
    $$\delta^{(2)} = (w^{(3)})^T \delta^{(3)} \odot \sigma'(z^{(2)})$$

  3. 参数更新:
    $$\Delta w^{(l)} = -\eta \delta^{(l)} (a^{(l-1)})^T$$
    $$\Delta b^{(l)} = -\eta \delta^{(l)}$$

三、Python 实现

import numpy as np

# 激活函数实现
def sigmoid(x):
    return 1/(1+np.exp(-x))

def sigmoid_derivative(x):
    s = sigmoid(x)
    return s*(1-s)

# 前向传播
def forward_pass(x, weights, biases):
    layer_outputs = []
    a = x

    for w, b in zip(weights, biases):
        z = np.dot(w, a) + b
        a = sigmoid(z)
        layer_outputs.append((z, a))

    return layer_outputs

# 反向传播
def backward_pass(x, y, layer_outputs, weights):
    gradients_w = []
    gradients_b = []
    delta = None

    # 从输出层开始反向计算
    for l in range(len(weights)-1, -1, -1):
        z, a = layer_outputs[l]

        if l == len(weights)-1: # 输出层
            delta = (a - y) * sigmoid_derivative(z)
        else: # 隐藏层
            delta = np.dot(weights[l+1].T, delta) * sigmoid_derivative(z)

        # 计算梯度
        a_prev = x if l == 0 else layer_outputs[l-1][1]
        grad_w = np.dot(delta, a_prev.T)
        grad_b = delta

        gradients_w.insert(0, grad_w)
        gradients_b.insert(0, grad_b)

    return gradients_w, gradients_b

四、数值稳定性处理

  1. 梯度消失问题:使用 ReLU 等激活函数替代 Sigmoid
  2. 梯度爆炸问题:梯度裁剪(Gradient Clipping)
  3. 权重初始化:Xavier 初始化或 He 初始化

五、避坑指南

  1. 学习率设置:
  2. 常见初始值 0.01
  3. 可以使用学习率衰减策略

  4. 参数初始化方法对比:

  5. 全零初始化:导致对称性问题
  6. 随机初始化:需要合理设置方差
  7. Xavier 初始化:适合 Sigmoid/tanh
  8. He 初始化:适合 ReLU

  9. 梯度检查实现:

    def gradient_check(x, y, weights, biases, epsilon=1e-7):
        # 计算数值梯度
        numerical_grads = []
        for w in weights:
            grad = np.zeros_like(w)
            it = np.nditer(w, flags=['multi_index'])
    
            while not it.finished:
                idx = it.multi_index
                original = w[idx]
    
                w[idx] = original + epsilon
                cost_plus = compute_cost(x, y)
    
                w[idx] = original - epsilon
                cost_minus = compute_cost(x, y)
    
                grad[idx] = (cost_plus - cost_minus)/(2*epsilon)
                w[idx] = original
    
                it.iternext()
    
            numerical_grads.append(grad)
    
        return numerical_grads

六、思考与扩展

  1. 如何扩展为 Mini-batch 训练?
  2. 计算 batch 内样本梯度的平均值
  3. 使用矩阵运算优化批量处理

  4. 自动微分 vs 手动实现:

  5. PyTorch/TensorFlow 的 autograd
  6. 手动实现有助于深入理解原理

希望这篇从理论到实践的完整介绍,能帮助你真正理解 BP 算法的精髓。建议尝试修改代码中的网络结构,观察不同激活函数的表现,这是掌握神经网络训练的最佳方式。

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