第 11 章 深度学习优化算法梯度下降

优化算法:让模型学得更好

本章来源:本文整理自《动手学深度学习》(zh.d2l.ai)第 11 章《优化算法》,原书作者 Aston Zhang、李沐、Zachary C. Lipton、Alexander J. Smola。

本章讲什么

前面反复提到"优化"——找到让损失函数最小的参数。这一章系统介绍优化算法:从最基础的梯度下降,到实践中几乎人人都在用的 Adam,再到学习率怎么调度。

一个重要的前提认知:优化和深度学习的追求不完全一致。优化关注最小化目标函数(降低训练误差),而深度学习真正关心的是在有限数据下找到泛化好的模型(降低泛化误差)。两者的最小值点往往不同——训练误差低不代表泛化好,就像考试题做得好不代表真学会了。

优化面临的三座大山

深度学习的目标函数有三个典型困难:

  • 局部最小值:梯度为零但只是局部最优。高维问题中到处都是这样的点。
  • 鞍点:所有梯度都消失,但既不是极大也不是极小。想象马鞍——一个方向是谷、另一个方向是脊,放个球上去,它既不是最低也不是最高。高维空间中鞍点比局部最小值更常见。
  • 梯度消失:梯度趋近零导致训练停滞(比如 tanh 在输入较大处导数趋近 0)。这是 ReLU 被引入的原因。

有趣的是,小批量 SGD 的天然噪声反而是跳出局部最小值的有利特性。

凸性:一个理想的分析环境

凸函数在优化理论里是理想环境:凸函数的局部极小值就是全局极小值(怎么走都不会卡在半山腰的坑里),算法分析也容易得多。虽然深度学习问题大多非凸,但局部极小值附近往往表现出一定凸性。

詹森不等式是凸性的核心工具:凸函数的期望 ≥ 期望的凸函数,用它可以用简单表达式约束复杂表达式。

约束优化里还有几个概念:拉格朗日乘数(把约束并入目标函数)、惩罚(实践中直接加惩罚项,如权重衰减)、投影(把点映射到约束集里最近的点,如梯度截断)。

梯度下降:优化的起点

梯度下降的思想来自泰勒展开:函数在某点附近可用一阶近似,沿负梯度方向移动能使函数值下降。于是迭代更新:x ← x - η·梯度,直到梯度足够小。

学习率 η 极其关键:

  • 太小:进展缓慢,走很多步还离最优很远。
  • 太大:一阶近似失效,可能越过最优点甚至发散。

就像"蒙眼下山":梯度告诉你哪个方向最陡,每次朝最陡方向迈一步。步子(学习率)太大可能一步跨过谷底甚至滚下山(发散),太小则下得很慢。

更高级的牛顿法用二阶导数(Hessian 矩阵)一步确定最优步长,凸问题上收敛极快,但 Hessian 计算和存储代价太高,非凸时还可能上升,实践中很少直接用。

随机梯度下降(SGD)

梯度下降每次迭代要计算全部 n 个样本的梯度,代价 O(n),大数据集下不可行。SGD 每次只随机采样一个样本,用它的梯度更新参数,单步代价降到 O(1)。

关键性质:随机梯度是完整梯度的无偏估计——期望等于真实梯度,所以平均意义上它是良好估计,只是带噪声(轨迹比梯度下降嘈杂得多,接近最优点后仍会抖动)。

有噪声就带来新问题:常数学习率下永远收敛不到最优点,所以必须动态降低学习率。基本策略:分段常数、指数衰减(太激进)、多项式衰减(表现好)。

"用抽查代替全面调查":完整梯度像普查(准但贵),随机梯度像抽查一两个样本(快但有噪声),抽查多次平均下来结果也可靠。

小批量随机梯度下降:实用之选

梯度下降"数据高效但计算不高效",SGD"计算高效但数据不高效"。小批量随机梯度下降每次用一小批样本,是两者的折中。

计算效率的根源是向量化缓存:一次性算矩阵乘法比逐元素循环快几个数量级。小批量的统计性质:梯度期望不变(无偏),方差显著降低——b 个独立梯度的平均使标准差降为 b 的平方根的倒数。

但批量大到一定程度后,方差的额外减少相比计算代价的线性增长就微不足道了。实践中选择"能填满 GPU 内存又足够大"的批量。

"点外卖凑单"的类比:一次只送一份(SGD)太浪费运费,一次全送(GD)又慢又占地方,凑个"刚好装满配送箱"的份量(小批量)最划算。

动量法

SGD 有两个问题:梯度噪声大,以及病态条件数问题(不同方向进展速度差异悬殊,比如"窄峡谷"型的目标函数——一个方向平缓、一个方向陡峭)。

动量法的核心思想是"泄漏平均值":用过去梯度的指数加权平均替代瞬时梯度做更新。历史梯度按指数衰减,较大的衰减系数相当于更长期的"记忆"。

好处:在噪声大的方向(梯度方向随机振荡),平均后相互抵消,步长变小;在方向一致的方向(如峡谷底部),平均后梯度对齐,步长加大——相当于"自动判断该冲还是该稳"。

"滚下山坡的小球":普通梯度下降像每一步都重新评估方向的谨慎步行者,动量法像滚动的球——球有惯性,沿峡谷方向越滚越快,横向抖动被惯性抵消。β 像摩擦力,越大惯性越强、越能冲过小坑。

AdaGrad:按坐标自适应

AdaGrad 解决"稀疏特征"问题:语言模型中不常出现的词需要更大的学习率才能学到足够信息,而常见特征需要学习率快点降下来避免过拟合。固定全局学习率无法兼顾两者。

AdaGrad 的思路:为每个坐标维护独立的、自适应的学习率——用历史梯度平方的累加作为缩放因子。梯度大的坐标学习率被压得小,梯度小的坐标学习率相对大,实现"逐坐标自适应"。

"按科目难度分配学习时间":不常考但难的知识点(稀疏特征)要多分配时间(大学习率),常考的知识点学得快、时间递减。

缺陷:累积值只增不减,学习率持续衰减,对凸问题够用,但深度学习的非凸问题上衰减可能过于剧烈,训练后期移动很慢。

RMSProp 与 AdaDelta

RMSProp 针对 AdaGrad 的缺陷做修复:把"梯度平方的累加"改成"梯度平方的指数加权平均"(泄漏平均值)。这样累积值不会无限增长,学习率可以独立控制、不随迭代自动衰减到零。

AdaDelta 是另一种变体,最大特点是没有学习率参数——用"参数本身变化量"的尺度来校准更新步长。它同时跟踪"过去步长多大"和"当前梯度多大",两者相除得到与梯度绝对尺度无关的相对步长。

"照自己过去的步伐走路":不看固定步幅表(学习率),而是参照"我上次迈了多大步"来调整当前步伐,再结合当前坡度决定走多远。

Adam:集大成者

Adam 把之前所有技巧合并进一个算法:SGD 的高效、小批量的向量化、动量法的历史梯度累积、AdaGrad 的逐坐标缩放、RMSProp 的学习率分离。

它用两个指数加权移动平均状态:一个估计梯度的一阶矩(动量,记住方向),一个估计梯度的二阶矩(方差,知道路况)。关键细节是偏差校正——状态从零初始化会产生初始偏差,要校正成无偏估计。

"既有方向感又有节奏感的司机":动量像记住要去的大方向(惯性),二阶矩像知道路况好坏(哪段路颠簸就开慢点),Adam 把两者结合——朝惯性方向开、路面差的地方自动减速,还带一个可调的基础油门(学习率)。

优点:对超参数不太敏感、通常开箱即用。Adam 是实践中应用最广泛的优化器之一。

学习率调度

调整学习率的调度策略与实际算法本身同等重要:

  • 学习率太大发散、太小训练过慢或次优。
  • 预热(warmup):初始参数随机,最初的更新方向可能无意义,一开始用大步长没好处——应先小步预热再加速。
  • 常用策略:分段常数调度(在固定里程碑把学习率减半,让优化先跑到驻点再降学习率精修)、余弦调度(学习率按余弦曲线平滑下降)、单因子衰减(每步乘一个衰减因子)。

"炒菜火候管理":一开始大火(预热)让锅热起来,但一直大火会糊(发散),于是中间逐渐调小火(衰减),最后文火慢炖(小学习率精修)。

小结

  • 优化与深度学习追求不同:前者降训练误差,后者要泛化好。
  • 局部最小值、鞍点、梯度消失是深度优化的三大挑战。
  • 梯度下降沿负梯度迭代,学习率过大发散、过小缓慢。
  • SGD 用单个样本梯度(无偏但有噪声),需降低学习率。
  • 小批量 SGD 兼顾计算效率和统计效率,是实用之选。
  • 动量法用梯度指数加权平均,加速一致方向、抑制噪声。
  • AdaGrad、RMSProp、AdaDelta 逐坐标自适应学习率。
  • Adam 集大成,是实践中应用最广的优化器。
  • 学习率调度(预热、分段常数、余弦)与算法同等重要。

关键术语

术语 一句话解释
局部最小值 / 鞍点 局部最优 / 梯度为零但非极值的点
凸函数 局部极小值即全局极小值的函数
詹森不等式 凸函数期望的不等式
梯度下降 沿负梯度方向迭代更新参数
学习率 控制每步更新幅度的超参数
SGD 用单个样本梯度更新参数
小批量 SGD 每次用一小批样本更新参数
向量化 用矩阵运算批量处理数据
动量法 过去梯度的指数加权平均
AdaGrad / RMSProp / AdaDelta 逐坐标自适应学习率的算法家族
Adam 结合动量与自适应缩放的一阶优化算法
学习率调度 按迭代自动调整学习率的机制
预热 初始阶段学习率先升后降