一、先接上第 5 讲的悬念

第 5 讲拼出了一个完整的函数:矩阵乘法、ReLU、softmax,层层叠加,理论上能表达相当复杂的映射关系。但留了一个没解决的问题:W 和 b 里的每一个数字,都是随便设的。随机参数喂进去,只会吐出随机的、没有意义的概率分布。

这一讲要解决的问题就一句话:怎么让这些参数,从"随机的乱码",自动变成"能把下一个词猜对"的正确值?

二、打个比方:蒙着眼睛下山

想象你站在一片高低起伏的山地里,蒙着眼睛,目标是走到山谷最低点。你看不见地形全貌,但你能感觉到脚下这一小块地面往哪个方向倾斜、倾斜得有多陡。

一个合理的策略是:每一步都朝"脚下感觉最陡的下坡方向"迈一小步,然后重新感觉一次坡度,再迈下一步。走的步数够多,你大概率会停在某个谷底附近——即便你从头到尾都没看到过整片地形。

⭐ 这就是这一讲要讲的算法的全部直觉。“山地的高度"对应损失函数(衡量模型当前错得有多离谱);“脚下的坡度"对应梯度;“朝最陡下坡方向迈一小步"对应梯度下降这个更新参数的规则。

三、把直觉钉成数学

损失函数:把"错得有多离谱"变成一个数字

要下山,先得知道"当前站的地方海拔多高”。对神经网络来说,这个"海拔"叫损失(loss)——一个衡量"模型现在的输出,和正确答案差多少"的数字,数字越大,模型错得越离谱。

第 4 讲已经埋好了这个工具:交叉熵——那一讲用它给困惑度打分,这一讲它换一个身份,变成训练时要主动去缩小的目标。这不是两个不同的量,是同一个量的两种用法:算完之后拿来"评估”,就是第 4 讲的困惑度;算完之后拿来"调参数”,就是这一讲的损失函数。

导数:挪一点点参数,损失会变化多少

要知道"脚下往哪边倾斜",需要一个衡量"改变这个参数一点点,损失会跟着变化多少"的工具——这就是导数。

用一个具体到可以逐步手算的小例子建立直觉。假设某个参数 w 只有一个,损失函数是:

L(w) = (w − 3)²

(这是虚构的一个简化损失,只是为了让"下山"这件事可以被完整地画出来——真实的损失函数由整个网络加交叉熵算出来,形状复杂得多,但梯度下降处理它的方式完全一样。)

这个函数的导数是 dL/dw = 2(w−3)——不需要深究求导的规则,只需要知道导数告诉你,在当前这一点,w 往哪个方向挪,L 会变大还是变小,变化得有多快。

从 w=0 开始,每一步走"学习率" η=0.1 乘以负梯度的距离:

步数 w dL/dw = 2(w−3) 更新:w ← w − η·(dL/dw)
0 0 −6.0 0 − 0.1×(−6.0) = 0.6
1 0.6 −4.8 0.6 − 0.1×(−4.8) = 1.08
2 1.08 −3.84 1.08 − 0.1×(−3.84) = 1.464
3 1.464 −3.072 1.464 + 0.307 = 1.771
… … … 逐步逼近 3

⭐ 每一步都往梯度的反方向挪(梯度是"变大最快"的方向,要下降就要反着走),w 一路从 0 逼近损失最小的地方——也就是 w=3(这一点上 dL/dw = 2×(3-3) = 0,坡度为零,“脚下已经是平地”,这就是梯度下降判断"到底了"的信号)。

梯度下降(gradient descent):沿着损失函数对参数的梯度的反方向,反复对参数做微小更新,逐步逼近损失更小的参数取值的一种迭代算法。更新规则:w ← w − η · (dL/dw),η 叫学习率,控制每一步迈多大。

⚠️ 学习率不是越大越好,也不是越小越好:η 太大,每一步迈得太猛,可能直接越过谷底冲到对面的坡上,损失不降反升,甚至越走越离谱(发散);η 太小,每一步挪动量微乎其微,要走天文数字那么多步才能到谷底,训练慢得不现实。第 13、17 讲会看到,“怎么设置和调整学习率"本身就是训练一个大模型时最重要的工程决策之一。

多参数:梯度是一个向量

真实的网络不止一个参数,是成千上万甚至几十亿个 W、b 里的数字。这时候"梯度"不再是一个数字,而是每个参数各自的偏导数排成的一串数字——回忆第 3 讲:这正是一个向量。梯度下降的更新规则完全不变,只是从"一个数字减一个数字"变成"一个向量减一个向量(对应位置分别相减)":

所有参数 ← 所有参数 − η × 梯度向量

反向传播:高效算出每个参数的梯度

问题来了:一个真实的网络有很多层,损失是最后一层才算出来的,但需要调整的参数散布在每一层里。第一层的某个参数,怎么知道自己该往哪个方向挪,才能让好几层之后才算出来的损失变小?

答案是微积分的链式法则——把一个复合函数的导数,拆成一串局部导数相乘:

如果 L 是 x 经过 g 再经过 f 算出来的:L = f(g(x))
那么:dL/dx = (dL/dg) × (dg/dx)

⚠️ 这里要停一下澄清一个容易混淆的地方:这个名字你不是第一次见——第 4 讲讲概率时也用了"链式法则"这个词。两者名字相同,指的是完全不同的两条规则:第 4 讲的链式法则是概率论里的恒等式,把一整句话的联合概率拆成一串条件概率相乘;这一讲的链式法则是微积分里的求导规则,把一个复合函数的导数拆成一串局部导数相乘。两者不是巧合,都是"把一个复杂问题拆成一串简单步骤,再把结果组合起来"这同一种数学哲学的体现,但计算的对象完全不是一回事,不要混着用。

神经网络刚好是一长串函数的复合(一层接一层),所以链式法则可以从最后一层的损失开始,一层一层往输入方向倒着算回去——这正是"反向传播(backpropagation)“这个名字的来源:先正向算一遍输出(前向传播),再从输出端的误差出发,反着方向、一层一层往回传,把每一层的梯度都算出来。

反向传播(backpropagation):利用微积分链式法则,从输出层的损失开始,逐层向输入方向反向计算,从而高效算出损失对网络中每一个参数的梯度的算法。

一个完整的手算例子

把整个流程走一遍,用第 5 讲那种规模的小网络,全部换成具体数字。网络结构:

z₁ = w₁x + b₁
h  = ReLU(z₁)
y  = w₂h + b₂
L  = (y − t)²          ← t 是目标值(真实答案)

取 x=2,目标 t=10,当前参数 w₁=1, b₁=0, w₂=3, b₂=1。

前向传播(正向算一遍,顺便把每一步的中间结果都记下来,反向传播要用):

z₁ = 1×2 + 0 = 2
h  = ReLU(2) = 2
y  = 3×2 + 1 = 7
L  = (7 − 10)² = 9

反向传播(从 L 出发,反着往回算每一个参数的梯度):

① dL/dy   = 2(y − t) = 2×(7−10) = −6

② dL/dw₂  = dL/dy × dy/dw₂ = dL/dy × h = −6 × 2 = −12
   dL/db₂  = dL/dy × dy/db₂ = dL/dy × 1 = −6

③ dL/dh   = dL/dy × dy/dh = dL/dy × w₂ = −6 × 3 = −18

④ dh/dz₁  = ReLU'(z₁) = 1   (因为 z₁=2 > 0,ReLU 在正数区间导数恒为 1)
   dL/dz₁ = dL/dh × dh/dz₁ = −18 × 1 = −18

⑤ dL/dw₁  = dL/dz₁ × dz₁/dw₁ = dL/dz₁ × x = −18 × 2 = −36
   dL/db₁  = dL/dz₁ × dz₁/db₁ = dL/dz₁ × 1 = −18

⭐ 注意②到⑤每一步都在复用上一步已经算好的结果(dL/dy 算一次,后面全部拿来接着用),不需要为每个参数从头重新算一遍完整的链条——这正是反向传播比"对每个参数分别暴力求导"高效得多的原因,网络越深,这个效率差距越大。

拿学习率 η=0.01 更新一次全部参数:

w₂ ← 3 − 0.01×(−12) = 3.12
b₂ ← 1 − 0.01×(−6)  = 1.06
w₁ ← 1 − 0.01×(−36) = 1.36
b₁ ← 0 − 0.01×(−18) = 0.18

这就是训练神经网络唯一在做的事情——不断重复"前向算一遍、反向传播算梯度、按梯度小幅更新参数"这三步,跑过足够多的例子之后,参数会逐渐从随机值,收敛到能让损失变小的取值。

⚠️ 现实训练几乎从不会真的每算一个例子就更新一次,也不会攒够全部训练数据才更新一次,而是每次取一小批数据(mini-batch)算平均梯度再更新,叫随机梯度下降(SGD);实践中用得更多的是在 SGD 基础上加了些技巧(比如记住之前几步的更新方向)的改良版本(如 Adam)。这些是重要的工程细节,但核心机制和这一讲手算的例子完全相同,不是另一套原理。

四、这一讲机制自带的代价

  • 梯度消失/爆炸,第 2 讲的旧账:第 2 讲讲 RNN 时提到,训练信号要经过很多步连乘会指数级衰减或爆炸。现在可以把这句话说得更准确:这正是这一讲反向传播链式法则连乘的直接后果——网络(或序列)越深,梯度要连续相乘的局部导数就越多,只要这些局部导数持续小于 1(或大于 1),乘积就会指数级衰减(或爆炸)。这不是 RNN 独有的毛病,是反向传播这个机制在"层数/步数很深"时的通用风险,只是 RNN 因为按时间步展开后天生"很深”,撞得最早、最明显。
  • 没有找到全局最优的保证:损失函数的"地形"极其复杂,梯度下降只能保证每一步都在往"脚下的下坡方向"走,不能保证走到的是整片地形里最低的谷底,也可能停在一个局部的小坑里。⚠️ 好消息是:经验上,现代大规模神经网络的损失地形,在极高维参数空间里,糟糕的局部小坑相对没那么常见(更常见的困扰是大片近乎平坦、走得很慢的区域),但这依然是一个没有理论保证、只能靠经验和实践验证的说法。
  • 训练要占用大量显存:反向传播要求前向传播时把每一层的中间结果(比如手算例子里的 z₁、h)都存下来,反向传播时才有东西可用来计算局部导数。网络越深、批量数据越大,需要存的中间结果就越多——这是训练大模型需要巨大显存的直接原因之一,第 22 讲讲推理阶段的显存优化时,你会看到类似的存储-计算权衡以另一种形式重新出现。

五、和后面课程的关系

  • 第 13 讲(预训练目标):把这一讲的"损失函数"具体钉死成交叉熵,把"训练数据"换成海量文本,本质上就是把这一讲的三步循环(前向、反向、更新)在极大规模上重复运行。
  • 第 2 讲(RNN 的瓶颈):梯度消失问题在这一讲被正式讲清楚了数学来源——反向传播链式法则的连乘衰减,回头看第 2 讲会更清楚为什么 LSTM 的"闸门"是在直接对抗这个数学性质。
  • 第 17 讲(RLHF):强化学习里的"奖励信号"要转换成能驱动参数更新的梯度,转换的方式和这一讲不完全相同,但最终"用梯度下降更新参数"这一步,和这一讲讲的机制是同一套。
  • Lab 1:会把这一讲手算的例子,原封不动地写成一段可以运行、可以验证的纯 NumPy 代码——自己实现一次前向传播和反向传播,比看多少遍公式都更能建立直觉。

六、本讲小结

  • 损失函数把"模型错得有多离谱"变成一个数字;梯度告诉你,微调某个参数,这个数字会怎么变化。
  • ⭐ 梯度下降:沿梯度反方向,反复对参数做小幅更新,w ← w − η·(dL/dw),像蒙眼下山一样一步步逼近损失更低的地方;学习率 η 太大会冲过头发散,太小会慢得不现实。
  • 多参数时梯度是一个向量——每个参数各自的偏导数排成一串,衔接回第 3 讲的向量概念。
  • ⭐ 反向传播用微积分链式法则,从输出端的损失开始,逐层往回传,高效算出每一个参数的梯度,避免了对每个参数从头暴力求导——本讲给出的手算例子完整走过了前向、反向、更新三步,每一个中间数字都可以自己验算。
  • ⚠️ 这一讲的"链式法则"是微积分规则,和第 4 讲概率论的"链式法则"同名不同物,两者共享"拆解复杂问题、组合简单步骤"的哲学,但计算对象完全不同。
  • 代价:梯度消失/爆炸是反向传播连乘的通用风险(第 2 讲 RNN 的问题在这里有了准确的数学解释);没有全局最优的理论保证;训练需要存储大量中间结果,是显存开销的直接来源。

思考题

  1. 在第三节的手算下山例子(L(w)=(w-3)²,从 w=0 开始,η=0.1)里,继续手算第 4、5 步,观察 w 是不是在持续逼近 3。如果把学习率换成 η=1.1,从 w=0 开始走一步,w 会变成多少?这说明了什么?
  2. 手算例子里,如果 x=−2(其他参数不变:w₁=1,b₁=0),重新算一遍 z₁ 和 h。这时候 dh/dz₁ 还等于 1 吗?为什么(提示:ReLU 在负数区间的导数)?这对 dL/dw₁ 会有什么影响?
  3. 为什么反向传播要"复用上一步算好的结果"这件事,会让计算量随网络层数增长得比"暴力对每个参数分别求导"慢得多?试着数一数:一个 10 层的网络,暴力法大概要重复计算多少次"前 9 层"的部分?
  4. 结合本讲第四节和第 2 讲的内容,解释一下:为什么 RNN 按时间步展开后,“网络的深度"会随着序列长度增长,而普通的前馈网络(比如第 5 讲的 MLP)深度是固定的、不随输入变化?
  5. 第四节提到"损失地形里可能存在局部小坑,梯度下降不保证找到全局最优”——结合第三节"脚下感觉最陡的方向"这个比方,你能想到蒙眼下山这个策略在什么样的地形里会被"骗"到一个不是最低点的地方吗?