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

1次阅读
没有评论

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

image.webp

核心概念

反向传播(Backpropagation)是神经网络训练的核心算法,通过计算损失函数对网络参数的梯度,指导参数更新方向。其本质是链式法则的递归应用,将误差从输出层逐层反向传播至输入层。

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

数学推导

1. 基础符号定义

  • $x^{(i)}$: 第 i 层输入
  • $W^{(i)}$: 第 i 层权重矩阵
  • $z^{(i)} = W^{(i)}x^{(i-1)} + b^{(i)}$: 加权输入
  • $a^{(i)} = \sigma(z^{(i)})$: 激活输出
  • $L$: 损失函数

2. 链式法则分解

输出层误差:
$$\delta^{(L)} = \frac{\partial L}{\partial z^{(L)}} = \frac{\partial L}{\partial a^{(L)}} \odot \sigma'(z^{(L)})$$

隐藏层误差传播:
$$\delta^{(l)} = ((W^{(l+1)})^T \delta^{(l+1)}) \odot \sigma'(z^{(l)})$$

3. 参数梯度计算

权重梯度:
$$\frac{\partial L}{\partial W^{(l)}} = \delta^{(l)} (a^{(l-1)})^T$$

偏置梯度:
$$\frac{\partial L}{\partial b^{(l)}} = \delta^{(l)}$$

代码实现

import numpy as np

class NeuralNetwork:
    def __init__(self, layers):
        self.weights = [np.random.randn(y, x) * 0.1 
                       for x, y in zip(layers[:-1], layers[1:])]
        self.biases = [np.zeros((y, 1)) for y in layers[1:]]

    def forward(self, x):
        """前向传播"""
        self.activations = [x]
        self.zs = []
        for w, b in zip(self.weights, self.biases):
            z = np.dot(w, self.activations[-1]) + b
            self.zs.append(z)
            self.activations.append(self.sigmoid(z))
        return self.activations[-1]

    def backward(self, x, y):
        """反向传播"""
        nabla_w = [np.zeros(w.shape) for w in self.weights]
        nabla_b = [np.zeros(b.shape) for b in self.biases]

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

        # 隐藏层误差反向传播
        for l in range(2, len(self.weights)+1):
            z = self.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, self.activations[-l-1].T)

        return nabla_w, nabla_b

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

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

性能考量

1. 计算复杂度分析

  • 前向传播:$O(\sum_{l=1}^L n_{l-1}n_l)$
  • 反向传播:与前向传播同数量级
  • 内存消耗:需缓存所有层的激活值

2. 数值稳定性

  • 梯度消失:使用 ReLU 等替代 sigmoid
  • 梯度爆炸:梯度裁剪(gradient clipping)
  • 初始化策略:He/Kaiming 初始化

避坑指南

常见实现错误

  1. 维度不匹配:确保矩阵乘法的维度对齐
  2. 忘记转置:权重矩阵的转置操作
  3. 激活函数导数错误:如 ReLU 的负区间导数为 0

梯度问题解决方案

  • 梯度消失:
  • 残差连接(ResNet)
  • LSTM/GRU 结构
  • 梯度爆炸:
  • 梯度裁剪
  • 权重正则化

总结与延伸

通过完整的数学推导和代码实现,我们揭示了反向传播如何高效计算梯度。关键点在于:
1. 链式法则的递归应用
2. 误差信号的逐层反向传播
3. 参数梯度的精确计算

思考问题:
– 如何将反向传播扩展到卷积神经网络?
– 二阶优化方法(如 Hessian 矩阵)在反向传播中如何应用?
– 分布式环境下如何优化反向传播的计算效率?

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