跳转至

15.3   反向传播

小率在烘焙店尝了一口蛋糕:“太甜,还有点干。”均哥没有直接把蛋糕丢掉,而是拿出配方表:糖少一点,牛奶多一点,烤箱温度降一点,搅拌时间也要调整。

深度学习训练也是这样。模型先做出预测,损失函数(Loss Function)告诉它“错了多少”;反向传播(Backpropagation)再把这个错误沿着网络往回传,告诉每个参数该往哪个方向改。

均哥和小率在烘焙店理解反向传播

15.3.1   损失先给出最后的反馈

假设蛋糕甜度预测值是 \(\hat{y}\),真实期望是 \(y\)。回归任务常用均方误差(Mean Squared Error):

\[ L = \frac{1}{2}(\hat{y}-y)^2 \]

为什么前面有 \(\frac{1}{2}\)?因为求导后会抵消平方带来的 2,让式子更清爽:

\[ \frac{\partial L}{\partial \hat{y}} = \hat{y}-y \]

这个导数就是“最后输出应该往哪边动”的信号。

15.3.2   链式法则把反馈传回每一层

如果网络只有一个神经元:

\[ \hat{y} = wx + b \]

那么参数的梯度是:

\[ \frac{\partial L}{\partial w} = \frac{\partial L}{\partial \hat{y}} \frac{\partial \hat{y}}{\partial w} = (\hat{y}-y)x \]
\[ \frac{\partial L}{\partial b} = \hat{y}-y \]

多层网络只是把这件事重复很多次。后面一层的误差信号,乘上当前层的局部导数,就能得到当前层该怎么改。

前向传播与反向传播讲解图

15.3.3   梯度下降像小步改配方

得到梯度后,参数更新通常写成:

\[ w \leftarrow w - \eta \frac{\partial L}{\partial w} \]

其中 \(\eta\) 是学习率(Learning Rate)。学习率太小,改配方很慢;学习率太大,可能越改越离谱。

import torch

torch.manual_seed(0)

x = torch.tensor([[1.0], [2.0], [3.0], [4.0]])
y = torch.tensor([[2.2], [4.1], [6.0], [8.2]])

model = torch.nn.Linear(1, 1)
optimizer = torch.optim.SGD(model.parameters(), lr=0.05)
loss_fn = torch.nn.MSELoss()

for epoch in range(80):
    y_hat = model(x)
    loss = loss_fn(y_hat, y)

    optimizer.zero_grad()
    loss.backward()
    optimizer.step()

print("weight:", float(model.weight))
print("bias:", float(model.bias))

常见误解

反向传播不是“从答案里偷看规则”,它只是用链式法则计算梯度。真正决定模型学到什么的,是数据、模型结构、损失函数和优化过程。

小率的笔记本

前向传播负责得到预测,损失函数负责衡量错误,反向传播负责把错误分配给每个参数,优化器负责按梯度更新参数。四件事连起来,才叫一次训练迭代。