67·深度学习入门高级

反向传播算法详解

deep-learningbackpropagation

第67课 - 反向传播算法详解

学习目标

  1. 理解神经网络中损失函数与权重更新之间的关系。
  2. 掌握链式法则(Chain Rule)在梯度计算中的应用。
  3. 掌握反向传播算法(Backpropagation)的核心步骤与数学原理。
  4. 能够手动计算一个简单神经网络的梯度,并理解梯度消失与梯度爆炸问题。

核心概念

在上一课,我们学习了多层感知机(MLP)的结构。网络如何知道如何调整它的权重(w)和偏置(b)来学习呢?答案就是反向传播算法。它是训练神经网络的核心引擎,本质上是一种高效计算损失函数相对于网络中所有权重梯度的方法。

1. 前向传播(Forward Pass) 数据从输入层开始,经过各层的线性变换(z = wx + b)和非线性激活函数(a = σ(z)),最终计算出预测值 ŷ。这个过程叫前向传播。

2. 损失函数(Loss Function) 前向传播结束后,我们使用损失函数(如均方误差 MSE)来衡量预测值 ŷ 与真实值 y 之间的差距。损失值 L 是一个标量。

3. 梯度与链式法则 我们的目标是调整权重 w 以最小化损失 L。根据梯度下降法,我们需要知道 ∂L/∂w(损失函数关于权重 w 的偏导数,即梯度)。梯度指明了损失函数上升最快的方向,因此我们向梯度的反方向更新权重。 计算梯度需要用到微积分的链式法则。例如,对于一个复合函数 y = f(g(x)),其导数 dy/dx = (dy/dg) * (dg/dx)。神经网络正是一个深度复合函数,我们需要从输出层开始,逐层向前“反向”计算梯度。

4. 反向传播算法步骤 算法的核心是逐层计算误差项(δ),然后利用它计算权重的梯度。

  • 步骤1:前向传播,计算所有神经元的激活值 a 和输出 ŷ
  • 步骤2:计算输出层误差δ^L = ∂L/∂z^L(损失函数对输出层加权输入 z 的偏导数)。
  • 步骤3:反向传播误差:对于第 l 层,误差 δ^l 由下一层(l+1层)的误差 δ^{l+1} 计算得来:δ^l = ( (w^{l+1})^T · δ^{l+1} ) ⊙ σ'(z^l)。其中 表示逐元素相乘,σ' 是激活函数的导数。
  • 步骤4:计算梯度:权重的梯度 ∂L/∂w^l = δ^l · (a^{l-1})^T,偏置的梯度 ∂L/∂b^l = δ^l

代码示例

下面的代码手动实现了一个简单的三层神经网络(输入层、隐藏层、输出层)的反向传播算法。

import numpy as np

# 初始化一个简单的神经网络:输入层2个神经元,隐藏层3个神经元,输出层1个神经元
np.random.seed(42)
X = np.array([[0.5, 0.1], [0.2, 0.3], [0.4, 0.8], [0.7, 0.9]]) # 4个样本,2个特征
y = np.array([[0], [1], [1], [1]]) # 4个样本的真实标签

# 初始化权重和偏置
w1 = np.random.randn(2, 3) * 0.5 # 输入层到隐藏层权重
b1 = np.zeros((1, 3))            # 隐藏层偏置
w2 = np.random.randn(3, 1) * 0.5 # 隐藏层到输出层权重
b2 = np.zeros((1, 1))            # 输出层偏置

learning_rate = 0.1

# 定义 Sigmoid 激活函数及其导数
def sigmoid(z):
    return 1 / (1 + np.exp(-z))

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

# 定义均方误差损失函数
def mse_loss(y_true, y_pred):
    return np.mean((y_true - y_pred) ** 2)

# 训练循环
for epoch in range(1000):
    # === 步骤1: 前向传播 ===
    z1 = X.dot(w1) + b1        # 隐藏层加权输入
    a1 = sigmoid(z1)           # 隐藏层激活值
    z2 = a1.dot(w2) + b2       # 输出层加权输入
    y_pred = sigmoid(z2)       # 预测输出
    
    # 计算损失
    loss = mse_loss(y, y_pred)
    
    if epoch % 100 == 0:
        print(f'Epoch {epoch}, Loss: {loss:.4f}')
    
    # === 步骤2 & 3: 反向传播 ===
    # 计算输出层的误差项 δ2 (dL/dz2)
    # 对于 MSE 和 Sigmoid,δ2 = (y_pred - y) * sigmoid_derivative(z2)
    delta2 = (y_pred - y) * sigmoid_derivative(z2)
    
    # 计算隐藏层的误差项 δ1
    # δ1 = (w2 · δ2) ⊙ sigmoid_derivative(z1)
    delta1 = delta2.dot(w2.T) * sigmoid_derivative(z1)
    
    # === 步骤4: 计算梯度并更新参数 ===
    # 计算权重和偏置的梯度
    dW2 = a1.T.dot(delta2)
    dB2 = np.sum(delta2, axis=0, keepdims=True)
    dW1 = X.T.dot(delta1)
    dB1 = np.sum(delta1, axis=0, keepdims=True)
    
    # 使用梯度下降更新权重和偏置
    w2 -= learning_rate * dW2
    b2 -= learning_rate * dB2
    w1 -= learning_rate * dW1
    b1 -= learning_rate * dB1

print("训练完成!最终损失:", mse_loss(y, y_pred))
print("预测输出:", y_pred)

实践练习

练习1(简单):考虑一个只有1个输入、1个隐藏神经元(激活函数为 Sigmoid)和1个输出的极简网络。

  • 前向传播方程:a1 = σ(w1*x + b1), ŷ = w2*a1 + b2
  • 损失函数为 MSE:L = 1/2 * (y - ŷ)²
  • 请手动推导:计算 ∂L/∂w1∂L/∂w2 的表达式(要求用链式法则写出完整步骤)。

练习2(中等):修改上述代码示例中的网络,将其改为包含两个隐藏层(神经元数量自定),并实现完整的反向传播。观察增加网络深度后,训练是否变得更困难。

常见错误

  1. 混淆矩阵乘法顺序:在反向传播公式 δ^l = ( (w^{l+1})^T · δ^{l+1} ) ⊙ ... 中,权重矩阵需要转置。错误使用 w · δ 会导致维度错误和错误的梯度计算。
  2. 梯度方向弄反:更新权重时,应该沿梯度的负方向w = w - lr * grad)。写成 w = w + lr * grad 会导致损失增加。
  3. 忽略偏置的梯度:偏置 b 的梯度是 δ^l,计算方法和权重不同(求和维度)。容易遗漏 np.sum 操作。
  4. 不正确的激活函数导数:使用 ReLU 等激活函数时,其导数在负区间为0,这会导致神经元“死亡”,梯度无法传播。需要在实现时特别注意。

小结

  • 反向传播算法是神经网络学习的基石,它利用链式法则高效地计算损失函数关于所有参数的梯度
  • 算法分为 “前向传播计算输出与损失”“反向传播计算梯度” 两个主要阶段。
  • 链式法则是将深层网络的复杂梯度分解为一系列局部梯度的乘积。
  • 理解并能手动推导反向传播过程,对于调试网络、理解梯度消失/爆炸等问题至关重要。梯度消失(常见于深层网络使用Sigmoid)和梯度爆炸是限制网络深度的主要原因之一,将在后续课程中探讨。
  • 掌握了反向传播,你就掌握了训练神经网络的“核心密码”。下一步,我们将深入学习不同激活函数的特性,它们对反向传播中的梯度流有着直接而关键的影响。

练习编辑器

rust
Loading...

继续学习

完成本课后,建议继续学习下一课「激活函数:ReLU、Sigmoid 与 Softmax」 以巩固所学知识。