Skip to content

反向传播

概念

反向传播(Backpropagation)是计算损失函数对神经网络中每个参数的梯度的算法。它应用微积分中的链式法则,从输出层向输入层逐层传递梯度。

设损失函数为 L,我们需要计算每个参数的梯度:

  • 权重梯度 LW(l)
  • 偏置梯度 Lb(l)

这些梯度用于优化器更新参数。


链式法则

标量链式法则

y=f(g(x)),则:

dydx=dydgdgdx

矩阵链式法则

神经网络中的操作都是矩阵/向量级别的。考虑一个线性层:

Y=XW+b

给定损失对输出的梯度 LY(由后续层传入),需要计算三个梯度:

LX=LYWTLW=XTLYLb=n=1N(LY)n,:

线性层的梯度推导

1. 对输入的梯度 LX

从元素角度展开。设 Y=XW

Yn,j=i=1dinXn,iWi,j

对某个 Xn,i 求链式:

LXn,i=j=1doutLYn,jYn,jXn,i=j=1doutLYn,jWi,j

LX=LYWT

2. 对权重的梯度 LW

对某个 Wi,j

LWi,j=n=1NLYn,jYn,jWi,j=n=1NLYn,jXn,i

LW=XTLY

3. 对偏置的梯度 Lb

偏置对每个样本的输出贡献相同:

Lbj=n=1NLYn,j

Lb=n(LY)n,:(按行求和,keepdims=True)。


激活层的梯度

激活函数逐元素操作 y=f(x),因此梯度也是逐元素的:

Lx=Lyf(x)

其中 是 Hadamard(逐元素)积,f 是激活函数的导数:

激活函数f(x)
ReLU1{x>0}
Sigmoidσ(x)(1σ(x))
Tanh1tanh2(x)

代码实现:LinearLayer.backward()

src/nn/layers/linearLayer.py:107-150:

python
def backward(self, outputGradient: np.ndarray) -> np.ndarray:
    # dL/dX = dL/dY @ W^T
    inputGradient = outputGradient @ self.weights.T

    # dL/dW = X^T @ dL/dY
    self.gradWeights[...] = self.inputCache.T @ outputGradient

    # dL/dBias = sum(dL/dY, axis=0, keepdims=True)
    if self.useBias and self.gradBias is not None:
        self.gradBias[...] = np.sum(outputGradient, axis=0, keepdims=True)

    return inputGradient

注意 [...] = 语法是就地覆盖(不是累加)。这意味着每次 backward 前必须先 zeroGrad() 清除旧的梯度。


全局反向传播流程

src/nn/models/sequentialModel.py:91-112 中的 SequentialModel.backward() 协调全局反向传播:

python
def backward(self, outputGradient: np.ndarray) -> np.ndarray:
    inputGradient = outputGradient
    # 从最后一层向前,逐层反向传播
    for layer in reversed(self.layers):
        inputGradient = layer.backward(inputGradient)
    return inputGradient

完整训练步骤中的梯度流

Lz(L)Layer L .backwardLa(L1)Layer L1 .backwardLayer 1 .backwardLX

每一步的 layer.backward() 同时:

  1. 计算存储该层参数的梯度(self.gradWeights, self.gradBias
  2. 返回对输入的梯度(供上一层继续传播)

具体数值示例

以下是一个极简的二层网络反向传播演示。

设定

  • 输入 X=[1234](2 个样本,2 维特征)
  • W(1)=[0.50.10.20.3]b(1)=[0,0]
  • W(2)=[1.00.5]b(2)=[0]

前向传播

  • Z(1)=XW(1)+b(1)=[1(0.5)+2(0.2)1(0.1)+2(0.3)3(0.5)+4(0.2)3(0.1)+4(0.3)]=[0.10.70.71.5]
  • a(1)=ReLU(Z(1))=[0.10.70.71.5]
  • z(2)=a(1)W(2)+b(2)=[0.1(1.0)+0.7(0.5)0.7(1.0)+1.5(0.5)]=[0.451.45]

假设损失梯度 Lz(2)=[0.10.2](由损失函数返回)

反向传播第 2 层

  • La(1)=Lz(2)W(2)T=[0.1(0.2)][1.00.5]T=[0.10.050.20.1]

等等 — 这里让我更正。Lz(2) 形状 (2,1)W(2)T 形状 (1,2)

La(1)=Lz(2)(2,1)W(1,2)(2)T=[0.1(1.0)0.1(0.5)0.2(1.0)0.2(0.5)]=[0.10.050.20.1]
  • LW(2)=a(1)TLz(2)=[0.10.70.71.5]T[0.10.2]=[0.01+(0.14)0.07+(0.30)]=[0.130.23]

反向传播第 1 层(ReLU)

  • ReLU 的导数掩码:ReLU(Z(1))=[1111](所有值 > 0)
  • LZ(1)=La(1)ReLU(Z(1))=[0.10.050.20.1]

反向传播第 1 层(Linear)

  • LW(1)=XTLZ(1)=[1324][0.10.050.20.1]=[0.1+(0.6)0.05+(0.3)0.2+(0.8)0.1+(0.4)]=[0.50.250.60.3]

这些梯度随后由优化器用于参数更新。