1. 困境起点下山这件事为什么会让算法被“逼”出来做机器学习的朋友应该都有过这种体验早上信心满满地把模型丢上去训练下午回来看loss曲线发现它像一条坏掉的心电图——要么不动要么剧烈抖动要么卡在某个位置死活下不去。这时候你脑子里冒出来的第一个想法往往是这优化算法怎么回事先说结论我们今天耳熟能详的梯度下降、随机梯度下降、动量法、Adam等等没有一个是凭空设计出来的。它们全是被“逼”出来的。被什么逼的被下山这个动作本身逼的。你可以把训练模型想象成一个人站在一座群山环绕的山顶上任务是走到山脚的最低点。山的形状就是损失函数——横轴是模型参数纵轴是损失值。你每走一步就是在调整一次参数走完整个行程就是完成了一次迭代优化。而“梯度”这个词说白了就是告诉你当前位置哪个方向是下坡、坡度有多陡的指示牌。问题是真实场景下的“山”远比想象中复杂。这座山可能有峡谷、有悬崖、有鞍部、有局部坑洼还可能大雾弥漫让你看不清远处的路。朴素的想法是既然要下山那我就每一步都朝最陡的下坡方向走步子迈得大一点不就完了但你真这么试过就会发现事情没那么简单。步子迈大了容易扯到蛋——在优化里这叫“发散”loss直接飞到天文数字步子迈小了又慢得让人抓狂训练三天三夜还在山腰上磨蹭。所以迭代优化算法的发展史本质上就是一群聪明人不断被困在山里、然后想办法自救的历史。今天看起来优雅的数学公式背后全是血泪教训。这篇文章我就把这段“被逼出来”的过程完整拆开给你看顺便把每个方案背后的直觉、数学和踩坑经验一次讲透。适合谁来读如果你正在学机器学习、正在调模型但看不懂loss曲线的脾气或者你已经能用框架训练模型了但从来没深究过优化器里面到底在算什么这篇文章就是给你写的。我尽量不堆公式但该给的关键公式还是会给出——因为只有看懂公式你才能理解为什么Adam要设那两个beta为什么学习率不能乱调。2. 朴素下山梯度下降的基本逻辑和它暴露的三个硬伤2.1 最朴素的下山策略沿着最陡方向走把时间拨回最原始的思路。假设你有一个损失函数 L(θ)其中 θ 是模型的参数向量。你要找一组 θ 让 L(θ) 最小。微积分告诉你函数在某一点的梯度 ∇L(θ) 指向的是上升最快的方向那反方向就是下降最快的方向。于是更新规则呼之欲出θ_new θ_old − η · ∇L(θ_old)这里 η 就是学习率也就是你每一步迈多大。这个式子干净、优雅、有数学依据1853年就有数学家提出过类似思想后来被LeCun等人在80年代末引入神经网络训练。但真到用的时候你立刻会遇到三个非常现实的问题个个都让人头大。第一个问题是全局梯度算不起。假设你的训练集有100万张图片每算一次梯度就要把100万张图片全部过一遍模型算完才能走一步。一步就要等几个小时走到收敛得等到下辈子。这就是批量梯度下降Batch Gradient Descent的致命伤。第二个问题是学习率 η 是个玄学。设大了loss曲线直接起飞设小了loss下降慢得像蜗牛爬。而且不同参数、不同训练阶段对步长的需求完全不一样——有的参数需要大步快跑有的参数需要小步慢挪。用一个全局统一的学习率去应对所有情况本质上就是在拿一把尺子量所有人的腰围肯定有人不合适。第三个问题是这座山不是平滑的碗。真实损失函数地形复杂有局部极小值看着像谷底其实不是、有鞍部一个方向是下坡另一个方向是上坡梯度接近零但根本不是谷底、有细长峡谷梯度方向来回震荡沿着峡谷壁左右乱撞就是不肯往谷底走。这三个硬伤就是后续所有优化算法的“压力源”。你甚至可以画一条时间线每个新算法的出现都是因为前一个算法在某座“山”上摔了跟头。2.2 为什么不能“一步到位”损失函数地形的真实复杂度我见过很多初学者会问为什么不直接算出全局最小值这是个好问题答案也很残酷对神经网络这种高度非凸的损失函数来说全局最小值的精确解在数学上是不可行的。没人能写出一个闭式解你只能靠迭代逼近。更麻烦的是高维空间里的“地形”和三维直觉完全不一样。三维空间里你觉得一处凹陷就是谷底了但在高维空间里几乎所有点都是鞍部——某个方向上是谷底、另一个方向上却是山脊。梯度在这些点附近会变得非常小模型就像走到了一个平台怎么推都不动。这就是所谓的“梯度消失”式困局和深层网络里的梯度消失还不是一回事但效果类似loss卡住训练停滞。不光是鞍部还有峡谷。想象一个椭圆形的碗长轴方向很平缓、短轴方向很陡峭。你用梯度下降去走会发现在短轴方向上步子不敢迈太大一迈就冲过头在长轴方向上又总觉得走得太慢。结果就是loss曲线呈现一种锯齿状下降看着在动实际效率极低。这些地形特征不是理论上的杞人忧天。你随便训练一个稍微深一点的网络用最朴素的梯度下降去跑基本都会遇到上面几种情况之一。这也是为什么后来会出现动量法、自适应学习率这些东西——它们每一个都是在针对某一类“地形病”开药方。3. 被逼出来的三个拐点随机梯度下降、动量法与自适应学习率3.1 拐点一算不起梯度于是有了随机梯度下降第一个被逼出来的方案是针对“算不起”这个痛点的。既然全量梯度太贵那我不看完整座山只看眼前这一小块地行不行于是随机梯度下降Stochastic Gradient DescentSGD登场了。SGD的核心改动就一句话每次只随机抽一个样本或者一小批样本来估计梯度然后走一步。这里的逻辑和做民意调查一样——你不需要问完全部人口才知道选举倾向抽样1000个人就够估个八九不离十。单次梯度估计当然有噪声但噪声在优化里不全是坏事它甚至能帮模型跳出某些浅的局部坑洼。实操中现在几乎没人用纯单个样本的SGD大家都用Mini-batch SGD——每次取32、64、128个样本算平均梯度。这个“批大小”batch size是一个非常重要的超参数后面我会专门讲。批大小选得好训练又稳又快选得不好要么噪声太大到处乱跳要么太稳了卡在局部极小值出不来。SGD在1986年就有雏形2010年左右在深度学习复兴时被大规模使用。它解决了一个核心矛盾让训练在有限算力下能跑起来。但注意SGD只解决了“梯度太贵”的问题它并没有解决学习率难调、地形复杂这两个痛点。所以它只是第一个拐点而不是终点。3.2 拐点二在峡谷里乱撞于是有了动量法真正在实操中让人崩溃的是峡谷震荡问题。我拿一个具体的例子说明假设损失函数是 f(x, y) 0.1x² 10y²。在x方向坡度平缓在y方向坡度陡峭。你用SGD去跑学习率稍微大一点y方向就会来回震荡、发散学习率小一点x方向就慢得让人绝望。这个情境是不是很眼熟对这就是你训练模型时经常看到的loss曲线像锯齿一样抖动的原因。梯度下降在一个方向上过于敏感在另一个方向上又过于迟钝。怎么治物理直觉来了下山的人如果觉得每一步都走不稳他会怎么做他会借助惯性——先让自己朝某个方向跑起来靠着动量冲过那些坑坑洼洼。这就是动量法Momentum的核心思想不要让每一步完全由当前位置的梯度决定而是让历史速度参与决策。数学上就是引入一个速度变量 vv_t γ · v_{t−1} η · ∇L(θ_t)θ_{t1} θ_t − v_t其中 γ 通常取0.9左右。你可以把 v 理解成一个积累了历史梯度的“惯性速度”。如果之前一直在朝x方向走那即使当前点的梯度在x方向很小速度也会带着你继续往前走而在y方向上因为梯度方向来回变速度会被平均抵消掉震荡自然就减轻了。我用上面那个例子实测过纯SGD跑可能要上千步才能收敛加动量之后一两百步就到了。这个提升不是微调级别的是质变级别的。动量法可以说是第一个真正针对“地形”设计的优化器形态它标志着优化算法从“只看当前步”进化到“回顾历史步”。动量法还有一个进阶版本叫Nesterov加速梯度NAG思路是“先顺着速度方向探头看一眼再用那个位置的梯度修正方向”。用我的话说就是别等撞到墙了再刹车先看看前面是不是悬崖。NAG在部分场景下比标准动量收敛更快更稳但核心思想是一样的——利用历史信息优化当前决策。3.3 拐点三一把尺子量所有参数于是有了自适应学习率动量解决了方向震荡但还有一个问题它管不了不同参数该用不同步长。回到那个椭圆碗的例子就算加了动量x方向和y方向需要的学习率依然是矛盾的。动量只能平均掉方向上的震荡没法改变每个方向上的缩放比例。那如果给每个参数单独配一个学习率呢这就是自适应学习率方法的核心思路。最早的代表是AdaGrad2011年它对每个参数累积历史梯度的平方然后用它的平方根去缩放学习率θ_{i, t1} θ_{i, t} − (η / sqrt(G_{i, t} ε)) · ∇L(θ_{i, t})其中 G_{i, t} 是参数 i 到目前为止所有梯度平方的累积和。直观理解某个参数方向如果一直梯度很大、波动很剧烈那它的累积G就大步长就被自动压小反之如果某个参数方向一直很平缓G就小步长就自动放大。这就是自适应的含义。但AdaGrad有一个致命问题G是单调累积的只会越来越大所以学习率只会越来越小训练到后期几乎走不动路。这就像一个下山的人越走越谨慎最后干脆坐在原地不动了。于是有了RMSProp2012年和AdaDelta2012年。它们的改进是不要累积全部历史梯度平方而是用一个滑动平均来估计近期梯度的大小。用RMSProp的更新式E[g²]t β · E[g²]{t−1} (1−β) · g²_tθ_{t1} θ_t − (η / sqrt(E[g²]_t ε)) · g_tβ 通常取0.9或0.99。这意味着算法只关心“最近这一段路的坡度情况”把远古历史忘掉。这样学习率既有自适应能力又不会衰减到零。RMSProp在训练RNN等场景下表现非常好因为它能应对梯度尺度剧烈变化的序列数据。到这里两个关键思路都齐了动量管方向、自适应管步长。那接下来自然而然的问题就是能不能把两者合起来能这就是Adam。4. 集大成者Adam是怎么炼成的以及它为什么不是万能药4.1 Adam的组装逻辑动量 自适应 偏差校正AdamAdaptive Moment Estimation是2014年底提出的作者是两位研究者。它在机器学习圈的地位相当于手机界的安卓——不一定是每个维度都最优但综合体验最均衡属于“默认选择”。Adam的更新逻辑分三步。第一步维护一阶矩估计 m_t相当于动量它对梯度做指数滑动平均方向信息就靠它。第二步维护二阶矩估计 v_t相当于自适应它对梯度平方做指数滑动平均步长缩放就靠它。第三步做偏差校正bias correction把因为从零开始初始化导致的估计偏差修正掉。完整更新式是m_t β₁ · m_{t−1} (1−β₁) · g_tv_t β₂ · v_{t−1} (1−β₂) · g_t²m̂_t m_t / (1 − β₁^t)v̂_t v_t / (1 − β₂^t)θ_{t1} θ_t − η · m̂_t / (sqrt(v̂_t) ε)默认参数是 β₁0.9、β₂0.999、ε1e-8。这套参数在绝大多数任务上都够用这也是Adam“开箱即用”口碑的来源——不像SGD需要你费心调学习率Adam对学习率的敏感度低很多默认lr0.001基本能跑出像样的结果。为什么要偏差校正因为m和v初始都是0刚开始那几步估计值会被严重拉低如果不校正前几步会走得特别小气。校正之后前几步的估计就相对客观了。这个细节很多讲解会跳过但它在实操中确实有影响——头几个iterations的更新量直接决定了参数初始走向。4.2 Adam的优势场景与翻车现场Adam不是完美的这一点必须强调因为我见过太多人把Adam当万能神药然后在不该用的场景翻车。先讲优势。Adam最擅长的是非平稳目标、梯度含噪、参数尺度差异大、训练初期完全不知道该怎么设学习率的任务。典型例子是训练Transformer、RNN这类模型或者做强化学习、GAN训练这种梯度本身就不稳定的场景。在这些情况下Adam的自动缩放能力能帮你省下大量调参时间。再讲翻车现场。最典型的问题是泛化性能。很多研究者在CV任务上做对比实验发现用SGDmomentum训练出来的模型最终测试精度往往比Adam高一点。原因目前主流解释是Adam的自适应步长会让模型收敛到更“尖锐”的极小值点而SGD的噪声和相对固定的步长更容易走到“平坦”的极小值点——平坦的极值点泛化能力通常更好。这也解释了为什么计算机视觉领域很多人对Adam并不感冒坚持用SGD配动量。另一个问题是Adam在某些情况下不收敛。原因是它的二阶矩估计可能在训练过程中出现极端情况导致有效步长突然变大loss反弹。后来出现的AMSGrad等变体就是专门修这个问题的。所以在实操层面我的经验是先在默认Adam下把模型结构和数据管道的bug排除干净然后再根据任务类型决定是否切换到SGD系做最后冲刺。先跑通再调优这是最务实的路径。4.3 优化器家族的后续分支AdamW与“解耦权重衰减”Adam还有另一个被人诟病的问题它和L2正则化权重衰减结合得不好。这个问题直到AdamW2019年出现才被正式解决。L2正则化本来是在loss上加一个权重平方项用来限制权重不要太大缓解过拟合。在SGD里这等价于每次更新时把权重往零方向推一点点。但在Adam里因为每个参数有自己的自适应学习率那个“推一点点”也被缩放得不均匀了——大梯度参数衰减得猛小梯度参数衰减得弱这就破坏了权重衰减的初衷。AdamW的做法很朴素把权重衰减从梯度里拿出来直接在后面对权重做一步衰减不经过自适应缩放。公式层面就是θ_{t1} θ_t − η · m̂_t / (sqrt(v̂_t) ε) − η · λ · θ_t多出来的最后一项就是解耦后的权重衰减。这个改动听着简单实际效果却很显著。现在你在Hugging Face上用预训练模型做微调默认优化器基本就是AdamW因为它在带权重衰减的场景下真的更稳。从AdamW往后优化器研究进入了一个更细分的阶段有调整学习率调度的、有对不同参数分组设不同超参的、也有重归SGD系做更精细调参的。但核心骨架就这么多——动量、自适应、偏差校正、解耦正则化没有更多了。5. 实操复盘从零调好一个优化器的完整流程与核心参数5.1 第一步固定学习率还是用调度器这决定了80%的结果写代码调优优化器第一件事不是选Adam还是SGD而是决定学习率策略。我见过太多人只盯着优化器类型完全忽略学习率调度结果怎么调都不到位——其实锅在调度器身上。基础策略有这么几档。固定学习率适合小数据集、快速验证场景一般配合Adam使用lr0.001跑几百步看趋势。Step Decay每若干个epoch把学习率乘以0.1适合CV任务配SGD。Cosine Annealing学习率按余弦曲线从初始值衰减到接近零是目前Transformer系任务的常见选择。Warmup Decay前几千步从极小学习率线性升到目标值再走衰减——这是训练大模型的标配。我个人的经验法则是除非你是拿小数据跑着玩否则千万别锁死固定学习率。尤其训练超过10个epoch之后固定学习率的后期几乎必然出现loss锯齿状震荡——模型已经在最小值附近打转了步长还没变小能不震荡吗为什么warmup有用因为训练初期参数是随机初始化的梯度方向极不可靠。这时候你直接给大学习率等于在一个陌生的地方大步流星乱闯很容易被带到奇怪的位置再也回不来。先小步走几步、等梯度方向稳定了再提速才是稳妥的做法。这和开车刚起步不能直接油门踩到底是一个道理。5.2 第二步关键超参数怎么设一张表讲清楚不同优化器的可调参数并不多但每个都有讲究。先给一张速查表后面再逐个解释。优化器核心参数建议默认值调参方向SGDlr0.01~0.1最敏感需配合调度器SGDmomentumlr, momentumlr0.01, momentum0.9momentum 0.9起步0.99有风险Adamlr, β₁, β₂lr0.001, β₁0.9, β₂0.999lr优先调β一般不动AdamWlr, weight_decaylr0.001, wd0.01~0.1weight_decay对泛化影响显著RMSProplr, βlr0.001, β0.9适合RNN注意lr别设太大learning rate是永远的头号超参数。如果你只调一个参数就调它。怎么判断当前学习率是否合适一个实用的方法是“学习率测试”从极小lr开始指数增大跑几百个batch画出loss随lr的变化曲线。你会看到loss先降后升最低点对应的lr就是合理的初始学习率上界。这个技巧我叫它“lr探针”它能帮你把玄学变成有数据支撑的决策。momentum动量系数在SGD系里作用巨大。0.9是经典起点0.99会更强但更容易震荡0.8更稳但收敛慢。我建议从0.9开始如果loss曲线非常锯齿化可以适当提高到0.95~0.99如果曲线出现发散迹象降回0.9以下。Adam的β₁、β₂我强烈建议不要动。不是说不能调而是你几乎永远不需要调。β₁控制动量窗口长度β₂控制自适应步长的“记忆长度”。修改它们的效果非常微妙远不如把钱花在调lr和学习率调度上。有一个例外——如果你发现训练到后期loss在下降但验证集表现变差可以试试把β₂从0.999降到0.99让自适应步长更“警觉”一点但这种操作属于进阶中的进阶了。weight_decay对AdamW非常关键。很多人默认设为0但这等于放弃了L2正则化的保护伞模型更容易过拟合。经验范围是0.01到0.1。具体取多少要看模型规模和训练数据量数据量越大weight_decay可以越小模型越大通常需要越大的weight_decay来压住参数增长。5.3 第三步批大小与优化器的“联动效应”批大小batch size名义上是数据加载的参数实际对优化器行为影响深远。为什么因为梯度噪声水平直接由批大小决定。批越大梯度估计越准确训练越稳定批越小梯度噪声越大越可能跳出局部极小值。这里有一个工程上的两难。大batch 大lr是常见组合因为梯度噪声小了步子可以放心迈大小batch 小lr则适合精细调优。但大batch在大规模分布式训练中吞吐量高所以很多人倾向于能开多大就开多大——结果一个没注意模型收敛时泛化性能反而差了这就是批量大小和泛化之间的张力。实践中我建议batch size尽可能选8、16、32、64这类2的幂充分利用GPU并行不要选100、50这种数。然后batch size一旦选定要同步验证学习率是否需要缩放。一个粗略经验batch size翻倍lr也可以跟着翻倍这叫linear scaling rule。但这个规则只在batch size比较大的时候才近似成立小batch区间不用严格遵守。5.4 第四步梯度裁剪、数值稳定性与loss曲线的“翻译学”再补两个优化器实战绕不开的周边问题。第一个是梯度裁剪gradient clipping。当你发现loss曲线里突然出现一根冲天大刺——前一步还是1.2下一步直接变50——这多半是某几个样本产生了极端梯度。这时候把梯度模长限制在某个阈值内比如1.0或5.0就能把这种极端情况压住。梯度裁剪不是优化器的一部分但它是优化器能稳定工作的“保险丝”。尤其在训练RNN、Transformer或带长尾数据的模型时这个保险丝可以说是必需品。第二个是如何看懂loss曲线。我把常见的曲线形态翻译一下loss平滑下降后平稳一切正常看是否过拟合。loss震荡但整体下降lr偏大或batch偏小可尝试降lr、增batch。loss完全不降lr太小或者模型结构/数据有bug不要怀疑优化器。loss在初期剧烈震荡后发散lr太大调小0.1倍再试。loss先降后升典型过拟合特征要么加正则、要么加数据、要么提前停止。记住一个原则优化器不是背锅侠。当loss完全不降时先检查数据管道、模型实现、梯度是否传导正常最后才去怀疑优化器参数。我排查过太多次“优化器不收敛”的问题最后发现是数据标签错位或者模型输出层忘加激活函数——这些bug根本不是调参能解决的。6. 从SGD到自适应再到新范式优化器演化的内在规律6.1 一条清晰的演进主线从目标函数出发步步逼近现实把整条发展历史串起来看你会看到一条非常清晰的逻辑链批量梯度下降数学上完美算力上不可行。SGD牺牲精度换速度用噪声换跳出局部坑的能力。动量法用历史方向平滑当前方向解决峡谷震荡。自适应学习率用参数自身的梯度尺度来缩放步长解决“一刀切”的步长问题。Adam把动量与自适应统一加了偏差校正达到“默认好用”的舒适区。AdamW修复自适应与权重衰减的冲突让正则化真正发挥作用。新范式从“人工设计”走向“自动搜索”和“理论再审视”。这条线的内在驱动力只有一个让下山这个动作更快、更稳、更少手动干预。每一步都是因为前一步在真实地形中暴露了新的问题。6.2 被逼出来的新方向Lookahead、SWA与自动学习率近几年优化器研究出现了几个新方向每一个也都是“被逼”出来的。Lookahead2020年的思路很有意思它维护两组权重一组是慢权重一组是快权重。快权重正常用任意优化器比如Adam更新慢权重每隔K步向快权重靠拢一次。用我的话理解快权重负责“探索”慢权重负责“收编”两者结合让训练在崎岖地形上更稳。我实测在部分GAN训练任务中Lookahead确实能显著抑制loss震荡。SWA随机权重平均Stochastic Weight Averaging则是另一种思路既然最优解附近有一片“平坦区域”那就让模型在optimal点附近多逛几圈把逛到的权重做个平均。平均之后的模型往往比单一终点的模型泛化更好。这个技术在CV领域有很强的实证支撑而且实现成本极低——只是多存几份权重做个指数平均。还有一个趋势是“自动调学习率”。比如AdaBound、RAdam这类方法试图在训练过程中自适应地调整优化器的行为。RAdam特别有意思它是针对Adam在训练初期因为样本量少导致v_t估计不可靠的问题用整流项修正Adam的更新量。它在小batch、少数据场景下确实能比Adam更稳。这些新方法目前还没有动摇AdamW的“默认地位”但它们代表了一个明确信号优化器研究已经从“人手调参”走向“让算法自己适应地形”。我个人判断未来两三年内会出现一个更智能的默认优化器它可能会结合自适应步长、自动学习率调度、自动权重衰减调整这几个能力。6.3 我给新人的建议从默认配置出发但别停留在默认配置最后说点掏心窝的经验。如果你刚开始学不要一上来就陷进优化器的超参泥潭。我的推荐路径是三步走。第一步全部用默认配置跑通。模型用默认AdamWlr用0.001weight_decay用0.01batch size取32跑一个最小实验。目标是确认数据管道、模型结构、loss计算全部正确。这一步最重要因为你排查的大部分“优化器问题”其实都出在更上游的代码里。第二步做一次学习率探针和基础调度实验。花半天时间画出lr-loss曲线找出合理lr范围配上cosine schedule或step decay看loss曲线形态是否有明显改善。这一步能帮你建立对模型训练动态的直觉。第三步针对任务特性微调。如果你是做CV试试SGDmomentum配lr0.01和cosine schedule如果你是做NLP坚定用AdamW重点调weight_decay和warmup步数如果你训练不稳定、梯度爆炸别忘了梯度裁剪。我在实际项目中反复体会到一个朴素但重要的道理优化器不是越先进越好而是越适合地形越好。同一个模型在不同数据分布、不同batch size下最优优化器可能完全不同。保持开放心态每种都试试记录实验日志最后用数据说话——这才是处理优化器问题的正确姿势。