Part I · 第 6 章 优化 · 6.3

梯度下降家族

蒙着眼站在山坡上,只能用脚感受脚下的坡度,怎样走到谷底?朝最陡的下坡方向迈一步,再感受,再迈一步。这就是梯度下降。它的几个"亲戚"(动量、Nesterov、RMSProp、Adam)只是在"步子怎么迈"上多动了一点脑筋,却往往决定了你是 10 步到达,还是 10000 步还在山腰打转。

约 35 分钟 前置:3.2 梯度 · 3.5 Hessian · 6.1 什么是优化问题 5 个交互

    1为什么机器人需要它

    训练一个视觉-语言-动作模型 (VLA) 或扩散策略 (diffusion policy),就是调整几亿个参数,让"预测动作与示范动作之间的误差"变小。让机械臂末端到达指定位置的数值逆运动学 (inverse kinematics),是调整几个关节角,让"末端与目标之间的距离"变小。轨迹优化、相机标定、点云配准也一样,都是在一个代价函数上找最低点。

    这些问题规模天差地别,但最常用的求解办法都是同一个:沿着梯度往下走。6.1 节讲了怎样把问题写成"最小化 \(f(\x)\)";这一节讲怎样真的走下去,以及为什么同样是往下走,有人 10 步就到了,有人走了一万步还在山腰。

    2直觉:蒙眼下山

    你看不到整座山,只能感觉脚下的坡度。3.2 节说过,梯度 \(\nabla f\) 指向上坡最陡的方向,它的长度就是坡有多陡。所以最自然的规则是:朝 \(-\nabla f\) 迈一步,步长正比于坡度。比例系数 \(\eta\) 叫学习率 (learning rate),也叫步长。

    步子大小是第一个难题。步子太小,走到天黑也到不了;步子太大,一脚跨过谷底,落在对面更高的坡上,然后越跳越高。先在一维里感受一下。

    FIG 1一维梯度下降:步长的分寸
    点击曲线附近任意位置(或拖动空心圆)设定起点。紫色短线是脚下的切线,它的斜率就是 \(f'(x)\);黄色箭头是下一步 \(-\eta f'(x)\)。抛物线 \(f=\tfrac12 a x^2\) 模式下,右侧色条标出了三个区间:平稳收敛、来回跳但收敛、发散。
    • 在抛物线上找一个学习率,让小球每步都跳到对面,却仍然收敛(至少走 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动手玩:优化器赛跑

    下面是一张"地形图":底色里青色越浓的地方越低,等高线标出等高的位置。点击地图任意位置设定起点,五个优化器会同时出发。右侧可以开关每个优化器、看它的实时更新公式;下方是损失曲线(对数坐标,越往下越接近最小值)。换一种地形、拖一拖滑块,看看谁能赢。

    FIG 2优化器赛跑
    实时公式
    两个学习率是分开的:GD、动量、Nesterov 的一步是 \(\eta\times\) 梯度,所以 \(\eta\) 的合适大小取决于坡有多陡;RMSProp 和 Adam 先把梯度"归一化",一步大约就是 \(\eta_{\text{adapt}}\) 这么长。RMSProp 的 \(\rho\) 固定为 0.9。切换地形时学习率会恢复为该地形的推荐值。
    试一试
    • 在 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 的"之"字越密,需要的步数大约随 κ 线性增长。拖动起点和 κ 看看。

    FIG 3梯度垂直于等高线,不指向最小值
    收敛步数 vs. κ(对数坐标)
    GD 用它的最优步长 \(\eta^\star = 2/(\lambda_{\max}+\lambda_{\min})\);动量用 \(\eta = 1/\lambda_{\max}\)、\(\beta = (1-1/\sqrt\kappa)^2\),恰好让最缓的方向处于临界阻尼(第 5、6 节)。收敛的标准是离最小值的距离缩小到起点的千分之一。黄色箭头是 \(-\nabla f\),青色虚线是"真正该去的方向"。右图中实线是理论值,圆点是左图实际走的步数。

    注意右图两条线的斜率:GD 那条是 1(步数 ∝ κ),动量那条是 1/2(步数 ∝ √κ)。κ = 100 时,这就是几百步和几十步的差别。神经网络和机器人代价函数的 κ 动辄成千上万,这个差别就是"一晚上训完"和"一周训完"的差别。

    5第二个视角:动量是一颗有摩擦的铁球

    把 GD 想成"没有质量"的点:每一步的速度完全由当前的坡度决定,上一步怎么走的,它全忘了。动量法给这个点加上质量:速度 \(\v\) 先保留上一步的一部分(比例 \(\beta\)),再加上新的坡度。

    在狭长山谷里,横穿山谷方向的梯度一正一负,累积起来互相抵消;沿着谷底方向的梯度始终同号,累积起来越滚越快。这正是 2.5 节的弹簧-阻尼系统:\(1-\beta\) 像摩擦,曲率像弹簧刚度。摩擦太小,球会在谷里来回荡(欠阻尼);摩擦太大,球爬得像 GD 一样慢(过阻尼);刚刚好的时候,最快停到底(临界阻尼)。

    FIG 4山谷里的铁球
    两个方向上的位置随步数变化
    山谷沿 x 方向很缓(曲率 0.05),沿 y 方向很陡(曲率 2),κ = 40。紫色箭头是速度(这一步的位移),黄色箭头是这一步新加的 \(-\eta\nabla f\),两者都放大了 2 倍(太长时截短)。点击地图可以换起点。右侧曲线就是两个方向上的"弹簧振动":β = 0 时 x 方向像过阻尼一样爬,β 太大时两个方向都在荡。
    • 调节 β(也可以动 η),让铁球在 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)\)。梯度下降:

    $$ \x_{k+1} = \x_k - \hl{lr}{\eta}\, \hl{grad}{\cv{\nabla f(\x_k)}} $$

    动量(heavy ball, Polyak 1964):速度是梯度的累积,位置沿速度走。

    $$ \hl{vel}{\cw{\v_{k+1}}} = \hl{beta}{\beta}\,\cw{\v_k} + \cv{\mathbf g_k}, \qquad \x_{k+1} = \x_k - \eta\, \cw{\v_{k+1}} $$

    Nesterov:先按惯性"预支"一步,在预计落点处求梯度,再修正。冲过头时它能更早刹车。

    $$ \cw{\v_{k+1}} = \beta\,\cw{\v_k} + \cv{\nabla f(\x_k - \eta\beta\,\v_k)}, \qquad \x_{k+1} = \x_k - \eta\, \cw{\v_{k+1}} $$

    RMSProp 和 Adam:用梯度平方的滑动平均 \(\mathbf s\) 给每个坐标单独定步长(平方、开方、除法都按分量进行)。Adam 还对梯度本身做滑动平均 \(\mathbf m\),并做偏差修正:\(\mathbf m, \mathbf s\) 从 0 开始,前几步会偏小,除以 \(1-\beta^{k}\) 把它们拉回正常大小。

    $$ \text{RMSProp:}\quad \mathbf s_{k+1} = \rho\,\mathbf s_k + (1-\rho)\,\mathbf g_k^{2}, \qquad \x_{k+1} = \x_k - \frac{\eta}{\sqrt{\mathbf s_{k+1}}+\epsilon}\,\mathbf g_k $$ $$ \hl{adam}{\ck{\text{Adam:}}}\quad \begin{aligned} \mathbf m_{k+1} &= \beta_1 \mathbf m_k + (1-\beta_1)\,\mathbf g_k, & \hat{\mathbf m} &= \mathbf m_{k+1}/(1-\beta_1^{\,k+1}) \\ \mathbf s_{k+1} &= \beta_2 \mathbf s_k + (1-\beta_2)\,\mathbf g_k^{2}, & \hat{\mathbf s} &= \mathbf s_{k+1}/(1-\beta_2^{\,k+1}) \\ \x_{k+1} &= \x_k - \eta\,\hat{\mathbf m}\,/\,(\sqrt{\hat{\mathbf s}}+\epsilon) \end{aligned} $$

    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 的一维情形一模一样:

    $$ c_{i,k+1} = (1-\hl{lr}{\eta}\,\lambda_i)\,c_{i,k} \quad\Longrightarrow\quad \text{converges} \iff 0 < \hl{lr}{\eta} < \frac{2}{\lambda_{\max}} $$

    最慢的方向决定总速度。让最陡和最缓两个方向的收缩率一样好,得到最优步长和收敛率:

    $$ \eta^\star = \frac{2}{\lambda_{\max}+\lambda_{\min}}, \qquad \|\x_k\| \le \Big(\frac{\hl{kappa}{\kappa}-1}{\hl{kappa}{\kappa}+1}\Big)^{k}\|\x_0\|, \qquad \hl{kappa}{\kappa} = \frac{\lambda_{\max}}{\lambda_{\min}} $$

    κ 很大时 \((\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\) 时,所有方向都恰好处在临界附近,收敛率变成:

    $$ \text{rate}_{\text{momentum}} = \frac{\sqrt{\kappa}-1}{\sqrt{\kappa}+1} \approx 1-\frac{2}{\sqrt{\kappa}} \qquad\Longrightarrow\qquad \text{steps} \propto \sqrt{\kappa}\ \ \text{instead of}\ \ \kappa $$

    直觉上: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\),就最小化距离的平方:

    $$ C(\mathbf q) = \tfrac12\,\big\|\,\underbrace{\mathrm{FK}(\mathbf q) - \mathbf t}_{\ci{\mathbf e}}\,\big\|^2, \qquad \nabla_{\mathbf q} C = J(\mathbf q)^\top\,\ci{\mathbf e} $$

    梯度来自链式法则:\(\mathbf e\) 对 \(\mathbf q\) 的导数就是机器人雅可比 \(J\)(3.3、3.4 节),于是 \(\nabla C = J^\top\mathbf e\)。它的含义很直观:每个关节"感受"到的力,等于误差向量在这个关节能推动末端的方向上的投影。拖动黄色目标,看机械臂一步步追过去。

    FIG 5数值逆运动学
    连杆长 1.0、0.8、0.6,虚线圆是工作空间边界(半径 2.4)。红色虚线是误差 \(\mathbf e\),淡色的手臂是之前的迭代。目标放到圈外时,机械臂会伸直指向它,代价停在一个大于 0 的值:梯度为零,但目标没有到达。右下角是代价随迭代的变化(对数坐标)。
    • 把目标拖到离基座 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常见误区

    误区 1:损失变成 NaN,以为是代码 bug

    最常见的原因就是学习率太大(\(\eta > 2/\lambda_{\max}\)),参数一步比一步大,最后溢出。反过来,学习率太小时损失曲线几乎是平的,看起来像"模型学不动"。调参的第一步:在对数刻度上扫学习率(如 1e-5、1e-4、1e-3……),先找到"刚好不发散"的那个量级,再往下退一档。

    误区 2:参数单位悬殊,却不做缩放

    相机标定里,焦距约 600 像素,畸变系数约 0.01;轨迹优化里,位置以米计,时间以秒计,力矩以牛·米计。单位不同,曲率就能差好几个数量级,κ 巨大,GD 会走"之"字走到天荒地老。办法是先把每个参数归一化到差不多的量级(或输入特征做标准化);这其实就是在手动做 RMSProp / Adam 自动做的事。

    误区 3:在高维空间里最怕局部最小值

    低维直觉会让你担心掉进局部最小值。但在上百万维的空间里,一个临界点要成为局部最小值,需要所有方向的曲率都向上,这很难;更常见的是鞍点和大片平坦区域:有些方向向上,有些向下,梯度很小,GD 在附近磨蹭很久(FIG 2 的"鞍点"地形)。动量和自适应方法逃离鞍点的速度通常快得多。

    误区 4:Adam 的默认参数是万能的

    \(\eta=10^{-3}, \beta_1=0.9, \beta_2=0.999\) 对很多神经网络是不错的起点,但不是普适答案。Adam 在固定学习率下常常在最小值附近来回抖动(FIG 2 的 RMSProp 也是),所以实际训练要配合学习率衰减;权重衰减要用 AdamW 的"解耦"写法;在一些凸问题或小问题上,调好的 SGD + 动量甚至 L-BFGS 反而更好。

    误区 5:手推的梯度不做检查

    梯度写错一个符号,优化器照样会跑,只是往错误的方向跑,而且很难察觉。上线前用中心差分对比一下:

    $$ \frac{\partial f}{\partial x_i} \approx \frac{f(\x + h\,\mathbf e_i) - f(\x - h\,\mathbf e_i)}{2h}, \qquad h \approx 10^{-5}\text{ (double precision)} $$

    误差是 \(O(h^2)\)。相对误差在 1e-6 左右说明梯度没问题,在 1e-2 左右说明一定有 bug。本页 FIG 5 的 \(J^\top\mathbf e\) 就是这样验证过的。

    9小练习

    1. 对 \(f(x)=\tfrac12\cdot 4x^2\) 用学习率 \(\eta = 0.6\) 做梯度下降,会怎样?
    每步乘以 \(1-\eta a = 1-0.6\times4 = -1.4\)。负号表示跳到对面,绝对值大于 1 表示越跳越远。临界学习率是 \(2/a = 0.5\)。
    2. 一个二次函数的 Hessian 特征值是 10 和 0.1。梯度下降能稳定的最大学习率约是多少?此时条件数是多少?
    稳定条件 \(\eta < 2/\lambda_{\max} = 0.2\),\(\kappa = 10/0.1 = 100\)。用这样的步长,在 \(\lambda=0.1\) 的方向上每步只缩小约 2%,GD 需要几百步;动量大约只需几十步。
    3. 在一个狭长的椭圆形山谷里,GD 为什么走"之"字?
    椭圆越扁(κ 越大),梯度方向和"指向中心的方向"夹角越大。为了不在陡方向发散,步长又必须小于 \(2/\lambda_{\max}\),于是在缓方向上进展很慢,在陡方向上来回弹。
    4. 机械臂 IK 的代价 \(C(\mathbf q)=\tfrac12\|\mathrm{FK}(\mathbf q)-\mathbf t\|^2\),它对关节角的梯度是?(\(J\) 是 \(\mathrm{FK}\) 的雅可比,\(\mathbf e = \mathrm{FK}(\mathbf q)-\mathbf t\))
    链式法则:\(dC = \mathbf e^\top d\mathbf e = \mathbf e^\top J\,d\mathbf q\),所以 \(\nabla C = J^\top\mathbf e\)。维度也对得上:\(J\) 是 2×3,\(\mathbf e\) 是 2 维,\(J^\top\mathbf e\) 是 3 维,正好每个关节一个分量。\(J\mathbf e\) 的维度对不上;\(J\) 不是方阵,没有普通的逆。

    10延伸阅读