☰
模型优化器全解析:从SGD到AdamW的选型、原理与训练排坑
2026/9/29 1:53:07 网站建设 项目流程

写这篇的时候,我刚从一次离谱的调试经历里爬出来。一个在测试集上已经收敛得很漂亮的模型,只是为了换个优化器看一眼曲线,损失直接冲到NaN,整个训练日志红成一片。很多人觉得优化器就是那个model.compile()或optimizer = torch.optim.Adam(...)里顺手填的参数,但真正在工业界跑模型的人都知道,它才是那个决定了训练能不能成、收敛快不快、泛化强不强、显存爆不爆的隐形操盘手。这次想认真聊聊模型优化器(Model-Optimizer)这件事——不是给你抄一段API文档,而是把优化器从原理到选型、再到那些踩得头破血流的坑,完整梳理一遍。

1. 优化器到底在求解什么:三个看不见的决策层

每次训练迭代,优化器做的事情远不止“更新一下参数”。把它拆开看,任何一个优化算法都要回答三个问题:往哪个方向走、每次走多远、以及这个方向对全局到底可不可信。这三个决策层,直接决定了模型最终落在损失曲面的什么位置。

方向问题是梯度的直接投影。我们算出来的梯度是损失函数上升最快的方向,优化器要做的是取反方向拿去做参数更新。但这个“梯度”本身有噪声:小批量采样带来的随机性,参数空间局部的弯曲,特征尺度不一致导致的梯度分量失衡,都会让原始梯度不可直接信任。因此在走向上,优化器会做平滑、做归一化、甚至做二阶修正。

步长问题是学习率的具体编排。固定学习率几乎从来不是最优解,因为训练初期参数离最优点远,需要大步探索;后期接近谷底,步长过大就会震荡永远压不到低点。优化器内部会维护一套自适应机制,相当于给每个参数单独配一个“油门”。比如某个参数的历史梯度大,说明这个方向上下降剧烈,需要收油门防止超调;某个参数历史梯度小,说明它可能还远没到位,可以继续给油。

可信度问题是最容易被忽视的。动量(Momentum)的本质,是用历史梯度的指数平均来判断当前梯度是否值得相信。如果当前梯度的方向和历史趋势一致,说明没有跑偏,可以加大步长接着冲;如果猛地反方向来一下,大概率是一个噪声样本,这时候要把这次更新幅度压下来。

把这三个层面串起来看,SGD 只解决方向问题,动量方法开始解决可信度问题,自适应方法把步长问题也交给了算法。优化器每一次迭代的算力消耗、显存占用、收敛速度、最终精度,全部取决于它在这三个决策上做了多少额外的统计和维护。理解了这层,后面所有的选型和排错都有了解释的锚点。

我见过不少同学在同等结构下复现不出论文效果,把锅甩给数据增强或初始化,最后查下来只是优化器换了一个,超参数没跟着适配。优化器不是一个可以零成本替换的零件,它是一套带着自身偏好的求解策略。

2. 从 SGD 到 Sophia:常见优化器的原理拆解与它们各自的性格

玩过几个模型之后,你会感觉到每个优化器就像不同性格的向导带你下山。有的保守稳健,每步都踩实;有的激进聪明,会沿着惯性带小跑;有的过度敏感,遇到点噪声就想改方向。下面拆开看看这张优化器谱系上每一号选手。

2.1 SGD 与动量:简单山路上最可靠的选择

朴素 SGD 的更新就是params -= lr * grad,它相信每一个 batch 算出来的梯度方向,只有学习率一个超参数需要调。这个极端简单的算法在 CV 任务里的表现被验证了无数次:在 ImageNet 上训练 ResNet,SGD + Momentum 配上 cosine 退火和适当的 weight decay,通常能拿到比 Adam 更低的过拟合倾向和更好的测试精度。

动量版本的公式是v = beta * v + grad,params -= lr * v。这里 beta 通常取 0.9,相当于维护了一个历史梯度的指数滑动平均。你可以把动量想象成下山的小球:它不仅接受当前位置的力,还带着之前积累的速度。这样在平坦区域不会被“卡住”或停滞,在峡谷地带也能更平滑地穿过那些宽窄不一的梯度沟壑。

实战中我偏向在以下场景用 SGD + Momentum:图像分类、目标检测的主干网络预训练、以及任何不需要特别快收敛但很看重泛化性能的传统 CNN 任务。它的缺点也很明显——对学习率的设定太敏感,不同任务的合适学习率可以从 0.1 到 0.001 差两个数量级,需要认真去试。

2.2 Adagrad 与 RMSProp:给每个参数独立的步长

Adagrad 提出了一个重要的思路——每个参数应该使用自己的学习率。它维护了每个参数历史梯度的平方和,然后拿这个累加和的平方根去除学习率。梯度大的参数学习率自动变小,梯度小的参数学习率保持较大,这解决了一些稀疏特征问题(比如推荐系统里某些特征只出现过几次)。但 Adagrad 有一个致命伤:梯度平方累加只增不减,到训练后期分母巨大,学习率被压到近乎为零,基本学不动了。

RMSProp 修复了这个毛病:不再累计全部历史梯度平方,而是用指数滑动平均来估计当前梯度的二阶矩,让自适应能力始终保持在合理尺度。RMSProp 在 RNN 这类长期依赖的任务上表现很好,也是后来 Adam 实现的核心构件。

我个人理解这两个算法的关键价值,不是它们本身还剩多少真实使用率,而是“每个参数独立学习率”这个思想根系,整个自适应优化器家族都从这儿长出来的。

2.3 Adam 与 AdamW:当前事实标准及其唯一软肋

Adam 把动量(一阶矩)和 RMSProp 的自适应学习率(二阶矩)结合起来,同时修正了训练初期矩估计偏差过大的问题。它的更新公式大致可以理解成:m = beta1 * m + (1-beta1) * grad,v = beta2 * v + (1-beta2) * grad^2,然后参数更新的方向是m / sqrt(v+eps),其中 eps 是一个防止除零的小常数,默认常在 1e-8 左右。

Adam 真正的杀手锏是“梯度过大时压缩步长、梯度过小时放大步长”,相当于对梯度做了整体规范化。这让它在 NLP、Transformer、扩散模型等领域成为绝对主角——这些任务里梯度的尺度异质性极强,SGD 根本招架不住。

但 Adam 有一个被长期忽略的副作用:它把权重衰减(weight decay)和基于梯度的自适应更新搅在了一起。传统 L2 正则项作用在梯度上,会再被1/sqrt(v)缩放一遍,导致大梯度参数的权重衰减被削弱、小梯度参数的权重衰减被放大。AdamW 的解法是把权重衰减从梯度计算中解耦出来,直接在参数更新时减去decay * lr * param,不参与自适应的缩放。这个改动看似微小,实践里的收益很实在——尤其是在 Transformer 类模型上,同样的权重衰减预算,AdamW 的泛化表现稳定优于 Adam。所以现代训练框架里,PyTorch 默认推荐的都是torch.optim.AdamW。

2.4 新一代优化器:Lion、Sophia 与自适应剪枝

近几年出现了几款挑战 Adam 地位的选手。Lion 的做法很粗暴也很有趣:它只在动量的符号方向上进行更新,即更新时只看梯度方向的正负,不看大小,等价于把参数更新变成了一次“选边站”。这样省掉了计算平方和、开方等操作,理论上几乎只保留了 Adam 的动量逻辑,速度和显存都更优。但要注意,符号函数让更新量完全不随梯度幅值变化,等价于引入了一种特殊的噪声正则,在不少小规模任务里反而带来更好的泛化。实战中我通常在预训练大模型的小规模试点上试用 Lion,效果比较看任务,时好时坏,得自己跑。

Sophia 引入了二阶信息(对角线 Hessian),通过估计损失曲率来调整步长,目标是解决 Adam 这类一阶方法在大模型预训练时后期步长过低、收敛速度变慢的问题。它维护的 Hessian 对角估计额外消耗一点显存,但收敛步数实质减少。对训练成本极高的 LLM 场景,像是用更贵的体力劳动换取更短的工作天数,是否划算需权衡自己的算力预算。

实践中我整理过一个选型表,方便不同任务快速定位方向。

场景优化器配置倾向备注
CNN/图像分类SGD+Momentumlr 0.1/0.01 + momentum 0.9配合 weight decay 泛化更好
目标检测/分割AdamWlr 1e-4 ~ 3e-4ViT 和 CNN 通用
Transformer 训练AdamWlr 1e-4 ~ 3e-4warmup + cosine 必须
大模型预训练AdamW / Sophialr 3e-4 ~ 1e-3注意梯度裁剪与 loss spike
LLM 微调/LoRAAdamWlr 1e-5 ~ 5e-5学习率比预训练低一到两个数量级
推荐系统/稀疏特征Adagrad/Adam按特征稀疏程度二值特征多时可考虑
强化学习 policyAdambeta2 调大成 0.95 以上梯度噪声大,需要稳定的二阶矩估计
GAN 双网络Adam两个网络可分开调 lrS 和 G 的更新频率调制很关键

表格只是起点,真正落地还需要看任务量级和数据分布,后面的章节会展开怎么把这些配置调得可靠又省心。

3. 面对实际任务时的选型链路与超参数触感

选优化器不是从字典里抓阄,而是把任务的梯度信息结构、收敛瓶颈和训练成本串起来看。这里分享一下我自己在不同场景下反复验证过的选择策略。

3.1 梯度结构:稀疏性和尺度异质性决定基础分类

看你的数据特征结构。推荐系统里 embedding 层有海量稀疏二值特征,每个特征的出现频率差异极大,这时候全参数共享一个学习率会严重拖慢低频特征的学习。Adam 或者 Adagrad 都能让低频特征拥有更大的有效学习率,收敛会快得多。

反过来,如果特征是稠密且尺度接近的数据,比如标准化后的图像像素,SGD 的保守反而成了优点。因为梯度尺度没有极端差异,每个参数用同一个学习率并不会带来什么问题,同时 SGD 没有额外的动量缓存和二阶矩缓存,显存占用更小,在批量较大的 CNN 训练里更省资源。

判断方法也很简单:跑一个完整 epoch,把每层梯度范数打印出来,看同一时刻不同 tensor 的梯度量级是否跨越很多个数量级。跨度大,用 Adam 家族;跨度小,用 SGD 也可以稳定收敛。

3.2 损失曲面与收敛瓶颈:绕不开的两个评估维度

损失曲面的“形状”会影响优化器收敛时的行为。如果损失是高度非凸、有很多窄谷和陡峭壁面,比如 GAN 的对抗损失、强化学习的奖励噪声环境,Adam 的自适应步长能帮你从震荡中活下来;但你可能马上遇到另一个问题——后期接近纳什均衡点时,梯度的二阶矩估计异常,导致步长忽大忽小,训练不稳定,这种时候建议把 beta2 从默认的 0.999 调成 0.95,因为新样本的梯度变化应该更快反映到步长决策上。

收敛瓶颈的维度则更实际。如果你观察 loss 曲线,发现它在某个值附近震荡了很久但就是不下降,可以先检查是不是学习率太低。TensorFlow 时代常见的是 1e-3 固定跑完整个训练;现代通用做法是 warmup 到目标学习率后接 cosine 退火,让训练后期学习率自然降到非常低,配合它在最优点附近精细收敛。

但过低的最终学习率加上过大的 weight decay,会让 Loss 在后期出现突然升高然后挂掉的模式,这种组合也很常见。所以选型的时候不要只看优化器本身,还要和调度器、正则化一起考虑。

3.3 调参流程:从学习率起步的“三步定位法”

我自己的调参流程基本固定成三步。第一步,选一个稳定的优化器和初始化,比如 AdamW + lr=1e-4,配一个比较小的 weight decay(比如 1e-2),只跑 10%~20% 的训练量看 loss 是否稳定地降。如果这都不能收敛,后面什么都别谈。第二步,做学习率扫描。把 lr 从 1e-5 到 1e-2 按对数间隔排 8~10 个点,每个点跑几百步,记录损失最小值对应的学习率区间,再结合曲线陡降程度锁定 2~3 个候选。第三步,锁定候选后在完整训练脚本里分别跑一次,对照收敛步数、峰值精度和训练后期稳定性,取综合最优的那组。

这套流程看起来很笨,但比凭经验直接给定超参可靠得多。我做强化学习项目时甚至会在 offline replay buffer 上先跑一遍同样的扫描,因为在线训练的方差太大,扫描结果完全不可信。

3.4 参数服务器视角:不同优化策略的实际开销对比

优化器不只是逻辑层面的选择,也是硬件资源层面的谈判。Adam 类方法需要额外维护一阶动量(参数大小相同)和二阶动量(参数大小相同),也就是说模型本身大小为 S,Adam 的优化器状态会吃掉 2S 的显存。SGD+Momentum 只需要一个动量缓存,多占 S。Sophia 需要维护 Hessian 对角估计,通常也是 S 到 2S。在 7B 参数的模型上,这几个选择间就差出去好几张 A100 的容量。

显存不够时的替换思路通常是:先换掉优化器类型,再考虑梯度累积、混合精度和断章取义的 offload。显存够但算力吃紧时,可以降低二阶矩更新频率(比如每 k 步更新一次 v,k=16),这是一种很糙但有效的近似,效果和显存收益比也很划算。

4. 调度器、权重衰减与梯度裁剪:优化器真正的黄金搭档

很多人把优化器训练简单理解成“把模型扔给 Adam 就行”,完全忽略了它身边三个形影不离的伙伴。优化器的行为上限,很大程度由这三个搭档决定。

4.1 Warmup 存在的数学直觉:为什么要避免开局大步子

训练一开始,模型参数随机初始化,梯度的方向信息非常不可靠。如果一开始就用最终学习率,相当于在一座完全陌生的山上闭着眼睛大步跑,容易被带到奇怪的局部洼地。更关键的是,Adam 这类优化器在前期矩估计还在“热身”,它对自己算出来的步长也缺乏信任。

Warmup 的思想很简单:前若干个 step 里,把学习率从接近 0 线性或非线性升到目标值,让模型先走出很短但方向正确的小碎步,等运动趋势稳定后再逐渐放开步子。经验上,Transformer 类模型如果去掉 warmup,训练早期 loss 大概率会直接飙升甚至 NaN;CNN 对 warmup 的需求稍弱,但大 batch 训练时依然强烈推荐。

PyTorch 里可以用torch.optim.lr_scheduler.LambdaLR自定义 warmup 曲线,也可以通过T5LinearWarmup这类现成封装,原理都一样:计算当前 step 占比,把初始 lr 替换为base_lr * min(1, current_step/warmup_steps)。

4.2 退火策略的选择与训练阶段的匹配

训练后期,学习率需要收缩,让参数在最低点附近精细打磨。最常用的两种是 StepLR(每隔固定步数降一格)和 CosineAnnealing(按余弦曲线平滑降到接近 0)。我的经验是:

  • 小数据集 + 训练轮数少(比如 20 轮内):Cosine 退火可以让你一直维持高学习率到后半程,用后段的平滑降低避免震荡。你不用刻意保存最后一个 epoch 的权重,反而应该取训练过程中验证集最佳的那一版。因为最后几步 lr 接近零,实际上是在做一次局部精修,不代表最终泛化最强。
  • 大数据集 + 长期训练:用 warmup + cosine 从头到尾很常见;但如果训练长度足够长,也可以在中间保留一段平台期再开始退火。
  • 迁移学习和微调:如果基础模型已经收敛,通常不需要长 warmup,几百步的短暂预热就够。有人为了“保险”,从 0 开始做 10% 长度的 warmup,白白浪费了训练预算。

4.3 权重衰减与优化器之间的耦合解耦实验

AdamW 里“解耦”的权重衰减,与普通 Adam + L2 正则的区别,前面原理部分提过。这里给一个实验感觉:在 BERT-base 规模的模型上做微调,同样的 weight decay=0.01,AdamW 的微调稳定性和下游任务得分通常优于 Adam + L2 一两个点。这个差距在数据量少、训练轮数短的任务上更明显。

解耦之后,weight decay 变成一副纯粹的“参数收缩剂”,不再被二阶矩缩放歪曲。这也让它在视觉 Transformer 上可以调得更大,比如 ViT 的常见配置 weight decay 可以到 0.1,甚至会带来额外的正则收益。但这个量级直接放到 ResNet 的 SGD 上大概率会爆,因为规则完全不同。所以当你从一篇论文里抄优化器时,至少要把它配套的 weight decay、lr、warmup 一起抄来,只抄名字是最容易翻车的。

4.4 梯度裁剪的三层防护效果

梯度裁剪常被误认为只是“把梯度的模长卡在某个阈值内”,其实它有三个不同维度:按全局范数裁剪(clamp global norm)、按元素逐值裁剪(clip value)、以及按层裁剪。它们的适用场景不同。

  • 全局范数裁剪适合大多数自动求导框架:torch 的torch.nn.utils.clip_grad_norm_会先算整个参数组的梯度总范数,超过阈值就按比例缩放。对 Transformer 和大模型的 loss spike 非常有效。
  • 逐值裁剪适合某些量化训练或梯度本身有固定范围预期的任务,但会破坏梯度相对比例,对复杂任务可能影响精度。
  • 按层裁剪适合处理特定的爆炸层,比如 embedding 层在过长序列中出现极大梯度。

在优化器层面,梯度裁剪的作用是防止单步更新过大。这一步的保护在 loss 突然出现 spike 时尤其关键:如果曲线冲上去之后不再回来,很可能是那个 spike 破坏了模型内部结构,连 Adam 的自适应都没办法短时间修复。设一个裁剪阈值(常见为 1.0)是低成本高收益的保险。

5. 显存与计算瓶颈:大模型训练下的优化器生存策略

模型规模上去后,优化器第一个暴露的问题就是显存。GPT-3 级别的模型,光 Adam 的优化器状态就是模型参数显存的两倍。很多实验室所谓的“显存不够”,不是模型放不下,而是优化器状态放不下。

5.1 Adam 状态到底耗了多少显存:算一笔简单账

以 7B 参数模型、全参数训练为例,假设模型权重用 fp16 或 bf16 存储,每个参数字节数为 2。权重本身占 14GB。Adam 需要同时维护 fp32 的主权重(因为更新累积需要更高的数值精度,通常保留 fp32 的 master weight)、一阶动量 m(fp32),二阶动量 v(fp32),一共是三个 fp32 的张量,每个 28GB,合计 84GB。再叠加梯度(fp16 或 fp32)、中间激活值,训练一个 7B 模型单卡基本别想了。

这也是为什么 LoRA 这类参数高效微调在消费级显存上流行:冻结原模型权重,只训练几个低秩矩阵,优化器状态也就只针对这些低秩矩阵而言,显存占用大幅下降。如果你手头只有一张 24GB 的卡,直接全参数微调 7B 是根本不现实的,哪怕你用 AdamW 也一样。

5.2 8-bit 优化器与低精度二阶矩:显存减半的实用派

bitsandbytes 这类库把优化器里的主权重、一阶、二阶动量量化到 8-bit 存储,反向传播时再反量化回高精度做更新。这样 Adam 的优化器状态从 2S 显存降到差不多 0.5S,整体训练占用大幅缩小。实践上,8-bit Adam 在大多数任务上能匹配全精度 Adam 的表现,尤其是语言模型微调阶段。

但要注意,8-bit 优化器不是零代价:极端情况下的训练稳定性可能变差,如果任务本身对数值很敏感,建议先用小规模实验验证。同时它还会引入一点点额外 CPU/GPU 量化反量化的计算消耗,好在通常并行度够高,速度损失可以接受。

低精度二阶矩的另一个技巧是把 v 的更新频率降低,比如每 16 步更新一次,中间步骤复用旧的 v。这在扩散模型和 Transformer 的训练里都被实践过,收敛质量损失很小,但显存和算力都省下一块。算力吃紧时这个 trick 很值得先试。

5.3 ZeRO 与 FSDP 如何切分优化器状态

分布式训练时代的优化器问题变成了“如何把数百 GB 的优化器状态装进一群卡里”。ZeRO 的核心思想:既然每张卡上的每个参数都会计算梯度,但优化器做参数更新时有完整的全局梯度是更好的,那么就可以把模型的各层按 rank 切分,每张卡只维护自己负责的那部分参数的优化器状态,更新时再用 all-gather 把参数同步回去。

FSDP 是 PyTorch 对 ZeRO 思路的原生实现。开启 FSDP 后,Adam 的动量、二阶动量并不需要在每个 rank 上各存一份完整副本,而是按分片存储,训练显存上限直接变成模型显存 / 卡数 + 优化器状态 / 卡数的量级。这在百卡千卡集群上是刚需,哪怕小规模多卡训练,FSDP 配合 CPU offload 也能显著降低每卡显存压力。

5.4 Offload 到 CPU:把显存压力转移给内存带宽

优化器状态还可以放到 CPU 内存中,反向传播时把梯度搬到 CPU,更新后再把新参数搬回 GPU。CPU offload 的代价是 PCIe 带宽成为瓶颈,训练速度可能显著变慢,因此它一般只在显存捉襟见肘后作为最后的保底手段。如果你使用 FSDP,可以在ShardingStrategy.SHARD_GRAD_OP的基础上开启 CPU offload;如果是 Deepspeed,则对应 Zero-Offload 配置。个人的经验教训是:offload 的 batch size 可以适当往下调,因为 CPU 更新频率与 GPU 计算严重不均时,反而会拖慢整体的吞吐。

6. 训练不收敛的排查链路:从优化器视角定位根因

这段内容很直接、很实战。训练不收敛或 loss 爆炸,很多人的第一反应是“模型结构有问题”,但根据我这些年排障的记录,优化器相关的原因占了一半以上。下面把从现象到定位的链路完整走一遍。

6.1 现象一:Loss 变成 NaN 或 inf

排查顺序有明确的优先级。

先后确认数值稳定性:是否用了混合精度但 loss scaling 没有开启或阈值不对。fp16 下梯度下溢成 0 是常见原因;bf16 则相对更稳,可以优先换用。如果混合精度正常,再看学习率是否过大。一个 7B 模型若在 warmup 阶段就冲到 3e-4,很可能第一个大 batch 的异常梯度就毁掉所有参数。这种迹象可以从日志中是否出现过一次突然巨大的 loss spike 来判断。

然后看优化器内部数值:Adam 的 eps 是否太小。早期默认的 eps=1e-8 在某些框架中被改成传入的极小值,如果 v 接近 0 会导致m/sqrt(v + eps)出现巨大数值,形成隐形的梯度爆炸。我在自己项目中把 eps 固定为 1e-6 或 1e-7 后,很多顽固的 NaN 就消失了。

权重衰减是否过大也常被忽视。深度学习里一个常见蠢操作是 weight decay 设到 0.1 却在一个小模型上训练几百步,参数被快速压向 0,Loss 一路走高最终爆掉。排障顺序建议:梯度裁剪 -> 学习率 -> eps -> weight decay -> 混合精度。

6.2 现象二:Loss 正常下降,但验证集不涨

这个现象很多情况下不是优化器的问题,而是训练时间和学习率调度的匹配问题。验证集不涨,往往说明训练还没到达可以泛化的区域。拿 ResNet 训练 CIFAR 举例,如果只用 SGD 默认 lr=0.01 跑 50 个 epoch,验证精度一般远不如 lr=0.1 配 cosine 退火的版本。泛化的好成绩通常在退火后段出现,不要在半程就下结论。

如果学习率已经退到很低验证集仍然不涨,再去查数据分布不匹配、增强过强、标签噪声等问题。从优化器视角看,这种情况更常出现在权重衰减设得太大,导致模型容量被过度压缩。

6.3 现象三:不同框架间复现结果差异

同一个模型同一个优化器,从一个框架迁移到另一个框架,结果对不上,第一反应不应该是数据加载,而是查优化器默认参数。TensorFlow 的 Adam 默认 eps=1e-8 且没有修正偏置的开关配置,PyTorch 的 Adam 默认 eps=1e-8 但会有偏置修正,两者即使学习率相同,实际更新步长的尺度也有细微差别。更经典的问题是 weight decay 和 L2 正则的实现差异,TensorFlow 的 L2 是加到梯度上的,PyTorch 的 weight_decay 在 SGD 里也是按 L2 方式实现,但在 AdamW 里是解耦的,直接拿同一个 weight decay 值去用,效果自然不同。

所以我的习惯是,每个实验的配置里把优化器参数完整地写进一份config.yaml,训练日志里也定期打印优化器的当前超参快照。复现失败时先 diff 这个,再 diff 模型结构和数据处理。

6.4 搭建一套可自动告警的“优化器健康监测”

除了事后排查,更推荐把优化器状态纳入训练监控体系。每个 step 记录:当前 loss、学习率、全局梯度范数、参数更新范数、Adam 二阶矩均值。通过把梯度范数和更新范数做对比,可以直观判断优化器的行为是否正常。梯度过大但更新过小,说明二阶矩很大,优化器在压步子;两者都过大,就要防止爆炸了。

我在项目里还会记录阶梯数对应的二阶矩最大值,如果它出现指数级增长,通常预示着某个层在退化。把这些指标接入 TensorBoard 或 W&B,训练异常不用等到最后看曲线,每一步的风险都能提前巡到。这个方法在大模型训练里尤其值钱,因为一次 NaN 就是损失几十卡时的算力。

7. 落到你自己的项目:从零配好一套优化器方案的注意清单

前六章把原理、选型、搭配和排障都铺开了,最后这部分,我按自己的实际项目流程给一份可以直接照做的落地清单。

第一件事是明确训练目标。你是想快速实验还是追求最终精度?快速实验选 AdamW + 中等学习率即可,省去大调参时间;追求精度,SGD+Momentum 配合合适的退火通常更可靠,但你需要更多时间调参,也要有耐心的心理准备。没有人能一上来就知道哪个优化器最好,落地第一版永远是简化版,先保证流程跑通。

第二件事是设置优化器状态。建议把所有超参都写进配置而非代码。优化器类型、学习率、betas、eps、weight_decay、warmup_steps、lr_scheduler 类型、梯度裁剪阈值,缺一不可。训练日志里也加入这一份配置,防止换设备或换同事后出现“神秘复现差异”。

第三件事是按任务卡住关键参数的大致范围。Transformer 类任务 lr 从 1e-5 到 5e-5;CNN 分类从 0.01 到 0.1;CLIP 类对比学习任务则可以用更大学习率如 1e-3,配合大 batch size。不用自己凭空创造,参考同任务规模相近的工作即可。

第四件事是留一个早停策略。监控验证集指标,如果在 N 个 epoch 内没变好就趁早调优或停止。训练资源花在探索多个配置上,远比死磕一个注定不涨的模型有意义。

第五件事是把日志沉淀成自己的“优化器经验表”。每次实验跑完,记录下配置、峰值指标、收敛步数、是否有 loss spike。时间久了,你会形成一种直觉:这个任务该用什么优化器、多少个 warmup step、weight decay 的合理区间,甚至能从曲线形状判断出是不是 beta2 需要调大。这个东西,比任何公开的 baseline 都值钱。

我在实际项目中体会最深的一点是:优化器不是一个“选完就完事”的组件,它是你训练全流程的调音台。同一个任务,换掉一个超参可能让结果从“跑不起来”变成“收敛得又快又稳”。所以下次训练出问题时,别急着怪模型结构,先看看你的优化器和它的黄金搭档们是不是真的在协同工作。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询