all_lessons/数学的逻辑/27第 28 课 / 共 44 课

第八部分 · 学习就是优化

梯度下降:沿着坡一步步走到底

我们有了最快下坡的方向 −∇L,也能高效地把它算出来。可"知道往哪走"不等于"会走"。一步迈多大?冲过头会发散,挪太慢要等到天荒地老。把方向变成一条真正走得到谷底的路线,这条规则叫梯度下降。

线性回顾
上一课:反向传播 = 链式法则 + 计算图,前向算值、反向沿边把局部导数相乘,一次遍历就把所有参数的梯度 ∇L 算出来——百万参数也算得动。
留下的问题:梯度只告诉我们方向−∇L 是最快下坡)和陡度。可它没说一步该迈多大。梯度本身只是"此刻的局部坡度",沿它直冲并不能一步到谷底;走多大、走多少步、会不会冲过头,都还没回答。
本课新增:梯度下降——一条朴素到极致的更新规则:"站在当前参数,朝 −∇L 迈一小步,到了新位置重新算梯度,再迈一步,如此反复。"那"一小步"的大小由学习率 η 控制:太大震荡甚至发散,太小慢如蜗牛。再补上随机梯度下降(SGD)让它在海量数据上跑得起来,和动量让它走得更稳更快。这就是几乎所有神经网络训练的内核。
本课路线
(1) 从"方向"到"路线":迭代更新的思想;(2) 更新规则 w ← w − η·∇L;(3) 学习率 η——太大发散、太小龟速、刚好稳稳下坡;(4) 随机梯度下降 SGD:为什么不能每步都看全部数据;(5) 动量:给下坡的小球加上惯性;(6) 它什么时候停、停在哪——埋下凸性的伏笔;(7) 这就是 gpt_mini 训练循环的心脏。

从"方向"到"路线":一步一步地走

梯度给的是局部信息——只在当前这一点告诉你最陡下坡朝哪。地形是弯的,沿当前方向直冲很快就会"偏"。聪明的办法不是一口气冲到底,而是走小步、勤回头:迈一小步到新位置,重新算那里的梯度(方向可能已经变了),再迈一小步。像在浓雾里下山,看不见全貌,但每一步都摸清脚下哪边更低,然后朝那边挪一点。只要每步都让海拔下降,反复迭代,就能一路摸到谷底附近。

这把"找最低点"这个一蹴而就办不到的难题,拆成了一连串简单的小步。每一步只需要当前点的梯度——而梯度,上一课已经能高效算出来了。整套思路就是:

1在当前参数 w 处算梯度 ∇L
2朝 −∇L 迈一小步,更新 w
3回到第 1 步,直到走不动为止

更新规则:w ← w − η·∇L

把上面那句话写成一行公式,就是整个深度学习训练的心脏:

w ← w − η · ∇L
读作"新的参数 = 旧的参数,减去 学习率 η 乘以 梯度"。那个箭头 表示"赋值/更新"。减号是关键:梯度指向上坡,减去它就是朝下坡走(第 25 课)。η(读"eta")是学习率,也就是步长。

逐字拆开:沿哪个方向−∇L 决定(下坡最快);迈多大η 和该点坡度共同决定——坡越陡(梯度越长)这一步迈得越远,接近谷底时坡变缓、步子自然变小,慢慢停下来。这是个很美的自适应性质:它会自己"踩刹车"。

用第 25 课的例子 L(w)=w²∇L=2w)走两步,取 η=0.1、起点 w=5:第一步 w ← 5 − 0.1×(2×5) = 5 − 1 = 4;第二步 w ← 4 − 0.1×8 = 3.2;再下去 2.56、2.048……一路朝最低点 w=0 收敛,且越靠近走得越慢。下面的小实验把这个过程画在损失曲面上:拖动学习率滑块,亲眼看它太大怎么发散、太小怎么龟速、刚好怎么稳稳滑到谷底。

梯度下降动画:学习率太大发散 / 太小龟速 / 刚好下坡
损失曲线 L(w)=w²(碗形,最低点在 w=0)。小球从左侧起点出发,每一步执行 w ← w − η·∇L。拖 η 看:η 很小 → 步步挪、慢;η 适中 → 平滑滑到谷底;η ≥ 1 → 来回横跳;η > 1 → 越跳越高、发散。可勾选"动量"给小球加惯性。点"走一步"或"自动跑"。
步数
0
当前 w
-4.00
当前损失 L
16.00

学习率 η:这一步的成败全在它

更新规则只有一个旋钮,却最难调,它就是学习率 η。三种典型情形:

所以训练里第一个要调、也最值得调的超参数,往往就是学习率。现代做法还会让它随训练逐渐变小(学习率衰减):开头大步快速接近,后期小步精细收敛——大步找方向、小步抠细节。

随机梯度下降(SGD):别每步都看全部数据

有个现实问题:损失是在全部训练数据上定义的。若每走一步都要把几百万、上亿条数据全过一遍才算出一个梯度,一步就慢得离谱。随机梯度下降(SGD)的妙招是:每步只随机抽一小批数据(mini-batch),用这小批估出一个近似梯度就立刻更新。

每步:抽一小批样本 → 在这批上算 ∇L → w ← w − η·∇L
读作"用一小批数据估计梯度,然后立刻更新"。这个梯度有噪声,方向略歪,但便宜,于是同样时间里能走多得多的步。

反直觉的是,这点噪声不全是坏事:它像给小球加了点随机抖动,有机会把它从浅坑里抖出来,反而帮上忙。"全量精确但极慢"换成"小批带噪但飞快",是深度学习能在海量数据上训练的关键工程权衡。你在 gpt_mini 看到的 batch、step,正是这件事。

动量:给下坡的小球加上惯性

纯 SGD 在"细长山谷"里会犯一个毛病:沿陡的那个方向来回横跳,沿平缓的谷底方向却挪得很慢,整体走成一条低效的"之"字形。动量借用物理直觉来治它:不只看当前这一步的梯度,还累积过去的方向,像一个真有质量、有惯性的小球——

v ← β·v − η·∇L ; w ← w + v
读作"速度 v = 上一时刻速度的 β 倍(保留惯性,β 约 0.9),再加上这一步的下坡推力;然后用速度去更新参数"。横跳方向的来回推力相互抵消,谷底方向的推力持续累加——于是横跳被压住,沿谷底加速前进。

效果就像让小球真的从坡上滚下来:一致的方向越滚越快,左右乱晃则被惯性熨平。在上面的小实验里勾上"动量",能看到它在同样学习率下更稳、更快地冲向谷底。Adam 等现代优化器,本质上是在动量之上再做更聪明的自适应步长,但内核仍是这条"沿 −∇L 走、带点惯性"的规则。

它会停在哪?——埋下凸性的伏笔

梯度下降何时停?当梯度 ∇L ≈ 0 时,更新量趋近于零,它就"走不动"了——停在一个各方向都平的点。可第 25 课的最后一个误解早已警告过:梯度为零的点不一定是全局最低,可能是局部极小,甚至马鞍点。这就引出一个让人不安的问题:

撞墙
梯度下降只保证每步局部下降,它停下来的地方只是"附近最低"。它凭什么不会卡在一个局部山谷里、永远到不了真正的全局最低点?对一般的损失地形(坑坑洼洼、多个谷),它确实可能卡住——那到底什么样的损失能保证下坡就一定到全局最优?

这个问题的答案,要靠地形的一类特殊形状——。当损失是凸的(像一个标准的碗,只有一个谷),局部最优就是全局最优,梯度下降必到真底。下一课正是研究这件事。

常见误解

一句话带走
梯度下降把"找最低点"拆成一连串小步:w ← w − η·∇L,朝最快下坡方向迈一步、重算梯度、再迈一步。学习率 η 决定成败(太大发散、太小龟速);SGD 用小批数据让它在海量数据上跑得起来;动量给它惯性。这条规则,就是几乎所有神经网络训练的内核——但它只保证局部下降,不保证全局最优。
下一步
梯度下降会卡在局部最优——除非地形本身就只有一个谷。什么样的损失能保证"下坡就一定到全局最优"?答案是一类叫的函数:凸问题里局部最优即全局最优,优化稳稳到底。我们还会顺手学会处理带约束的优化(拉格朗日乘子),为后面第 37 课的支持向量机埋下伏笔。→ 第 28 课《凸优化:什么时候保证能到全局最优》。