第 1 站
算梯度要多少钱?
上一课那台训练台,每点一步都用到了梯度 ∂L/∂w——可梯度本身是怎么算出来的?这一课就来揭晓,也给卷二收尾。
最笨的方法是数值微分:对每个参数 wi,把它微微增大 ε,看损失怎么变:
∂wi∂L≈εL(w+εei)−L(w)
ei 是第 i 个分量为 1、其余为 0 的单位向量。 算一个参数的梯度,需要一次额外的前向传播。
n 个参数需要 n 次额外前向传播
GPT-3 有 1750 亿个参数。算一次完整梯度,需要 1750 亿次额外前向传播。 每次前向传播本身就要处理整个网络——这需要的时间和能源是天文数字。
有没有方法,只用一次前向传播,就把所有 1750 亿个参数的梯度全部算出来?
损失是很多层运算的复合:L = loss(softmax(W₃ · relu(W₂ · relu(W₁x + b₁) + b₂) + b₃))。从哪里切入能让梯度「流回来」?
第 2 站
链式法则:梯度怎么流回去
反向传播的精神,可以用一个词概括:追责。输出层算出来错了一截,这份「错」该怎么 摊派给前面每一层、每一个权重?谁的影响越大,谁就该多背一点责任、被调整得多一点。 而链式法则,正是这套追责的精确账本。
微积分里的链式法则:如果 L=f(g(x)),那么:
dxdL=dgdL⋅dxdg
翻译一下:「L 怎么随 x 变化」= 「L 怎么随 g 变化」× 「g 怎么随 x 变化」。 如果 g 是一层网络,x 是上一层的输出,那么这个公式就告诉我们:只要知道「损失对这层输出的梯度」,就能算出「损失对这层输入的梯度」, 进而继续往前一层传。
图 13-1前向传播从左到右,计算并储存每个节点的值。反向传播从右到左,把损失的梯度通过链式法则一步步传回每个参数。每个节点在前向传播时记录自己的输入值; 反向传播时,用记录的输入值计算本地导数,再乘以「右边传来的梯度」, 结果继续往左传。这就是计算图(computational graph)的工作方式。
第 3 站
跟着数字走一遍
最简单的网络:输入 x=2,一个权重 w=1.5,网络: z = w·x,a = z²,L = (a−6)²。目标输出 y=6。
图 13-2链式法则把梯度从输出向输入传递:∂L/∂w = ∂L/∂a × ∂a/∂z × ∂z/∂w = 6×6×2 = 72。不需要额外的前向传播。关键点:反向传播复用了前向传播中保存的中间值(z=3, a=9)来计算本地导数。整个过程只需要一次前向传播 + 一次反向传播, 不管有多少参数,代价只是两倍的前向传播。
把同样的链式法则用到我们那台猜水果的机器上(神经元 + Sigmoid + 交叉熵),会冒出一个特别漂亮的结果。 下面这台演示就把梯度从损失一路传回 w 和 b——改颜色或真实答案,看每个梯度实时重算:
互动 13-A单神经元上的反向传播。中间一长串局部导数(∂L/∂p × ∂p/∂z)神奇地约成了 ∂L/∂z=p−y(预测 − 真值),再乘输入得 ∂L/∂w=(p−y)x。这正是第 12 课训练台每一步用的那个梯度——闭环了。第 4 站
完整案例:一步不省,教网络认出西瓜
上面那台单神经元太简单,梯度只穿过一层就到了权重。真实网络真正的难点,是梯度要穿过隐藏层、一层层往回传。 下面这个完整案例就把这件事彻底摊开:一个最小的「真」网络——2 个输入 → 1 个隐藏神经元(Sigmoid)→ 2 个输出(Softmax), 任务是判断一个水果是不是西瓜(颜色深、个头大就更像)。从前向、Softmax、损失,到反向传播每一步的链式法则、再到权重更新与训练收敛,所有数字一个不省、全程可交互。
建议照标签页顺序走一遍:① 看清网络结构 → ② 前向算出预测(拖动权重滑块感受变化)→③ Softmax 把得分变概率、交叉熵量出损失 → ④ 反向传播,A→E 五步把梯度从输出一路传回每个权重 →⑤ 按梯度更新一步、看损失是降是升 → ⑥ 连点训练,看损失曲线一路滑到底、网络真的学会认西瓜。
场景:根据颜色和尺寸,判断是不是西瓜
西瓜通常颜色深、个头大。我们用这两个特征训练一个极简分类网络:2 个输入 → 1 个隐藏神经元 → 2 个输出。
🍉
西瓜(是)
颜色深 x₁=0.9 / 尺寸大 x₂=0.8
🍊
橙子(不是)
颜色浅 x₁=0.2 / 尺寸小 x₂=0.3
🍋
柠檬(不是)
颜色浅 x₁=0.1 / 尺寸小 x₂=0.2
🍉
西瓜(是)
颜色深 x₁=0.8 / 尺寸大 x₂=0.9
本案例自始至终用这一颗西瓜:颜色 x₁ = 0.9,尺寸 x₂ = 0.8,真实答案 = 西瓜(y = 0,即第 0 类)。网络初始权重是随便给的,要靠反向传播一步步学会。
符号说明
x₁ 颜色深浅(0=浅, 1=深绿),本例 x₁ = 0.9
x₂ 尺寸大小(0=极小, 1=极大),本例 x₂ = 0.8
w₁, w₂ 输入→隐藏层权重
z 隐藏层线性值:z = w₁·x₁ + w₂·x₂
h 隐藏层输出:h = sigmoid(z) ∈ (0,1)
v₁, v₂ 隐藏→输出层权重
o₁, o₂ 输出层线性值:o₁ = v₁·h,o₂ = v₂·h
ŷ₁, ŷ₂ Softmax 后的概率(ŷ₁=西瓜, ŷ₂=不是)
L 交叉熵损失 y = 0(真实类别:西瓜)
② 前向传播:数字从左流到右
本案例的初始权重(随便给的,训练就是要改掉它们):w₁=0.600、w₂=0.500、偏置 b_h=0.100;输出层 v₁=0.400、v₂=-0.300、偏置 b₁=0.200、b₂=-0.100。输入是那颗西瓜 x₁=0.9、x₂=0.8。
z_h = w₁·x₁ + w₂·x₂ + b_h = 0.600×0.9 + 0.500×0.8 + 0.100 = 1.0400
a_h = σ(1.0400) = 0.7389 (隐藏神经元,Sigmoid 激活)
z₁ = v₁·a_h + b₁ = 0.400×0.7389 + 0.200 = 0.4955
z₂ = v₂·a_h + b₂ = -0.300×0.7389 + -0.100 = -0.3217
两个输出神经元拿到的是同一个隐藏层输出 a_h —— 信息在隐藏层汇合后,再分发给两个「投票箱」。
③ Softmax 与交叉熵损失
两个原始分数 z 要先过 Softmax 变成「加起来 = 1 的概率」,再和真实答案(西瓜,y=0)比对。
e^(z₁) = e^0.4955 = 1.6414
e^(z₂) = e^-0.3217 = 0.7249
归一化:p₁(西瓜) = 1.6414 / 2.3663 = 0.6936 p₂(不是) = 0.3064
L = −log p_(y=0) = −log 0.6936 = 0.3658
模型给了正确答案 69.4% 的概率——不算离谱,但远谈不上自信。损失 0.3658 就是「这次猜得有多差」的量化。
④ 反向传播:沿着链式法则往回走
损失对每个参数的梯度,都从输出端那一个误差出发,一层层往回摊。真实类别是西瓜(y = 0,第 0 类),所以 z₁ 的误差是 p₁ − 1。
∂L/∂z₁ = p₁ − 1(第 0 类为真)= 0.6936 − 1 = -0.3064
∂L/∂z₂ = p₂ − 0 = 0.3064
∂L/∂v₁ = ∂L/∂z₁ × a_h = -0.3064×0.7389 = -0.2264 ∂L/∂b₁ = -0.3064
∂L/∂v₂ = ∂L/∂z₂ × a_h = 0.3064×0.7389 = 0.2264 ∂L/∂b₂ = 0.3064
∂L/∂a_h = ∂L/∂z₁×v₁ + ∂L/∂z₂×v₂ = -0.3064×0.40 + 0.3064×-0.30 = -0.2145
∂L/∂z_h = ∂L/∂a_h × a_h(1−a_h) = -0.2145×0.1930 = -0.0414
∂L/∂w₁ = ∂L/∂z_h × x₁ = -0.0414×0.9 = -0.0372 ∂L/∂b_h = -0.0414
∂L/∂w₂ = ∂L/∂z_h × x₂ = -0.0414×0.8 = -0.0331
特别注意 ∂L/∂a_h 那一步:两个输出端的梯度在这里汇合——这就是隐藏层「承上启下」的含义。∂L/∂z₁ 是负的:模型还不够自信,要把 p₁ 往上推。
⑤ 权重更新:沿梯度反方向迈半步(α=0.5)
每个参数都朝着自己梯度的反方向挪 α 那么多。
w₁: 0.60 → 0.60 − 0.5×(-0.0372) = 0.6186 w₂: 0.50 → 0.5166 b_h: 0.10 → 0.1207
v₁: 0.40 → 0.5132 v₂: -0.30 → -0.4132 b₁: 0.20 → 0.3532 b₂: -0.10 → -0.2532
用新参数再前向一遍:p₁(西瓜) = 0.7858,L = 0.2411 (损失从 0.3658 降到 0.2411,网络更自信了)
⑥ 训练模拟:同样的西瓜,反复喂
把这一颗西瓜反复喂给网络、每轮都按上面的规则更新,损失一路下滑。下表就是本页开头那组控制按钮(真实答案 / 颜色 x₁ / 学习率)驱动的实时重算。
轮w₁w₂损失 Lp(西瓜)
00.6000.5000.36669.4%
10.5840.4860.46962.6%
20.5900.4910.54558.0%
30.6140.5130.59655.1%
40.6550.5490.62953.3%
50.7090.5970.65052.2%
60.7740.6550.66351.5%
学习率 α = 0.5
点「训练一步」逐轮前进,或「▶ 自动训练」连播;拖动学习率 α 感受「步子太大来回震荡、步子太小龟速」。
互动 13-B反向传播完整案例。一个 2→1→2 的西瓜分类网络,六个标签页把前向、Softmax、交叉熵、反向传播(链式法则逐步)、权重更新、训练模拟全部画了出来,第 ⑥ 页的训练模拟还能换真实答案、改颜色与学习率实时重算。特别盯住第 ④ 页那条反向的链子:∂L/∂o=y^−y(预测 − 真实)→ 乘 v、两路相加得 ∂L/∂h→ 乘 Sigmoid 的导数 h(1−h) 得 ∂L/∂z → 再乘输入 x 得 ∂L/∂w。 每一环都只是「上一环传来的梯度 × 本环的局部导数」,一路相乘传回去——这就是反向传播的全部秘密。 唯一要小心的是:隐藏层 h 同时连着两个输出,它的梯度得把两条路相加。
第 5 站
梯度消失——链式法则的副作用
反向传播的梯度是一串相乘: ∂L/∂w₁ = ∂L/∂aₙ × ∂aₙ/∂aₙ₋₁ × … × ∂a₂/∂a₁ × ∂a₁/∂w₁
小数连乘趋向零
如果每一层的「本地导数」都小于 1(比如 Sigmoid 的最大导数是 0.25), 经过 100 层相乘:0.25¹⁰⁰ ≈ 10⁻⁶⁰——梯度消失为零。 靠近输入的层接收到几乎为零的梯度信号,完全学不到任何东西。
解法一:ReLU——正区间导数恒为 1,梯度不因层数而衰减。
解法二:残差连接(第 24 课)——给梯度开一条「直通车」,跳过若干层直传。
解法三:梯度裁剪——对爆炸的梯度设上限,防止另一个极端(梯度爆炸)。
好消息:你几乎永远不需要手写反向传播。 PyTorch、TensorFlow 等框架内置自动微分(autograd): 用框架的运算构建网络,调用 loss.backward(), 所有参数的 .grad 属性就自动填好了。 反向传播对用户完全透明。
第 6 站
总结:卷二完整了
卷二六课,你建立了「神经网络如何学习」的完整图景:
- 神经元:z = w·x + b,σ(z) 输出(第 7 课)
- 激活函数:打破线性诅咒,ReLU 解决梯度消失(第 8 课)
- 损失函数:MSE 用于回归,交叉熵用于分类(第 10 课)
- Softmax:把分数变成概率,temperature 参数的来源(第 11 课)
- Mini-batch / Adam:高效更新参数(第 12 课)
- 反向传播:一次反向扫描算出所有梯度(第 13 课)
本课核心 · TAKEAWAY
反向传播 = 链式法则 在计算图上的系统化应用。 前向传播保存中间值,反向传播复用它们计算本地导数,梯度从输出反向流到所有参数。 代价仅是两倍的前向传播——这让训练亿级参数成为可能。
这一课你亲手推导了
- 数值微分的问题:n 个参数需要 n 次额外前向传播,代价无法承受。
- 链式法则:dL/dx = dL/dg × dg/dx,梯度可以从外向内逐层传递。
- 反向传播:一次前向(保存中间值)+ 一次反向(传递梯度),搞定所有参数。
- 梯度消失:小数连乘趋向零;ReLU + 残差连接是主要解法。
- 自动微分:PyTorch autograd 帮你做了全部,只需 loss.backward()。
小测验
学习小测验
做完这一课,来检测一下核心知识点。选出你的答案后点击「提交」,即可看到正确选项与讲解。
Q1反向传播(backpropagation)要解决的核心问题是?
Q2反向传播之所以能把误差「逐层传回去」,依靠的是哪条数学法则?