新闻详情 资讯动态

全面了解最新资讯与建站知识,洞察行业趋势。

行业资讯

深度学习优化实战:从梯度下降到Adam的学习率调参指南

发布时间:2026/9/9 17:14:16
深度学习优化实战:从梯度下降到Adam的学习率调参指南 1. 这篇读书笔记到底在记什么先说清楚这个编号的来历。我给自己定了一个规矩读《Deep Learning》就是那本深度学习领域的“花书”Ian Goodfellow 等人写的教材时每整理一个专题的笔记就用“书名缩写章节范围篇序号”来命名。deeplearningbook_040-2意思是这本书第四部分相关内容的第二篇专题笔记核心聚焦在深度学习模型训练环节里最容易翻车的部分——梯度下降、反向传播与优化算法。你可能会问深度学习头绪这么多为什么偏偏先整理这一块因为我发现一个很普遍的現象很多初学者能把 CNN、RNN 的结构画得头头是道PyTorch 的模型类也写得很顺但一到训练环节就开始玄学调参——loss 不降就换学习率换完不降就加层加层不降就换模型最后实在不行就上早停。整个过程没有任何章法本质原因是没搞清楚优化这块的底层逻辑。这篇笔记适合这么几类人看正在啃花书读到第四章和第八章数值计算与优化觉得公式太多、不知道怎么落地的读者已经能跑通几个经典模型但训练时一遇到 loss 震荡、收敛慢、梯度爆炸就束手无策的工程师准备面试需要把“从 SGD 到 Adam 再到学习率调度”这条链路讲清楚的求职者。这篇笔记不打算复述书里所有公式而是把花书里关于优化的关键结论拎出来结合我在实际项目里的训练记录讲清楚“为什么这么做”“不这么做会怎样”“出问题了怎么排查”。后面每一节都会有一个明确的核心论点你可以把它当成一份带注释的读书卡片来用。2. 梯度下降的底层逻辑与三个关键细节2.1 梯度下降不是在“下山”而是在“反推灯光方向”花书第四章花了很大篇幅讲数值计算其中最重要的概念就是梯度。书上给的定义很严谨梯度是函数对各个自变量偏导数组成的向量方向指向函数值增长最快的方向。很多教材喜欢用“雾天在山中下山”来类比——你看不清全貌只能靠脚下坡度判断方向。这个类比没问题但我想补充一个更工程化的理解方式梯度下降实际上是在做一个极其朴素的“反推”过程。模型预测错了误差会通过损失函数变成一个数值而这个数值对每一个参数的偏导数就是在告诉我们如果把某个权重调大一点点误差会增加还是减少、增加或减少的幅度是多少。算法做的事情就是朝“减少误差”的方向迈一步然后重复。为什么强调这个理解方式因为它能帮你解释很多实务中的现象。比如 loss 曲线在训练初期下降很快、后来变慢不是因为“模型快收敛了”而是因为误差函数在最优解附近通常比较平缓梯度幅度变小同样的学习率迈出的实际步长也变小了。你看到的变化背后是梯度的空间分布规律不是某个神秘的收敛状态。从花书里可以提炼出一个实用结论梯度是局部信息它只告诉你当前位置最陡的方向不告诉你全局最优在哪。所有基于梯度的优化算法包括之后要讲的动量、Adam本质上都是在用各种技巧缓解“只看局部”带来的问题。2.2 学习率才是真正需要反复调的“超参数之王”在花书第八章作者明确给出了一个观点学习率是训练深度网络时最重要的超参数之一。这一点我在实际项目中举双手赞成——如果你只能调一个参数那就调学习率如果你只能做一件事来改善训练那就是给学习率设计一个合理的调度策略。学习率设置不当的后果通常表现为两种学习率过大loss 在训练初期不降反升或者剧烈震荡甚至直接变成 NaN。原因是参数更新步长过大跳过了损失函数的低谷区域跑到了梯度更大、更不稳定的地方。学习率过小loss 下降极慢训练了几个 epoch 还停留在同一个量级。用 TensorBoard 看梯度分布会发现参数更新的幅度非常小模型基本在原地踏步。我在实际训练中遇到过最典型的一次事故用 0.1 的学习率训练一个图像分类模型前三轮 loss 从 2.3 降到 0.8看起来一切正常第四轮开始时 loss 突然变成 3.9然后一路飙升到 7.2紧接着出现 NaN。当时第一反应是数据有问题检查了数据加载、标签对齐、归一化全部正常。最后把学习率降到 0.01从头训练问题消失。那次排查耗费了整整一天而根因只是一个学习率设置。从那以后我养成一个习惯新模型第一次训练一律从 0.001 或 0.0001 起步跑两三个 epoch 观察 loss 的下降斜率再决定要不要把学习率调大。花书里建议的“在训练初期做几次学习率扫描测试”就是这个思路的规范化操作——先用几个相差 10 倍的学习率各跑一小段看 loss 的下降曲线选一个下降最快且不震荡的区间再正式训练。2.3 损失曲面不是碗而是“皱巴巴的床单”读花书第四章时有一句话让我印象很深在高维空间中我们很难直观想象损失函数的形状。很多教程画的损失曲面是一个漂亮的碗最优点在碗底这个图对理解梯度下降有帮助但它会严重误导你对实际问题的判断。真实的高维损失函数更接近一张被揉皱的床单——有大量局部低谷、鞍点、平台区域。特别要注意“鞍点”它是某些方向上是极小值、另一些方向上不是极值的点。在高维空间里鞍点比局部最优更常见因为要让一个点在所有方向上都高于邻域条件太苛刻了而在部分方向上高于邻域则相对容易。梯度下降到鞍点附近时梯度幅度很小训练看起来像停住了。这时候很多人会误以为“模型已经收敛到最优”其实只是被困在了一个梯度接近零但并不是最优的位置。这也是为什么后面要引入动量法——它有“冲量”能从鞍点区域冲出去。花书给的一个关键建议是不要试图用一阶方法精确逼近全局最优深度学习的目标本来就是找到一个“足够好”的解让训练误差和测试误差都达到可接受的水平。理解这一点你调参的心态会稳很多。3. 反向传播计算图与链式法则以及一次手推验证3.1 为什么反向传播是整个深度学习训练的发动机花书第六章有一节专门讲反向传播核心思想是通过计算图把前向传播的每一步拆成基本运算然后利用链式法则从输出端开始逐层反推每个参数的梯度。我最初的困惑是既然每个参数对误差的偏导可以单独算为什么非要用反向传播这种看起来“绕路”的方法后来算了一笔账才明白如果有 1000 万个参数正向方式计算梯度需要对每个参数都做一次完整的前向误差计算效率极低反向传播只需要一次前向传播和一次反向传播就能把所有权重的梯度全部算出来。计算量从“参数数量乘以单次前向开销”降到了“一次前向加一次后向”这个差距在深度网络里是数量级的。一次反向传播本质上是一套高效的梯度复用机制——先算出输出层误差再把这个误差逐层传回去每一层只需要做一个局部链式求导就能得到该层所有参数的梯度。这个设计是深度学习能够工程化的基石也解释了为什么现代框架都要把模型建模成计算图。3.2 手推一个两层网络的链式求导过程光看理论容易飘我建议每个想真正掌握反向传播的人都动手推一遍最简单的两层网络。下面是我在笔记里存的推导过程用一个不含偏置项的线性层加 Sigmoid 激活函数做例子。假设网络结构是输入 x一个标量第一层权重 w1输出 a1 w1 * x经过 Sigmoid 激活得到 h sigmoid(a1)第二层权重 w2输出 a2 w2 * h损失采用均方误差y 是真实标签L (a2 - y)^2对 w2 求梯度∂L/∂w2 ∂L/∂a2 * ∂a2/∂w2 2(a2 - y) * h对 w1 求梯度∂L/∂w1 ∂L/∂a2 * ∂a2/∂h * ∂h/∂a1 * ∂a1/∂w1 2(a2 - y) * w2 * sigmoid(a1) * x其中 sigmoid(a1) sigmoid(a1) * (1 - sigmoid(a1)) h * (1 - h)。观察这个式子你会发现一个关键信息w1 的梯度里包含了 w2、包含了激活函数的导数、包含了输入 x所有误差信息都是从输出层一级一级传回来的。如果你用代码把每一步的值打印出来和手算结果对比一致反向传播这块就算真正过关了。我在笔记里还留了个提醒计算时务必要注意每一步乘的是“前向传播的中间变量”不是重新计算的变量。比如上式的 h 必须是前向传播时 sigmoid(w1*x) 的输出不能在前面把 h 改了再回过头来求梯度。这是初学实现时特别容易踩的坑。3.3 框架自动求导背后的原理与限制现在我们写 PyTorch 或 TensorFlow一行 loss.backward() 就能拿到梯度很多人会忽略框架在背后做了什么。花书对计算图的一大贡献是明确区分了“符号形式求导”和“数值形式求导”现代框架用的是前者——先构建符号计算图再代入具体数值执行链式法则。理解这一点对你排查 bug 很有帮助。如果某个操作本身不可导或者你在计算图中引入了非法的操作比如原地修改参与求导的 Tensor框架通常会报错或给出警告。这时候不要只看报错信息要回看计算图是从哪里断开的。另外一个容易被忽视的限制是反向传播保存的前向传播中间变量会占用大量显存。这也是为什么同样的模型训练模式和推理模式的内存占用差异巨大。理解了这一层你就能明白为什么会出现“OOM 只在训练时发生”的现象——不是模型结构错了是反向传播需要把中间结果留在内存里供求导使用。4. 优化算法演进从 SGD 到动量再到 Adam我为什么最终换掉了纯 Adam4.1 SGD 的困境方向抖动与收敛慢花书第八章详细对比了各种优化算法起点是随机梯度下降也就是 SGD。SGD 的问题在训练时非常直观每一个 batch 算出的梯度都是整体梯度的一个带噪声的估计所以参数更新方向会在最优方向附近来回摆动体现在 loss 曲线上就是震荡明显、收敛缓慢。有一个优化的思路是把 batch 变大让梯度估计更准确。但 batch 太大会带来新问题内存不够、泛化性能可能下降。另一条路是设计更好的优化算法。花书将这类算法统一称为“带自适应学习率的优化方法”核心区别就在于它们如何利用历史梯度信息来修正当前更新方向。4.2 动量法给小球一个“初速度”动量法Momentum是我在实际项目里非常喜欢的一个改动因为它的物理解释太直观了想象一个小球从山坡上滚下来它的运动方向是历史速度和新坡度共同决定的。如果持续朝一个方向走速度会叠加越过小的坑洼和平台如果方向反复变化速度会相互抵消更新趋于平稳。用公式表达就是速度 动量系数 * 上一步速度 学习率 * 当前梯度参数 参数 - 速度动量系数的常见取值是 0.9也就是保留 90% 的历史速度再叠加当前梯度方向的影响。我在训练一个文本分类模型时做过对比同样的学习率、同样的初始化SGD 训练了 15 个 epoch 收敛到 87.2% 的准确率加了动量后 10 个 epoch 就达到了 87.5%。虽然只是零点几个百分点的提升但收敛速度明显加快。动量法最大的价值不只是提点而是让你的训练过程更稳。4.3 Adam 的“自适应”是怎么来的AdamAdaptive Moment Estimation可以说是目前使用最广泛的优化器花书也给了较高评价。它的思路是把动量和 RMSProp 的自适应学习率结合起来既维护历史梯度的指数加权平均作为动量又维护历史梯度平方的指数加权平均用于缩放每个参数的学习率。这样一来每个参数都有自己“独立”的更新步长。频繁更新的参数因为历史梯度平方大学习率会被缩小很少更新的参数学习率会相对放大。这在处理稀疏特征时非常有用也是 Adam 在 NLP、推荐系统等领域表现优异的重要原因。但 Adam 并非万能。花书和我在实践中都注意到Adam 的泛化性能在部分任务上不如调好学习率的 SGD 加动量。一个常见解释是Adam 的自适应机制让参数更新步子太“聪明”了反而在损失曲面比较平滑、需要精细收敛的时候不如 SGD 靠“惯性”能冲到更平缓的最优点。我现在的经验法则是数据量大、特征稠密、任务比较标准比如图像分类先用 Adam 快速跑通再换 SGD动量调优稀疏特征、推荐系统、NLP 里的 embedding 层优先 Adam 或 AdamW模型训练后期想逼近更优解可以切到 SGD或者用带余弦退火的学习率调度器搭配 Adam。4.4 学习率调度不是一个可有可无的附属品花书里有一句被我划了重点的话学习率调度是训练现代深度网络的关键组成部分。这句话翻译成工程语言就是不要在一个训练流程里写死学习率。我常用的三种调度思路按启动成本从低到高排列步长衰减Step Decay每训练 N 个 epoch学习率乘以一个小于 1 的系数比如 0.1。简单粗暴但需要人工确认衰减点和衰减幅度。余弦退火Cosine Annealing学习率按照余弦曲线从初始值逐步降到接近 0。不需要人工设置衰减点PyTorch 的 CosineAnnealingLR 直接支持。带热身的调度Warmup训练初期先用很小的学习率逐步升温到目标值再开始正常衰减。这在 transformer 类模型和超大 batch 训练中几乎是标配。为什么 warmup 在 transformer 里那么重要原因是注意力机制在初始阶段梯度非常不稳定如果一开始就用大学习率模型参数会被推到不好的区域后面很难回来。用小学习率先让模型“稳住”再逐步加速可以避免这个风险。花书没有直接用 warmup 这个词但它讨论的“在训练初期使用较小学习率有助于稳定训练”的思路是完全一致的。5. 实操记录一次模型训练从翻车到收敛的完整复盘5.1 问题现象loss 连续三轮不降反升几个月前我在训练一个多标签文本分类模型模型本身不复杂一个预训练的 embedding 层接到两层 BiLSTM 上最后接全连接层输出。优化器选的 Adam学习率 0.001batch size 32。前两个 epochloss 从 1.8 下降到 1.2还算正常。第三个 epoch 开始loss 不降反升到第五个 epoch 变成了 2.4比起点还高。用 TensorBoard 看了一下训练集和验证集的曲线训练集也在涨所以不是过拟合是训练过程本身出了问题。我按顺序排查了三类可能数据问题、梯度问题、学习率问题。5.2 排查过程与最终定位第一步检查数据。我随机抽取了一部分训练样本打印标签和文本确认没有 label 错位检查了损失函数里的 mask 逻辑确认 padding 位置没有参与 loss 计算。数据层面没有明显问题。第二步看梯度。我把模型每一层的梯度范数打印出来发现 embedding 层的梯度范数在训练开始后迅速增大其他层相对稳定。之前提到过梯度范数暴涨往往是学习率过大的信号。我用 torch.nn.utils.clip_grad_norm_ 把全局梯度裁剪到 5.0训练能跑下去但 loss 依然不降。第三步回到学习率。我把初始学习率从 0.001 降到 0.0003同时加了线性 warmup前 500 步从 0.00003 逐步升到 0.0003。结果从第一个 epoch 开始loss 就稳步下降最终收敛到 0.62比之前任何一次实验都要好。回看原因应该是这批数据的标签分布很不均衡模型在初始阶段就产生了较大的梯度0.001 的学习率在这个场景下显得太大了。学习率降低后模型没有进入“震荡区”训练自然就稳了。5.3 有效排查梯度问题的三个实用技巧基于这次经历我总结了一套排查梯度相关问题的流程分享出来供你参考技巧一每次新任务第一次训练先跑 5 到 10 个 batch打印 loss 和每个参数层的梯度范数。如果某个层的梯度范数在几个 batch 内暴涨优先考虑降低学习率或者给该层单独设更小的学习率。技巧二在模型里加梯度裁剪gradient clipping作为兜底手段。不仅在 RNN 里需要长序列 transformer、GAN 训练、多任务模型里同样有用。常见的裁剪阈值在 1.0 到 5.0 之间具体数值需要试。技巧三把 loss 曲线和梯度范数曲线放在同一个画布里观察。Loss 不降不一定有问题梯度范数正常下降说明优化器在正常推进Loss 不降且梯度范数一直在高值徘徊才是需要干预的信号。我建议所有人在正式训练前都花 30 分钟把这三条流程跑一遍。因为“训练翻车”的成本永远比“预防翻车”的成本高得多。5.4 关于 Adam 与 SGD 的实测数据对比顺手把我之前在同一组实验里记录的数据放出来数据集是公开的 AG News 文本分类子集模型统一用两层 BiLSTMAttention超参数完全一致只改优化器和学习率调度。优化器配置学习率收敛到 90% 准确率所需 epoch最终准确率SGD Momentum(0.9)0.019.591.2%SGD Momentum(0.9)0.001未收敛到 90%88.6%Adam0.0017.290.4%Adam CosineAnnealing0.0016.891.0%从这个测试里能看到两个结论和我前面讲的理论完全对应Adam 在同样学习率下收敛更快但最终精度不一定高于 SGD给 Adam 配上余弦退火调度后收敛速度和最终精度都有改善。这说明“好的优化器也要配好的调度策略”两者不是替代关系是互补关系。这个对比实验我建议你也做一次选一个自己熟悉的数据集和模型。因为只有用自己手里的数据去验证你才会真正理解花书里那些曲线图到底在说什么。6. 常见问题实录训练 loss 无法下降的六种典型场景6.1 从“loss 不降”到“loss 变 NaN”的完整现象表训练 deep learning 模型最怕的不是模型复杂、训练慢而是 loss 表现异常却找不到原因。我把近几年遇到过的问题整理成了下面这个速查表放在读书笔记的最后方便随时翻现象特征可能原因验证方法解决方向loss 一直在高位不下降学习率太小或特征未归一化打印梯度范数看是否过小调大学习率检查输入数据归一化loss 前几轮下降后不降了陷入鞍点或局部低谷看梯度范数是否接近零用动量法或 Adam或重启训练loss 震荡剧烈学习率过大或 batch 太小缩小学习率看是否缓解调低学习率或增大 batch sizeloss 突然变成 NaN梯度爆炸或数据含 NaN打印各层梯度范数梯度裁剪降低学习率检查数据训练集 loss 正常验证集 loss 升高过拟合比对训练集和验证集曲线加正则化、Dropout早停loss 一直下降但准确率不升标签错位或损失函数写错抽样检查标签和预测输出检查数据处理、损失函数逻辑这张表是我压箱底的经验汇总每次新项目遇到问题我都会先对照一遍能省下很多盲目调整的时间。6.2 典型场景一loss 打印出来是负数损失函数是负数的情况通常不是因为“训练得很好”而是损失函数本身写错了。一个常见原因是目标函数里带了对数而输入给对数的值是经过某些变换后的概率当概率非法或极小对数输出会变成负的无穷大。另一个常见原因是自定义损失时用了错误的符号比如把“最大化相似度”写成了“最小化相似度”loss 就会一路下探到负值。遇到这种情况我的建议是先打印 loss 的每一项分解逐项检查符号和取值范围。千万别觉得“loss 是负的那我的模型一定学到了很厉害的东西”——这多半是 bug不是惊喜。6.3 典型场景二增加模型深度后 loss 反而更高加了更多层之后训练效果不升反降这是深度学习里一个经典现象。花书里解释了退化问题深层网络在优化上并不天然优于浅层网络。细节原因包括梯度消失、残差连接设计不合理、初始化不匹配等。我遇到过的真实案例把一个两层的 MLP 扩展成十层的 MLP结果在同一个数据集上准确率反而掉了 3 个百分点。排查后发现问题出在初始化——深层网络对初始化范围更敏感如果初始权重过大训练初期梯度就不稳定。改成 He 初始化之后十层 MLP 的准确率才超过两层版本。这类现象特别容易让初学者产生“深度学习靠玄学”的错觉。其实不是玄学每一步都有据可循加层之后要么加残差连接要么调整初始化方案要么加 Batch Normalization总有一项能解决问题。6.4 典型场景三加了正则化后 loss 不降Dropout、权重衰减这些正则化手段在训练初期会让 loss 比不加正则化时更高这是一件正常的事情没必要慌。正则化的本质是牺牲一部分训练集拟合能力换取泛化能力提升。只要训练的曲线整体趋势是下降的最终验证集指标比不加正则化时更好就说明正则化在工作。需要担心的是“加了正则化后训练 loss 在某一步骤突然不再下降验证集指标也不见好”这种情况通常不是正则化的锅而是学习率调度出了问题。比如学习率被调度得太快降到零模型根本没有机会继续学习。检查一下学习率曲线的形状多半能找到原因。7. 写在笔记末尾的一点体会花书第八章节的标题是“Optimization for Training Deep Models”整章读完最大的收获不是记住了多少个优化算法的公式而是建立了“训练过程是可以被拆解和诊断的”这个信念。现在每当我遇到训练不收敛的问题第一反应不是换模型而是踏踏实实检查三件事数据有没有问题、梯度有没有异常、学习率设置是否合理。这三步能解决我遇到过的大概八成问题。如果你也想真正吃透深度学习优化这部分内容我的建议是不要只看书一定要亲手在代码里做几组对比实验。拿同一个模型分别用 SGD、Momentum、Adam 跑一遍记录 loss 曲线再固定优化器调几次学习率看曲线的变化规律。不用多每个配置跑三五个 epoch你就能获得直观看感。这个过程比抱着书啃十遍公式更有用。等这些基础实验做熟了再回去翻花书的第八章你会发现那些公式不再是抽象符号而是对“你亲手观察到的现象”的精确描述。理论与实践就这么对上了。

想做一个「会获客」的企业网站?

留下需求,1 小时内获取专属建站方案与透明报价。

免费咨询方案