ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

优化器怎么选?从SGD到Lion的选型与调参实战指南

优化器怎么选?从SGD到Lion的选型与调参实战指南 想聊一个我在优化器上摸爬滚打多年的真实感受顺便把这几年积攒的一些实操经验做个系统梳理。别急着划走虽然标题里带个问号但这篇不是要跟你辩论“哪个优化器天下第一”而是要解决一个更实际的问题当你在训练模型时面对SGD、Adam、AdamW、LAMB、Lion这一堆选项到底该怎么选、怎么调、怎么避坑。这篇适合所有正在被loss曲线折磨的炼丹师无论你是刚入门的新手还是已经在调参路上秃了一半的进阶玩家都应该能从里面找到点有用的东西。1. 以“大而美”之名聊聊优化器这摊子事先解释下标题。“A Big Beautiful Optimizer”这个说法其实是圈子里对一类优化器的调侃指的是那些设计上看起来格局宏大、理论上写得天花乱坠、论文里效果图又特别好看的优化器。但等你真的把它们搬到自己项目里往往会发现一个尴尬的事实效果并没有论文里那么神有时候甚至不如老老实实用一个SGD加Momentum。为什么会出现这种情况说白了优化器这个东西本质上就是“用什么样的规则去更新模型参数”。这个规则设计得好不好直接决定了你的模型能不能收敛、收敛得多快、最后收敛到的点是不是够好。但问题在于优化器领域的“好”和“美”往往是两回事。有些优化器理论推导非常漂亮各种不等式证明一环扣一环GPU显存占用却高得离谱或者对学习率极其敏感稍微调不好就loss爆炸。而有些优化器看起来平平无奇比如SGD理论性质甚至被某些论文批得一文不值但在很多实际任务上就是能work得非常好。我自己第一次被“大而美”的优化器坑到是在一个Transformer的文本分类任务上。当时听说某个新优化器在多个benchmark上屠榜论文里的收敛曲线漂亮得像是P过的于是二话不说换上结果训了十几个epochF1分数死活上不去。后来换回AdamW按默认参数一跑指标直接涨了三个点。从那以后我就养成一个习惯任何新优化器进入我的视野先不急着追捧先搞清楚它到底解决了什么问题又带来了什么新问题。这一篇我打算从优化器的演进脉络讲起拆解几个代表性工作的核心机制再结合我实际项目里的一些数据和踩坑记录给你一份可以直接抄作业的选型参考。我会尽量把每个关键选择背后的“为什么”讲清楚——为什么Adam要加偏置校正为什么AdamW要把权重衰减从梯度里解耦出来为什么LAMB能在大batch下稳定训练为什么Lion对学习率这么敏感。只有把这些机制层面的东西吃透了你才不会被各种“大而美”的包装忽悠住。2. 从SGD到Adam优化器家族演进背后的设计逻辑2.1 SGD和Momentum最朴素的往往最耐打先回到最初的原点。随机梯度下降SGD的思想其实非常简单每次从训练集里随机抽一小批样本计算梯度然后沿着梯度的反方向更新参数。它朴素到几乎不需要什么证明但就是这样一个简单的算法却是很多大规模模型训练的基石。但SGD有一个很实际的问题是收敛慢尤其在碰到损失函数呈狭长山谷形状的时候梯度会在陡峭方向上不停震荡而在平缓方向上前进得极其缓慢。为了缓解这个问题研究者引入了动量Momentum的概念。你可以把动量想象成给参数更新加了一个“惯性”它不仅仅依赖当前步的梯度还会累积之前所有梯度方向上的历史信息。物理图景就是一个球从山坡滚下来速度越来越大遇到小的坑洼也不容易停下来能够更顺利地冲过局部极小值所在区域的平坦带。动量项的更新公式很简单但它的效果非常显著。我记得在CV里做图像分类用ResNet训练ImageNetSGD加动量momentum0.9配上合理的学习率衰减策略效果一直都稳得一批。很多经典论文比如ResNet原文、EfficientNet原文用的都是SGD加动量而不是后来花里胡哨的自适应学习率方法。这说明什么问题说明在卷积神经网络这种相对平滑的损失表面上SGD这种朴素方法加上成熟的训练技巧就已经足够了。2.2 AdaGrad和RMSProp自适应学习率的启蒙SGD家族最大的弱点在于所有参数共享同一个学习率。但实际情况是模型里不同参数的重要性不同它们对应的梯度尺度也差异巨大。词嵌入矩阵的梯度可能很稀疏而某些全连接层的梯度可能很稠密且数值较大。如果用同一个学习率去更新所有参数就必然面临顾此失彼的困境。AdaGrad是第一个在思路上做出重大突破的方法。它的做法是为每个参数维护一个历史的梯度平方累积量然后用这个累积量去缩放学习率。哪个参数的梯度大它的有效学习率就自动变小哪个参数的梯度小学习率就相对大。这个思路听着很完美但AdaGrad有一个致命缺陷梯度平方的累积量是只增不减的导致学习率会单调衰减到几乎为零训练到后期基本学不动了。RMSProp的改进很直接把“累积所有历史梯度平方”改成“用指数移动平均去估计梯度平方的期望”。这样历史梯度不会无限累积学习率就有了上下波动的空间。虽然RMSProp当年在学术圈没有太大排面但它解决了AdaGrad学习率急剧衰减的问题为后面Adam的诞生铺平了道路。2.3 Adam及其偏置校正为什么每个细节都有讲究Adam本质上就是Momentum和RMSProp的结合体一阶矩估计动量负责累积方向信息二阶矩估计梯度平方的指数移动平均负责调整每个参数的更新步长。这个组合让Adam在大多数任务上都能开箱即用对学习率不那么敏感收敛速度也远超SGD。但Adam刚提出时有一个容易被忽略的细节在训练初期由于一阶矩和二阶矩都是从零开始初始化的它们的估计值会严重偏向于零。如果不做校正训练早期的更新步长会被严重扭曲。所以Adam引入了偏差校正bias correction这相当于在天平还没稳定时先人为地修正一下读数让估计值在训练初期也能保持无偏。这个细节值得单独拎出来说因为很多简化的Adam实现会漏掉这一步尤其是当你手写优化器或者用某些自制框架时漏掉偏置校正在小batch、长训练场景下会带来明显的性能退化。另外一个常被忽略的点是Adam的epsilon参数。默认值通常是1e-8它的作用是在分母上加一个小常数防止除零同时也能防止某些梯度极小的参数在学习后期被放得过大。但我实际测试过在FP16混合精度训练中默认的1e-8经常会因为精度不足导致训练不稳定把epsilon调大到1e-6甚至1e-7训练稳定性会有肉眼可见的提升。2.4 AdamW与解耦权重衰减一次“修bug”级别的改进Adam在很长一段时间里都是默认选择但后来人们发现它在很多任务上泛化能力不如SGD。这个现象引发了一系列研究其中最有影响力的是Ilya Loshchilov和Frank Hutter在ICLR 2019发表的论文。他们指出Adam里实现权重衰减的方式存在根本性问题L2正则化会把权重衰减项混入梯度中再被自适应学习率缩放导致不同参数的衰减效果不一致。这个说法的直觉解释是Adam的核心是让每个参数拥有自己的有效学习率而L2正则化的梯度项经过缩放后不再等价于“均匀地施加权重衰减”。所以AdamW做的事很简单——把权重衰减从梯度的自适应过程中分离出来直接在参数更新之后单独执行一次衰减。就是这样一个“修bug”级别的改动让AdamW在多个任务上的泛化性能大幅提升尤其在Transformer类模型上几乎成了标配。我自己在BERT微调、GPT类模型预训练中使用AdamW的经验是如果不做warmup权重衰减大小对结果的影响其实不大但一旦训练步数较长、学习率调度策略复杂AdamW的稳定性优势就会体现得非常明显。如果你的项目还在用AdamL2正则并且泛化效果不太理想建议先把权重衰减方式改成AdamW试试这可能是在所有优化器切换里成本最低、收益最明显的一个改动。3. “大而美”的典型代表LAMB、Lion以及背后的取舍3.1 LAMB给超大batch训练准备的“正骨专家”如果说Adam是手持精密螺丝刀的手术医生那LAMB更像是给骨骼错位的病人正骨的推拿师。它解决的核心问题是当batch size从几百增大到几万甚至几十万时Adam的自适应学习率机制会导致更新步长在不同层之间产生巨大差异。embedding层和大输出头的梯度方差极大而中间层的梯度相对较小用同一个全局学习率去约束所有层大batch训练直接崩给你看。LAMB的做法是先按层计算更新量的范数然后做一个归一化让每一层的更新步长保持相对一致。你可以把每一层想象成一个不同弹力系数的弹簧LAMB做的事情就是根据每根弹簧的弹力系数去调整拉扯的力度保证整体形变比较均匀。这让它在ImageNet、BERT预训练这种需要上万batch size的场景里大放异彩训练速度提升的幅度相当可观。但LAMB不是没有代价。首先是显存开销更大因为它需要额外存储按层归一化相关的统计信息其次它对超参数同样敏感尤其对学习率和weight decay的组合要求比较苛刻。所以我个人的建议是如果你只是单卡训练或小batch微调完全没有必要上LAMBAdamW就够用了如果你在做大规模预训练或者被迫要用超大batch弥补训练资源不足那LAMB值得认真考虑。3.2 Lion又大又漂亮但切到手也会疼2023年谷歌提出的Lion是一个很有意思的样例。它的更新规则简单到令人发指只看梯度的正负符号来决定参数往哪个方向移动更新时不再计算一阶矩和二阶矩的精确数值而是根据符号运算的结果直接更新。这直接省掉了指数移动平均的部分计算量速度和显存占用都优于Adam。从Benchmark上看Lion在图像分类、视觉语言模型等任务上确实取得过不错的效果尤其在大规模训练中的收敛速度让人印象深刻。论文里“更漂亮的收敛曲线”也实至名归。但问题在于Lion对学习率极为敏感。很多人直接套用Adam的学习率去跑Lion几乎必然梯度爆炸。我实测的经验是Lion的推荐学习率大约是Adam的1/10甚至更低而且weight decay也需要相应调大。坦率地说Lion并没有在工业界取代AdamW的地位原因也很简单——稳定性。AdamW经过无数项目验证任何一个小团队都能很快上手而Lion对于超参数的要求实在太高稍微一个不小心就要重跑好几天训练。这就像一辆限量超跑性能数据非常漂亮但它需要最顶级的赛道和车手才能稳定发挥。如果你只是个通勤代步何必非要去跟这种“大而美”的优化器较劲呢3.3 其他值得关注的“小而美”思路除了LAMB和Lion这种知名度较高的还有一批“小而美”的优化器思路值得关注。比如AdaFactor它是对Adam二阶矩估计做矩阵低秩分解的降维实现主要解决Transformer训练时显存占用过高的问题再比如TAdam它尝试把Transformer结构里的注意力机制融合进优化器设计虽然业界讨论热度不算高但某种程度上代表了“把模型结构和优化器耦合在一起”的一个研究方向。还有一类跟优化器紧密相关但常被遗忘的技术是梯度裁剪gradient clipping。它本身不是优化器但它对优化器能否稳定工作起着至关重要的作用。尤其在训练Transformer时梯度范数偶尔会突然暴增不裁剪的话轻则loss震荡重则直接NaN。我的实践结论是AdamW配合max_grad_norm1.0在绝大多数NLP任务上都能明显提升稳定性这个组合比纠结优化器本身的小改动值钱得多。4. 实操策略手把手教你选优化器、调超参数4.1 一个清晰的任务与优化器匹配矩阵各家优化器各有擅长场景我不能给你一个“万金油”答案但可以给你一个基于通用经验的匹配矩阵。这个矩阵不是拍脑袋定的它综合了优化器的理论特性和我以及我身边的同行在各种任务中的实际经验。任务类型推荐优化器第二选择核心理由视觉分类CNNSGDMomentumAdamWCNN损失面较平滑SGD泛化好收敛稳定Transformer微调AdamWAdam自适应学习率适合不同层梯度尺度差异大的结构大batch预训练LAMBAdamW按层归一化保证大batch下更新稳定大规模稀疏特征场景AdamWAdaGrad自适应学习率对稀疏特征更友好生成对抗网络GANAdam或AdamWRMSPropAdam对非平稳损失更鲁棒强化学习策略梯度AdamRMSProp稀疏且噪声极大的梯度需要自适应缩放这个表格只能算一个起点。真正靠谱的流程应该是先跑小规模实验做对比选定主优化器再做一轮它对应的超参数扫描最后才上全量数据。我的习惯是在小规模数据上先以默认参数快速跑通然后用一组对比实验验证选型是否合理——每个实验只改一个变量比如第一组SGD vs Adam第二组固定选型之后做batch size和学习率的配对扫描。这种方法虽然听起来不够炫酷但它是真正能让你对一个优化器建立手感的方式。4.2 超参数选择的底层逻辑很多新人问优化器超参数到底怎么调其实关键超参数就那几个学习率、momentum/beta1、beta2、weight decay、warmup步数、梯度裁剪阈值。学习率永远是第一优先级。它决定整体训练的步长过高直接发散过低则收敛慢。一个常见经验是AdamW在Transformer任务上大体以5e-5为基准如果batch size翻倍学习率大致也可以尝试翻倍但必须配合warmup。SGD加动量的学习率通常比AdamW大一个量级0.1、0.01甚至更大都不稀奇这取决于数据集和网络结构需要用小规模跑几次学习率扫描来确定。beta1和beta2决定了梯度的“记忆长度”。beta1是一阶动量相关的滑动平均系数通常固定为0.9它控制着动量对当前梯度的反应速度beta2是二阶矩相关的滑动平均系数默认0.99或0.999。如果你发现训练过程中loss出现周期性的突然抖动可以试试把beta2调大让梯度平方的估计更平滑。在我做长序列Transformer训练时把beta2从0.999调成0.99有时候会加快收敛但代价是训练后期不稳定这个需要谨慎地做消融。weight decay的使用也有讲究。分类任务上它往往能通过抑制过拟合让指标涨一点但幅度和任务、数据量高度相关。一般推荐从1e-4开始尝试如果你发现训练loss和验证loss的gap很小那就没必要加weight decay如果gap过大且训练集不大可以考虑调高到1e-3同时观察是否欠拟合。4.3 学习率调度和warmup优化器的左膀右臂优化器本身是一把武器但怎么挥舞这把武器经常决定胜负。学习率调度策略learning rate schedule和warmup在几乎所有现代深度学习训练中都是不可或缺的环节。Warmup的作用在训练初期尤其关键。刚开始训练时模型参数是随机的梯度方向噪声极大如果一开始就用很大的学习率很容易把参数推向一个不好的区域之后想拉回来就困难了。Warmup的直觉是先用一个很小的学习率让模型在“安全区域”里探索几步等梯度估计变得稳定可靠了再调高到目标学习率。使用AdamW时warmup的步数通常占总步数的5%到10%在训练比较深或者比较宽的模型时这个比例还可以适当上调。学习率衰减策略也很重要。常见的有线性衰减、余弦退火、带重启的余弦退火等。我的经验是余弦退火能在训练后期带来更好的收敛效果尤其配合SGD在图像任务上效果非常明显。在Transformer预训练中线性衰减到0或者保留一个最小学习率是更常见的做法。需要特别注意的是衰减策略必须和优化器类型配合。如果用的是AdamW学习率在训练后期应该衰减到一个很小的值而SGD如果衰减得太狠反而可能破坏已经学到的良好参数状态。4.4 混合精度与优化器的隐性适配如果你的显存非常紧张免不了要用混合精度训练AMP。这个环节里优化器的适配问题很容易让人栽跟头。混合精度训练会把主模型参数保存在FP32里但前向和反向计算使用FP16来加速。FP16的数值范围比FP32小得多梯度更新时如果参数值太小很容易在即将下溢的边缘来回试探导致更新失效。在这样的背景下优化器在Adam中的二阶矩估计参与运算时的精度问题就变得非常关键。很多混合精度框架比如PyTorch的Apex或者原生AMP都有一个“动态损失缩放”Dynamic Loss Scaling机制专门防止梯度在反向传播时下溢为0。但如果你从头手写混合精度训练流程就需要特别注意在更新前要把梯度从FP16转回FP32优化器里所有计算尽量以FP32承载最后再转回FP16做参数更新。这个过程做对了优化器才能在混合精度下和全精度保持同等的收敛表现。5. 那些年我们一起踩过的优化器坑5.1 现象一loss不降反升甚至直接NaN这是所有炼丹师都遇到过的问题。你是否反复确认过数据没有问题网络结构也没毛病却依然在训练开始后不久loss就飞了这时候优先怀疑优化器设置。我踩过最经典的一次在一套视频理解模型里换了一个新优化器结果忘了调整学习率直接用了之前AdamW在别的任务上的默认值结果loss秒变NaN。排查过程花了一个多小时最后发现就是学习率偏大加梯度裁剪阈值设置过高。建议处理顺序是先把学习率降到原来的1/10确认能不能正常跑几步如果没问题再逐步加大如果还不行检查梯度裁剪阈值和weight decay最后再怀疑数据里是否存在异常的标签或特征。每次遇到NaN可以先打印一下梯度的范数判断是某一个层出现了梯度爆炸还是有差值的张量破坏了计算图。这个习惯能帮你省下大量无意义的轮次。另外提醒一下如果用了混合精度NaN往往也和动态损失缩放的阈值设置有关需要把这个维度也加入排查范围。5.2 现象二验证集指标上不去训练集loss也降得平缓有时候loss没炸但就是下降得特别慢像蜗牛爬一样到后面甚至接近一条直线。这种情况大概率不是优化器的锅而是初始化或数据预处理的问题但如果排除了这些再回头看看优化器设置。我手里有一个NLP项目序列标注任务用BERT微调。一开始选了Lion作为优化器以为它的收敛速度快能节省时间结果跑了20个epochF1反而比用AdamW少了将近两个点。后来仔细看日志发现Lion在前几个epoch收敛确实快但它会在后期产生很明显的过拟合现象需要更强正则或者更早地打早停。这就是“大而美”优化器的风险前期收敛快不代表最终效果好你做决策时一定要看完整收敛曲线而不是只对比前几个epoch。5.3 现象三训练到一半loss突然跳高然后又慢慢降回去这个现象的特点是loss曲线在某一步突然出现了尖峰随后又恢复之前的下降趋势。这样的突变往往不是优化器本身造成的而是数据加载时的随机性或者某些样本存在异常大梯度。也可能是学习率调度到了衰减阶段但衰减速度没跟模型状态匹配上。我用过一个笨办法在训练循环里加一个钩子遇到loss超过一定阈值就打印出当前batch的index和数据特征。连续追踪几轮之后很快就定位到了一个包含大量噪声标注的batch。把它过滤掉之后训练曲线就恢复丝滑了。优化器和数据之间的冲突用这种方式排查效率很高。5.4 现象四小batch下没问题大batch下效果变差这是一个非常经典的现象当你把batch size从32放大到256或1024后同样的epoch数下效果明显变差。很多新手第一反应是增加学习率但直接放大的话很大概率会训练不稳。正确思路是增大batch size时同步增加学习率但要按比例缓慢放大同时增加warmup步数让训练更顺滑。如果这样调整后效果还是不好那就需要认真考虑LAMB或者其他的大batch专用优化器了。我之前训练一个浅层文本模型时把batch从32翻到512用Adagrad优化器做了完整对比。Adagrad在batch 32时效果还行但batch 512时因为梯度累积矩阵无约束地增长有效学习率衰减太快模型根本没有充分学习信号。换用AdamW之后同样加大batch性能就明显好转。这再次说明大batch场景下选择正确的优化器和学习率策略比单纯堆算力更重要。5.5 一张“避坑速查表”送给你以下是我这几年来在项目落地中沉淀下来的一张检查清单每次遇到优化器相关问题时我都会拿着它逐条排查问题现象优先检查项推荐操作训练直接NaN学习率过大 / 梯度爆炸学习率降1/10开梯度裁剪收敛极慢学习率过小 / 调度不合适学习率扫描确认warmup合适后期不稳beta2过小 / 调度过猛增大beta2软化衰减曲线过拟合明显weight decay缺失调大weight decay或加更多正则大batch效果差学习率和batch不匹配学习率按sqrt或linear规则放大验证集波动大优化器与任务不匹配对比SGD、AdamW、Lion等再定6. 写在最后Optimizer是手段不是信仰聊到这里我想把标题那个问号重新拿出来放大一下。“A Big Beautiful Optimizer”真的值得追求吗我个人在走过这么多弯路之后最大的体会是优化器是服务于目标的手段不是需要供奉的信仰。真正重要的永远是对问题本身的理解、对数据质量的把控以及一套成体系的实验迭代方法。一个再漂亮的优化器也无法弥补数据泄漏、标签噪声和模型结构错误带来的影响。实际工作中我见过有人为了追逐最新的优化器论文而不停更换训练设置结果模型效果始终没有提升原因是他根本没有建立一个固定的实验基线。而另一些经验老到的工程师常年只用SGD或AdamW这两个最不起眼的选项却能把模型调到很好的效果。他们的秘密不在于优化器选得多华丽而在于他们已经完整掌握了优化器与环境之间的互动关系学习率、batch、初始化、正则项、数据分布这些变量互相制约任何一个点发生变化都要重新审视整个训练方案。所以如果你现在正为“该用哪个优化器”而发愁我的建议是先选一个你最熟悉的、在类似任务上被验证过的优化器然后踏踏实实地把它的超参数调明白。不要被“大而美”的说法带节奏稳定和可控才是工程上最宝贵的东西。最后再送给大家一个小技巧不管用什么优化器每训练一个阶段就把模型checkpoint保存下来同时记录优化器当时的状态字典。很多训练事故其实都可以通过回滚到之前的checkpoint和对应的优化器状态来挽回。这个习惯帮我避免过好几次推倒重来的灾难希望也能帮到你。
返回列表