梯度下降家族
蒙着眼站在山坡上,只能用脚感受脚下的坡度,怎样走到谷底?朝最陡的下坡方向迈一步,再感受,再迈一步。这就是梯度下降。它的几个"亲戚"(动量、Nesterov、RMSProp、Adam)只是在"步子怎么迈"上多动了一点脑筋,却往往决定了你是 10 步到达,还是 10000 步还在山腰打转。
1为什么机器人需要它
训练一个视觉-语言-动作模型 (VLA) 或扩散策略 (diffusion policy),就是调整几亿个参数,让"预测动作与示范动作之间的误差"变小。让机械臂末端到达指定位置的数值逆运动学 (inverse kinematics),是调整几个关节角,让"末端与目标之间的距离"变小。轨迹优化、相机标定、点云配准也一样,都是在一个代价函数上找最低点。
这些问题规模天差地别,但最常用的求解办法都是同一个:沿着梯度往下走。6.1 节讲了怎样把问题写成"最小化 \(f(\x)\)";这一节讲怎样真的走下去,以及为什么同样是往下走,有人 10 步就到了,有人走了一万步还在山腰。
2直觉:蒙眼下山
你看不到整座山,只能感觉脚下的坡度。3.2 节说过,梯度 \(\nabla f\) 指向上坡最陡的方向,它的长度就是坡有多陡。所以最自然的规则是:朝 \(-\nabla f\) 迈一步,步长正比于坡度。比例系数 \(\eta\) 叫学习率 (learning rate),也叫步长。
步子大小是第一个难题。步子太小,走到天黑也到不了;步子太大,一脚跨过谷底,落在对面更高的坡上,然后越跳越高。先在一维里感受一下。
- 在抛物线上找一个学习率,让小球每步都跳到对面,却仍然收敛(至少走 4 步)。它和 \(1/a\) 有什么关系?
- 把学习率调大,让它发散。再把曲率 \(a\) 调大一倍:临界学习率怎么变?
在抛物线上,一步更新是 \(x \leftarrow x - \eta a x = (1-\eta a)\,x\):每一步都把 \(x\) 乘上同一个数。这个数的绝对值小于 1 才会收敛,于是 \(0 < \eta < 2/a\)。曲率越大,能用的步长越小。记住这句话,下面所有的故事都从它开始。
到了二维,第二个难题出现了:一个方向陡、另一个方向缓的狭长山谷。陡的方向要求小步长,缓的方向却需要大步长才走得动,一个 \(\eta\) 顾不了两头。梯度下降家族的其他成员,就是为这个问题想出的不同办法:
| 形象比喻 | 它额外记住了什么 | |
|---|---|---|
| GD | 蒙眼的人,每步只看脚下 | 什么也不记 |
| Momentum | 一个沉重的铁球,有惯性 | 速度:过去梯度的累积 |
| Nesterov | 会"看路"的铁球:先按惯性滑一下,再看那里的坡 | 速度,外加在"预计落点"处求梯度 |
| RMSProp | 每个坐标各配一个油门:常年陡的方向收着走,常年平的方向放开走 | 每个坐标梯度平方的滑动平均 |
| Adam | 铁球 + 分坐标油门 | 梯度的滑动平均和梯度平方的滑动平均 |
梯度只告诉你局部哪边是下坡,不告诉你最低点在哪。所有改进都在回答同一个问题:只凭一路上摸到的坡度,怎样把步子迈得更聪明?动量用历史抵消来回摆动,自适应方法用统计给每个方向配不同的步长。
3动手玩:优化器赛跑
下面是一张"地形图":底色里青色越浓的地方越低,等高线标出等高的位置。点击地图任意位置设定起点,五个优化器会同时出发。右侧可以开关每个优化器、看它的实时更新公式;下方是损失曲线(对数坐标,越往下越接近最小值)。换一种地形、拖一拖滑块,看看谁能赢。
- 在 Rosenbrock 香蕉形山谷里,让 Adam 到达最小值 (1, 1)。再看看 GD 在弯弯的谷底爬得有多慢。
- 让 GD 发散。在二次碗上,临界学习率是多少?(提示:最陡方向的曲率是 2。)
- 在二次碗上把条件数 κ 调到 30 以上,让 Momentum 在 GD 还在路上时就先收敛。
多玩几种地形,你会发现没有哪个优化器总是赢。在"双谷"地形上,动量有时能冲过中间的小山包,掉进更深的那个谷;在"鞍点"地形上,GD 会在几乎平坦的鞍点附近犹豫很久,Adam 因为把小梯度"放大"了,往往最先逃出来。
4为什么会走"之"字:条件数
在狭长的二次碗里,GD 为什么不直奔谷底?因为梯度垂直于等高线(3.2 节)。在一个扁椭圆上,除了两条主轴,垂直于等高线的方向几乎都指向"对面的谷壁",而不是指向中心。
两个方向的曲率之比叫条件数 \(\kappa = \lambda_{\max}/\lambda_{\min}\),也就是 3.5 节 Hessian 最大和最小特征值之比。κ 越大,碗越扁,GD 的"之"字越密,需要的步数大约随 κ 线性增长。拖动起点和 κ 看看。
注意右图两条线的斜率:GD 那条是 1(步数 ∝ κ),动量那条是 1/2(步数 ∝ √κ)。κ = 100 时,这就是几百步和几十步的差别。神经网络和机器人代价函数的 κ 动辄成千上万,这个差别就是"一晚上训完"和"一周训完"的差别。
5第二个视角:动量是一颗有摩擦的铁球
把 GD 想成"没有质量"的点:每一步的速度完全由当前的坡度决定,上一步怎么走的,它全忘了。动量法给这个点加上质量:速度 \(\v\) 先保留上一步的一部分(比例 \(\beta\)),再加上新的坡度。
在狭长山谷里,横穿山谷方向的梯度一正一负,累积起来互相抵消;沿着谷底方向的梯度始终同号,累积起来越滚越快。这正是 2.5 节的弹簧-阻尼系统:\(1-\beta\) 像摩擦,曲率像弹簧刚度。摩擦太小,球会在谷里来回荡(欠阻尼);摩擦太大,球爬得像 GD 一样慢(过阻尼);刚刚好的时候,最快停到底(临界阻尼)。
- 调节 β(也可以动 η),让铁球在 60 步以内停到谷底(离最小值小于 0.01)。默认的 β = 0.95 和 β = 0 都做不到。
算法视角:动量是"把过去的梯度做指数加权平均",权重按 \(\beta^k\) 衰减,有效记忆大约 \(1/(1-\beta)\) 步。物理视角:动量是对带阻尼的牛顿方程 \(\ddot{\x} + \gamma\dot{\x} = -\nabla f(\x)\) 的离散化。前者告诉你怎么写代码,后者告诉你 β 该怎么调。
6再看数学
提示:把鼠标悬停在公式中带底色的符号上,FIG 2 和 FIG 4 中对应的元素会被高亮(梯度方向、步长、速度、Adam 的轨迹)。
五条更新规则
记 \(\mathbf g_k = \nabla f(\x_k)\)。梯度下降:
动量(heavy ball, Polyak 1964):速度是梯度的累积,位置沿速度走。
Nesterov:先按惯性"预支"一步,在预计落点处求梯度,再修正。冲过头时它能更早刹车。
RMSProp 和 Adam:用梯度平方的滑动平均 \(\mathbf s\) 给每个坐标单独定步长(平方、开方、除法都按分量进行)。Adam 还对梯度本身做滑动平均 \(\mathbf m\),并做偏差修正:\(\mathbf m, \mathbf s\) 从 0 开始,前几步会偏小,除以 \(1-\beta^{k}\) 把它们拉回正常大小。
Adam 的一步大致是 \(\eta \cdot \hat m/\sqrt{\hat s}\)。如果梯度方向稳定,\(\hat m/\sqrt{\hat s}\approx \pm 1\),每个坐标都走约 \(\eta\) 那么远,与坡的陡缓无关。这就是为什么 FIG 2 里 Adam 的学习率要单独设置:它的单位是"参数的单位",而不是"参数 / 梯度"。
二次函数上的精确分析
任何光滑函数在最小值附近都像一个二次碗 \(f(\x)=\tfrac12\x^\top H\x\)(3.5 节的泰勒展开),所以二次函数上的结论处处适用。此时 \(\nabla f = H\x\),GD 的一步是 \(\x_{k+1} = (I-\eta H)\,\x_k\)。在 \(H\) 的特征向量坐标系里,各个方向互不干扰,第 \(i\) 个分量每步乘以 \(1-\eta\lambda_i\),和 FIG 1 的一维情形一模一样:
最慢的方向决定总速度。让最陡和最缓两个方向的收缩率一样好,得到最优步长和收敛率:
κ 很大时 \((\kappa-1)/(\kappa+1)\approx 1-2/\kappa\),误差缩小 \(e\) 倍需要约 \(\kappa/2\) 步。动量把每个方向变成一个二阶线性递推(特征方程 \(z^2-(1+\beta-\eta\lambda)z+\beta=0\),FIG 4 右侧实时显示),选 \(\eta = 4/(\sqrt{\lambda_{\max}}+\sqrt{\lambda_{\min}})^2\)、\(\beta = \big(\tfrac{\sqrt\kappa-1}{\sqrt\kappa+1}\big)^2\) 时,所有方向都恰好处在临界附近,收敛率变成:
直觉上:GD 的步长被最陡方向"卡住",在最缓的方向上每步只能前进 \(\eta\lambda_{\min}\approx 2/\kappa\) 的比例;动量在缓的方向上积累速度,把这个比例放大到约 \(2/\sqrt\kappa\),同时在陡的方向上靠符号交替的梯度相互抵消,保持稳定。
7机器人中的它:用梯度下降解逆运动学
一个三连杆平面机械臂,关节角 \(\mathbf q = (q_1, q_2, q_3)\),正运动学 \(\mathrm{FK}(\mathbf q)\) 给出末端位置。想让末端到达目标 \(\mathbf t\),就最小化距离的平方:
梯度来自链式法则:\(\mathbf e\) 对 \(\mathbf q\) 的导数就是机器人雅可比 \(J\)(3.3、3.4 节),于是 \(\nabla C = J^\top\mathbf e\)。它的含义很直观:每个关节"感受"到的力,等于误差向量在这个关节能推动末端的方向上的投影。拖动黄色目标,看机械臂一步步追过去。
- 把目标拖到离基座 0.8 以内,让机械臂把自己"折"起来够到它(误差小于 0.01)。哪个优化器最快?
工业级 IK 求解器(如 KDL、TRAC-IK)很少用纯梯度下降,而是用阻尼最小二乘 / Levenberg–Marquardt(6.6 节),它利用了 \(J^\top J\) 这个曲率信息,几步就收敛。但在神经网络训练里,参数动辄上亿,算不起 Hessian,Adam / AdamW 就是默认选择:从 ACT、Diffusion Policy 到 RT-2、OpenVLA 一类的 VLA 模型,训练脚本里几乎都写着 AdamW。轨迹优化、可微仿真、NeRF / 3D Gaussian Splatting 场景重建,也大量使用 Adam。
8常见误区
最常见的原因就是学习率太大(\(\eta > 2/\lambda_{\max}\)),参数一步比一步大,最后溢出。反过来,学习率太小时损失曲线几乎是平的,看起来像"模型学不动"。调参的第一步:在对数刻度上扫学习率(如 1e-5、1e-4、1e-3……),先找到"刚好不发散"的那个量级,再往下退一档。
相机标定里,焦距约 600 像素,畸变系数约 0.01;轨迹优化里,位置以米计,时间以秒计,力矩以牛·米计。单位不同,曲率就能差好几个数量级,κ 巨大,GD 会走"之"字走到天荒地老。办法是先把每个参数归一化到差不多的量级(或输入特征做标准化);这其实就是在手动做 RMSProp / Adam 自动做的事。
低维直觉会让你担心掉进局部最小值。但在上百万维的空间里,一个临界点要成为局部最小值,需要所有方向的曲率都向上,这很难;更常见的是鞍点和大片平坦区域:有些方向向上,有些向下,梯度很小,GD 在附近磨蹭很久(FIG 2 的"鞍点"地形)。动量和自适应方法逃离鞍点的速度通常快得多。
\(\eta=10^{-3}, \beta_1=0.9, \beta_2=0.999\) 对很多神经网络是不错的起点,但不是普适答案。Adam 在固定学习率下常常在最小值附近来回抖动(FIG 2 的 RMSProp 也是),所以实际训练要配合学习率衰减;权重衰减要用 AdamW 的"解耦"写法;在一些凸问题或小问题上,调好的 SGD + 动量甚至 L-BFGS 反而更好。
梯度写错一个符号,优化器照样会跑,只是往错误的方向跑,而且很难察觉。上线前用中心差分对比一下:
误差是 \(O(h^2)\)。相对误差在 1e-6 左右说明梯度没问题,在 1e-2 左右说明一定有 bug。本页 FIG 5 的 \(J^\top\mathbf e\) 就是这样验证过的。
9小练习
10延伸阅读
- G. Goh, Why Momentum Really Works, Distill, 2017 — 本节 FIG 3、FIG 4 的灵感来源,对动量的特征值分析讲得极好
- S. Ruder, An overview of gradient descent optimization algorithms, arXiv:1609.04747 — 各种优化器的简明综述
- S. Boyd & L. Vandenberghe, Convex Optimization, Ch. 9 — 无约束极小化:梯度法、最速下降、条件数与收敛分析
- D. P. Kingma & J. Ba, Adam: A Method for Stochastic Optimization, ICLR 2015 — Adam 原论文,包括偏差修正的推导