资讯动态

深度学习调参核心:损失函数与优化器选择及训练曲线诊断

发布时间:2026/10/19 17:05:25 来源:尧图企业网站定制
深度学习模型的训练效果七成取决于损失函数和优化器的选择这个比例一点也不夸张。我见过太多朋友上来就套一个Adam、配一个交叉熵结果模型不收敛还以为是网络结构写得不对折腾几天最后发现就是优化器相关的学习率没配好。也有不少人把大量精力花在调batch size上却忽略了优化器权重衰减的默认值已经过时。这篇内容我会从损失函数、优化器的底层逻辑讲起再结合我实际跑过的训练项目串一遍调参流程讲清楚每个关键选择的理由以及怎么通过观察训练曲线快速定位问题。适合正在做深度学习项目、对模型训练效果不满意、或者刚开始接触训练细节的读者。1. 内容整体设计与思路拆解1.1 为什么损失函数和优化器是调参的核心很多人理解调参就是调学习率、调batch size、调网络层数其实这些都是外围旋钮。真正决定模型学什么的是损失函数决定模型怎么学的是优化器。这两个组件一旦选错后面的所有操作都像是在给一个方向已经偏了的车反复补油越跑越偏。损失函数本质上是给模型定义一个“什么是对、错到什么程度”的评分标准。比如MSE均方误差会觉得大误差特别不可接受因为平方项放大异常值而MAE平均绝对误差对所有误差一视同仁。同样是回归任务选MSE还是MAE模型对离群值的敏感度完全不同。我实际跑过一个工业场景的回归任务数据里混着约5%的传感器噪声用MSE的时候这些噪声点把整个梯度方向带偏了模型学出来的曲线始终贴合不到主体趋势换成Huber Loss之后异常值的影响被压制住效果一次就对了。优化器方面SGD随机梯度下降、Momentum、RMSProp、Adam都不只是换个写法它们对梯度的处理逻辑完全不同。SGD每步只用当前梯度容易在峡谷地形震荡Momentum引入速度积累帮参数冲出平坦区RMSProp自适应调节每个参数的学习率让稀疏特征也能有效更新Adam把动量和自适应学习率合到一起收敛速度快但有时候会错过泛化更好的平坦解。所以选择优化器不是在选“哪个更好”而是在选“哪个更匹配你当前的损失曲面和训练阶段”。1.2 训练效果的三层观测逻辑选择之前先想清楚你的项目处在哪个阶段因为损失函数和优化器的选型策略在模型研发的不同阶段是不同的。我习惯把训练过程分成三层来看第一层是“能不能收敛”。这个阶段关注loss是否持续下降网络能不能拟合训练数据。如果这一步都做不到问题往往出在优化器参数、学习率、初始化或者数据标签有误跟模型结构关系都不大。第二层是“收敛到什么质量”。loss在训练集上降下来了但是验证集表现不佳这就是过拟合问题。这个阶段要回头审视正则化手段权重衰减是不是开得太小、数据增强是否足够、以及损失函数是否太强地拟合了训练集上的异常模式。第三层是“泛化能力是否稳定”。模型在验证集上表现不错但换一批数据就拉胯。这种情况常常跟优化器的最终收敛位置有关Adam解往往处于尖锐极小值附近而SGDMomentum更容易跑到平坦的极小值。近年来大家说的“Adam泛化不如SGD好”本质就是这个原因。这套三层观测逻辑决定了我下面的每一个选型和调参动作。不要一上来就追求完美指标先把每一层的问题拆出来分别对症下药。1.3 损失函数与优化器如何联动损失函数和优化器不是独立工作的。它们的联动关系体现在梯度的数值分布上。交叉熵loss的梯度天然比较稳定因为softmax的输出被归一化到0到1之间但像IoU Loss这种目标检测里常用的损失梯度尺度会随着目标尺寸剧烈变化这时候如果优化器没有自适应调节能力训练就会很痛苦。我实际经验里有个简单的联动原则如果你的损失函数梯度天然在量级上差异很大优先选择带自适应学习率的优化器比如Adam、RMSProp如果你的损失函数梯度本身比较规整用SGD配合动量往往能得到更好的最终效果。这个原则几乎可以解释大部分场景下的优化器选型问题。另外损失函数本身也可以做可微的平滑处理让梯度分布更友好。比如把二值交叉熵换成带标签平滑的版本或者把排序损失改成soft版本的近似都能显著提升优化器的工作效率这一步很多人忽略了。2. 损失函数实务指南——都选什么、为什么、怎么改2.1 回归任务L2、L1与Huber的取舍回归任务最常见的三个损失是MSEL2、MAEL1和Huber。很多教程只讲它们公式上的区别很少提实际训练中的表现差异这里我展开说。MSE对离群点极其敏感原因在梯度误差为2时梯度是4误差为10时梯度是20离群点会主导整个batch的梯度方向。如果你的数据干净、没有异常值MSE收敛平滑而且数学性质好很多理论分析都基于它研究原型阶段选它没问题。MAE的梯度恒定为正负1对异常值稳健但它在误差接近0的时候梯度不连续训练后期会围绕最优解抖动难以精修。而且MAE的梯度对每个样本的贡献都一样大容易让模型“忙于”处理那些本身就不容易预测的样本反而忽视了好样本。Huber是折中方案它在误差较小的时候表现为L2误差较大时表现为L1。脏数据和干净数据混在一起的场景它的表现通常最好。实际使用中参数delta控制切换阈值我一般先设成1.0如果发现模型对离群值还是太敏感就提高到2.0或3.0。注意Huber并不是对所有回归任务都更优。如果你的任务是高精度数值预测误差必须被严格惩罚比如金融场景大偏差不可接受MSE这类二次惩罚反而更符合业务需求。损失函数的选择要服务于业务目标不是服务于“谁更稳健”。2.2 分类任务交叉熵的等价变形与更多坑分类任务默认用交叉熵但很少有人意识到配合softmax的交叉熵其实自带一个非常好的梯度形式对于batch中每个样本梯度等于模型预测概率与真实one-hot标签的差。这意味着当预测稳定时梯度自然趋近于零这个特性让训练过程相对平滑。实际工程中更需要注意的是数值稳定性问题。如果你的实现是先算softmax再算log再算交叉熵中间经过的log(0)会造成NaN。正确做法是用带log_softmax融合算子的实现或者干脆用现成框架里的CrossEntropyLoss它内部会把softmax和交叉熵合并计算数值稳定性好得多。另一个经常被忽略的是类别不均衡场景。直接用标准交叉熵时样本量大的类别会把梯度方向完全压制。我常用的处理方式不是简单地加权而是先用一个小的实验确认比例失衡的严重程度计算一下每个类别的平均loss贡献如果头部类别的loss贡献超过90%就说明梯度已经失衡了这时候该上focal loss或者类别加权。focal loss的gamma参数值得单独说。gamma0时就是普通交叉熵gamma增大时模型对置信度高的样本的梯度贡献会指数下降把注意力集中在难样本上。我试过gamma2.0在大多数不均衡场景下都表现不错但如果你把gamma调到6以上训练很容易失衡因为模型会过于关注那些可能本身就是标签噪声的极端难样本。2.3 多任务与对比学习损失权重的确定方式多任务学习里最常见的问题是几个loss的量级不一致导致优化器被量级大的loss主导。一个我亲测有效的做法是先分别用单任务训练几个小模型记录各自loss的初值然后按初值比例来设置权重而不是直接拍脑袋设成1:1:1等于。后面训练过程中每过几个epoch重新统计一次梯度量级再做一次权重校正。这个方法的核心逻辑是让每个任务的梯度在初始阶段就有接近的量级贡献后面再靠模型自身的动态平衡来调整。市面上还有一些自动加权的方法比如基于uncertainty的加权但在小团队的小项目里手动按loss初值比例加权已经能解决90%的问题不必引入额外的复杂度。对比学习场景下的InfoNCE损失有一个隐含的温度系数超参数这个参数的影响比很多人以为的大得多。它本质上是控制模型对“难负样本”的关注程度温度系数越小模型越关注与正样本相似的难负样本温度系数越大模型越关注所有负样本的均匀性。我在图像自监督预训练的项目里试过温度系数从0.05到0.5的范围0.07附近的表现显著优于0.2差距能达到好几个点。遇到多任务或者对比学习项目我建议你先把损失权重的调节当作一个独立的调参环节来做不要和其他超参数混在一起调。否则你根本分不清是分类头没学好还是回归头压住了共享特征。3. 优化器路线图——从SGD到AdamW的演进逻辑3.1 优化器核心机制拆解要理解怎么选优化器得先理解它们到底对梯度做了什么。SGD的更新公式只有一个学习率乘以梯度逻辑最简单。如果把loss曲面想象成一个山地地形SGD就是每一步都往最陡的方向走缺点是在峡谷地形中会来回震荡收敛慢。动量方法引入了速度变量每次更新时合并历史梯度的加权平均。这就像在下坡时积累惯性能平滑掉峡谷地形的震荡。动量的关键参数是0.9到0.99之间的系数越接近1惯性越大但过大也会导致收敛变慢甚至越过最优区域。RMSProp系列引入的是二阶矩的自适应。它为每个参数维护一个梯度平方的滑动平均让梯度小的参数获得更大的更新步长梯度大的参数获得更小的步长。这解决了稀疏特征更新缓慢的问题。Adam的贡献在于同时结合了动量一阶矩和RMSProp的二阶矩自适应并且在早期做了偏置修正让前几步更新不至于因为零初始化而过大。Adam的默认参数在很多任务上开箱即用这也是它为什么成为默认选择。但Adam有一个我一直介意的问题它没有把权重衰减和L2正则化正确区分导致了实际正则效果弱于理论预期。3.2 各流派适用场景对比与选择建议我用自己的项目经验给各优化器画了一个适用范围表你在启动新项目时可以直接参考。优化器收敛速度最终精度泛化能力适用场景SGDMomentum慢高强图像分类、检测、语义分割等CV任务Adam快中高中NLP、Transformer、小数据量快速实验AdamW快高中高预训练大模型、Transformer、需要解耦权重衰减的场景RMSProp中中中RNN、LSTM等循环结构训练LAMB/LARS快高强大批量分布式训练、大规模预训练这个表的背后逻辑是SGDMomentum虽然收敛慢但它到达的解通常平坦平坦极小值附近的泛化误差更小。Adam收敛快但容易停在尖锐极小值尤其在训练后期学习率不足时。Transformer和预训练模型用AdamW是因为参数规模太大SGD的收敛效率完全跟不上而AdamW修正了权重衰减的耦合问题让正则效果真正生效。实践里我的选型策略是拿不准的时候先跑AdamW配一个中等学习率大约1e-4到5e-4做基线确定模型能收敛后再切换SGDMomentum做精度冲刺。很多CV项目在后期从Adam切到SGD之后验证集精度能再涨一到两个点。切换时要同时调整学习率SGD通常需要更高的学习率0.01到0.1量级才能发挥作用。3.3 一阶优化之外EMA、梯度累积与数值精度除了选哪个优化器几个和优化器配合的使用技巧也值得单独说。EMA指数移动平均是我在几乎所有视觉项目里都会开的选项。它维护一组参数的移动平均副本平时正常训练原有的参数推理时用平均过的参数。这个操作几乎零成本但能显著提升模型的鲁棒性尤其在小数据集上效果明显。实现的时候记得在开始阶段做偏置修正否则前几个epoch的移动平均值会偏低。梯度累积是为了应对显存不够的情况。当你的batch size受限于显存只有16但你的实验需要等效batch size为64时可以连续跑4个前向反向把梯度累加起来做一次更新。这里只有一个坑我必须提醒梯度累积时要注意BN批归一化层的统计量是基于每个小batch计算的等效batch size增大但BN统计仍然按小batch来效果和真正的大batch并不完全等价。如果模型关键层用了BN建议想办法增大物理batch size或者改用同步BN方案。数值精度方面AMP自动混合精度训练已经是主流但我实际踩过一个坑某些损失函数在fp16下的数值范围太小比如一些姿态估计任务里的关键点热图loss直接混合精度会导致loss变成NaN。如果遇到这种情况可以先把损失函数计算部分保持fp32只在卷积和全连接层开启自动混合精度。4. 调参与训练全流程实操4.1 学习率策略lr、warmup、cosine与重启策略学习率是优化器最重要的配套参数。我的经验里学习率的数量级选择比具体数值重要得多。Transformer类模型常用1e-4到5e-4CNN类模型用SGD时常用0.01到0.1用Adam时常用1e-4到1e-3。如果你不确定先用一个简单的办法找基线把一个batch训练上几十步观察loss有没有下降。如果loss变大大概率是学习率过大如果下降非常慢就调高一档。warmup策略的必要性可以从优化器的状态分析Adam在第一步时二阶矩估计从零开始初始步长会被偏置修正放大直接用很大的学习率容易在初始阶段把参数推到不好的位置。所以给一个几百步到几千步的线性warmup让学习率从小到大爬升训练会稳定很多。尤其在用大学习率和大批量时warmup几乎是必须的。cosine退火是目前效果最稳定的学习率策略学习率先保持高位让模型有机会跳出浅的局部最优后面平滑下降进行精修。它的解释是学习率大时模型探索学习率小时模型收敛这种先探后收的节奏很适合深度模型。相比step decaycosine不用人工选择衰减节点训练全程自动完成。关于重启策略我试过一次用cosine退火到最低点后重新拉高学习率效果确实有提升但训练时间翻倍。如果项目时间紧我建议直接标配一次普通的cosine退火就够了把精力放在更核心的损失函数和数据工作上。4.2 权重初始化与正则化配合初始化问题在Transformer类模型中比在CNN中更值得注意。CNN的BN层让初始化选择相对宽容但Transformer的残差连接和层归一化结构对初始化的方差非常敏感。我遇到过用默认初始化Transformer训练到第几步就出现梯度爆炸的情况最后通过调整初始化方差才解决。一个可复用的检查初始化方案在模型定义之后选一个batch跑一次前向把每个层的激活值标准差打出来。如果不同层的激活标准差明显差异比如跨了几个数量级说明初始化不太合适。合理的状态是各层激活标准差在相近量级这样梯度回传时才不会出现衰减或爆炸。正则化部分我只建议加两种权重衰减和Dropout。权重衰减在AdamW里是解耦的直接设置weight_decay参数即可如果用原始的Adam要小心权重衰减被耦合在L2正则里实际效果打折。Dropout在Transformer类模型里一般设在0.1左右视觉模型用SGD时甚至可以不设。此外如果模型有严重过拟合先考虑数据增强和标签平滑不要一味加正则强度正则太强会拖慢收敛。4.3 批大小的选择逻辑batch size对训练的影响很容易被低估。它直接改变梯度噪声的尺度大哥batch带来更准的梯度训练曲线更平滑但需要更多步数来探索小batch带来更多噪声有时这种噪声反而有助于模型跳出尖锐局部极小值。实际项目中我通常把batch size作为“硬件约束优先满足”的参数在显存允许的范围内尽量取大再通过调整学习率来匹配。注意一个经验法则batch size增大K倍学习率一般也要增大K倍线性缩放法则但只在batch size不是特别大时成立。如果你从128切到1024线性缩放就不太可行了需要配合warmup来稳住训练。大批量训练下我觉得更稳妥的做法是保持学习率不变增加训练的step数成本和收益才比较平衡。如果你用的是预训练模型做微调batch size别卡得太高。微调阶段梯度噪声大一点有时反而能保留预训练学到的分布。我做过一组对比同一个预训练模型分别用32和128的batch size微调小batch在验证集上平均高出一个点左右这个现象在NLP分类任务上更明显。4.4 训练总时长、早停与模型快照训练epoch数量也是一个需要刻意选择的超参数而不是直接设一个50跑完就收工。我通常设置一个合理的上限比如30到50个epoch然后配合早停策略验证集指标连续多个epoch不提升就提前结束。这个做法的核心价值在于防止过拟合也节省了等待大量空跑的时间。保存模型快照是规定的动作不只是最后存一个best模型。我习惯在每个epoch结束都保存一个检查点同时把当前epoch的优化器状态一起保存这个优化器状态很多人忽略。一旦训练中断或者你想从某个epoch继续训练没有优化器状态就得重新warmup效果差别很大。顺便提一个EMA的保存细节如果你用了EMA记得保存的模型是EMA平均后的参数而不是当前训练参数。有些框架的ModelCheckpoint默认保存训练参数如果你配了EMA回调要检查模型存档的逻辑否则模型推理时用的根本不是你想让它在测试集上表现的参数。5. 观测、诊断与问题排查实录5.1 训练曲线阅读技巧训练曲线的形态能透露超参数的问题。loss持续下降但验证集指标纹丝不动提示过拟合或者训练集和验证集分布差异太大。loss在某个数值附近抖动可能纯粹是学习率偏大模型在最优解附近来回震荡。loss前期正常下降中期突然飙升多半是梯度爆炸或者数据里混入了异常样本。我建议把训练过程可视化做成至少包含三条曲线训练loss、验证loss、以及一个衡量业务指标的值比如分类准确率、检测的mAP。这三条曲线的相对关系才是判断的依据只看训练loss很容易被表面掩盖。验证loss的训练曲线里还藏着一个判断“是否需要切换优化器”的信号如果验证loss在最后阶段出现平台期用cosine退火或重启策略还能继续下降说明还有优化余地如果验证loss已经开始回升了再训练下去只会更差早停应该早触发。5.2 梯度爆炸、消失与Loss震荡排查我遇到过的梯度爆炸案例最常见的原因不是网络太深而是损失函数计算过程中出现了数值不稳定的中间操作。解决办法是先把loss计算改成数值稳定的内置实现再排查别的可能。如果确认实现没问题就看梯度裁剪是否开启梯度裁剪是防止梯度爆炸最直接的兜底手段设一个最大梯度范数如1.0几乎所有的训练崩溃都能被拦住。梯度消失的排查逻辑有些不同。如果是深层的网络先用检查激活值标准差的脚本逐层打印。如果看到后面的层激活标准差已经接近零大概率是初始化不合适或者激活函数选择有问题。在深层网络中把简单的ReLU换成带负斜率的变体或者加入残差连接都是从结构上改善梯度回传的手段。Loss震荡我偶尔会遇到它的成因往往不唯一。有时候是batch size太小导致梯度噪声大有时候是学习率过高导致来回震荡也有时候是损失函数本身对某些样本的梯度特别大。排查时我建议先调小学习率看震荡是否缓解然后调大batch size如果都无效再回头仔细检查数据标签是否错误尤其是跨epoch随机打乱时某些错误标签反复影响模型。5.3 常见问题速查表现象优先排查点参考解决方案loss一开始就是NaN损失计算数值稳定性、输入数据含NaN或Inf换成数值稳定实现清洗数据模型评估时开启NaN监控训练loss不下降学习率过小/过大、数据标签错误、模型代码有bug用单一batch过拟合测试检查数据抽样调整学习率到合理数量级训练loss下降、验证指标差过拟合增加权重衰减正则、数据增强、Dropout早点做早停判断验证loss中期飙升梯度爆炸、学习率过高开梯度裁剪调低学习率检查loss的数值范围两个loss的任务一个收敛一个不收敛多任务权重不均衡按loss初值比例重新分配任务权重换一个batch size表现完全不一样学习率和batch size不匹配线性缩放学习率后加上warmup用Adam训到后期精度停滞自适应学习率在后期步长太小切到SGDMomentum做收尾精修这张表的每一条都来自我的真实排查经验你可以直接当作排查手册来用。遇到问题先对照定位能省一整天的试错时间。5.4 一个实用的工具化复盘思路出于效率考虑我一般会给每次训练实验打一套结构化的日志包括随机种子、损失函数和参数、优化器类型和参数、学习率策略、batch size、权重衰减、数据增强方式、最终指标。这样做的原因很简单深度学习里耦合效应太强单改一个参数看不出原因但多组实验放在一起对比规律很快就会浮现。举个例子我有一组实验反复出现“同一个学习率策略在A优化器上表现好、在B优化器上完全不行”的现象。如果把日志对齐出来看很快能发现A优化器本身有较大的内置学习率缩放而B优化器没有。这只是个简单的线索但没有结构化日志你很难把这类跨实验的规律找出来。我推荐大家至少用电子表格维护一个实验记录表列名就按上面说的几项。训练开始前把参数填好训练结束后把指标填好一个月后你的实验记录就是你做新项目时最值钱的参考材料。6. 实操经验与个人体会6.1 从损失函数和优化器联动角度做一次全局优化我的训练流程中有几次项目提升最大的节点不是靠某一个精妙的超参数而是靠一次完整的“损失函数—优化器—学习率”联动调整。具体操作是先确认损失函数和业务目标一致再根据损失函数的梯度特性选择优化器档位最后用学习率策略去匹配优化器的收敛习惯。这三步循环几次每次只动其中一个维度其他保持不变。这套流程最大的价值是让你区分出每个组件各自贡献了多少。如果三个一起调模型效果变好了你也不知道是哪个起了作用一旦出问题更无从下手。深度学习的调参和做对照实验本质上是一回事控制变量永远是第一原则。6.2 一些能直接抄作业的小技巧最后一个部分分享几个我踩坑之后沉淀下来的小技巧。第一个是关于学习率的预设。如果你用Adam类优化器先试3e-4这个值它在很多任务上都是不错的起点如果你用SGDMomentum跑图像模型0.01到0.1之间通常能找到合适值但需要用对数刻度去搜索一次把0.1和0.01都跑一遍不要只跑一个值。第二个是权重衰减的设置。AdamW的weight_decay我通常设在0.01到0.1之间Transformer模型常见0.05左右。SGD的weight_decay一般在0.0001到0.001之间过大容易欠拟合过小等于没有正则。第三个是别迷信“默认配置”。框架给的默认参数只是为了通用性不是为你这个特定任务调过的。你在用默认配置跑第一次实验后至少把学习率、batch size、权重衰减这三个参数做一轮手动搜索大概率有稳定提升。第四个是和损失函数直接相关的对损失曲线做平滑观察时如果损失出现周期性的锯齿不用太紧张这通常只是数据打乱后的batch间的正常差异。但如果锯齿的幅度越来越大就要警惕了这通常代表训练正在走向不稳定。6.3 最后的体会我做过一段时间的大小项目之后最大的体会是调参的价值不在于找到一个“魔法参数”而在于理解每个参数在训练过程中的角色。损失函数决定了模型的目标优化器决定了达到目标的路径学习率则决定了这条路径上每一步的幅度。把这三者的分工和联动弄清楚你再面对任何新模型和新任务时都不会再被动地靠试错来找感觉。如果你现在正被某个训练效果困扰我的建议很简单先把模型结构放一边单看损失函数和优化器这一层。把这一层调顺了很多看似结构层面的问题其实会自动消失。这是我在深度学习实践里最划算的一笔投入。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑