【硬核数学】2.4 驯服“梯度下降”:深度学习中的优化艺术与正则化技巧《从零构建机器学习、深度学习到LLM的数学认知》
欢迎来到本系列的第九篇文章。我们已经知道,训练深度学习模型的核心循环是:前向传播计算损失,反向传播计算梯度,然后用梯度更新参数。这个“用梯度更新参数”的步骤,正是由优化器 (Optimizer) 来完成的。
在微积分的章节中,我们学习了最基础的优化算法——梯度下降(GD)。它的思想简单而纯粹:计算整个数据集上的损失,得到一个“真实”的梯度,然后沿着这个梯度的反方向前进一步。这就像拥有了一张整个山脉的精确地貌图,然后朝着最陡峭的下坡方向走。
但在深度学习的时代,这张“地图”(数据集)可能有数十亿个像素(ImageNet)或数万亿个单词(用于训练LLM的语料库)。要求计算机一次性读取整张地图来决定仅有的一小步,是完全不可行的。更何况,深度学习的损失地貌(Loss Landscape)远比我们想象的要复杂和险恶。
因此,深度学习的优化,是一门在“信息不完全”和“地形险恶”双重挑战下的“寻路艺术”。本文将带你领略这门艺术的演进过程,从最朴素的随机梯度下降,到统治当今业界的Adam优化器,并揭示那些让模型训练从“不可能”变为“可能”的关键技术。
第一部分:从蹒跚学步到踉跄前行 —— 随机梯度下降(SGD)
面对无法一次性处理整个数据集的困境,科学家们提出了一个简单而天才的想法:如果不能看全图,那就每次只看地图的一小块,然后大致估摸一个方向走一步。这就是随机梯度下降 (Stochastic Gradient Descent, SGD) 的核心思想。
批量梯度下降 vs. 随机梯度下降
-
批量梯度下降 (Batch Gradient Descent, BGD):这是我们之前讨论的标准GD。它在每次更新前,都会计算整个训练集的损失和梯度。
- 优点:梯度是“真实”的,下降路径平滑且直指最优。
- 缺点:计算成本极高,对于大规模数据集无法接受;容易陷入尖锐的局部最小值。
-
随机梯度下降 (SGD):在最极端的情况下,它每次更新只随机抽取一个样本来计算损失和梯度。在更常见的实践中,我们使用一个折中的方案,即小批量随机梯度下降 (Mini-batch SGD),每次使用一小批(例如32、64或128个)样本。在深度学习语境下,我们通常说的SGD,指的就是Mini-batch SGD。
SGD的参数更新公式为:
θ = θ − η ⋅ ∇ θ L ( θ ; x ( i : i + n ) ; y ( i : i + n ) ) \theta = \theta - \eta \cdot \nabla_{\theta} L(\theta; \mathbf{x}^{(i:i+n)}; \mathbf{y}^{(i:i+n)}) θ=θ−η⋅∇θL(θ;x(i:i+n);y(i:i+n))
其中, η \eta η 是学习率,梯度 ∇ θ L \nabla_{\theta} L ∇θL 是基于一小批样本(从索引 i i i 到 i + n i+n i+n)计算的。
SGD的“随机”之舞
这个用小批量梯度估算出的梯度,是对真实梯度的一个有噪声的近似。因此,SGD的下降路径不再是平滑的,而是充满了噪声和震荡,像一个踉踉跄跄的醉汉在下山。
这张图直观地对比了BGD和SGD的路径差异。BGD像一个专业的登山家,路线精准。SGD则像一个业余爱好者,走走停停,方向摇摆,但大体上也在向山下前进。
这种“随机性”既是缺点也是优点:
- 缺点:收敛速度慢,需要更多的迭代次数。由于梯度噪声,它永远不会精确地收敛到最优点,而是在最优点附近徘徊。
- 优点:噪声有时能帮助优化器“跳出”尖锐的、不好的局部最小值,或者更快地滑过鞍点,从而有可能找到一个更平坦、泛化能力更好的最小值区域。
SGD面临的挑战
尽管SGD是深度学习优化的基石,但它本身存在几个严重的问题,使得它在面对复杂的损失地貌时举步维艰:
- 峡谷/病态曲率问题:损失地貌常常呈现出“峡谷”形状——在某个方向上非常陡峭,而在另一个方向上非常平缓。SGD在这种地形中会表现得非常糟糕,它会在陡峭的峡谷壁之间来回震荡,而在平缓的谷底方向上进展缓慢。
- 鞍点问题:在高维空间中,局部最小值其实相对较少,而鞍点 (Saddle Points)(在某些维度上是最大值,在另一些维度上是最小值)则无处不在。在鞍点附近,梯度非常小,几乎为零,SGD很容易在这里“卡住”,停滞不前。
- 统一学习率的困境:对所有参数使用同一个学习率 η \eta η 是不合理的。有些参数可能需要大步前进,有些则需要小步微调。固定的学习率难以适应这种情况。
为了解决这些问题,一系列更先进的优化器被提了出来。它们的核心思想,都是在SGD的基础上,增加一些“智能”的机制。
第二部分:智能导航的进化 —— 从动量到Adam
为了克服SGD的种种缺陷,研究者们从物理学和信号处理中汲取灵感,开发出了一系列改进版的优化器。
动量 (Momentum):冲过山谷和高原的“重球”
为了解决SGD在峡谷中震荡和在鞍点上停滞的问题,动量法被引入。它的核心思想是,在更新参数时,不仅考虑当前的梯度,还累积过去梯度的方向,形成一个“动量”或“速度”。
想象一个重球从山上滚下。它不仅受到当前地势(梯度)的影响,还因为自身的惯性(动量)而保持着之前的运动趋势。这使得它:
- 在峡谷中,垂直于谷底方向的梯度会因为来回震荡而相互抵消,而沿着谷底方向的梯度则会持续累积,从而加速小球在谷底的前进。
- 在鞍点或平坦区域,即使当前梯度很小,小球依然可以凭借之前积累的动量“冲”过去。
动量法的更新规则如下:
v t = β v t − 1 + η ∇ θ L ( θ ) θ = θ − v t \mathbf{v}_t = \beta \mathbf{v}_{t-1} + \eta \nabla_{\theta} L(\theta) \\ \theta = \theta - \mathbf{v}_t vt=βvt−1+η∇θL(θ)θ=θ−vt
- v t \mathbf{v}_t vt 是在时间步 t t t 的“速度”向量。
- β \beta β 是动量系数(通常设为0.9),它控制着“摩擦力”的大小。 β \beta β 越大,过去的梯度对当前方向的影响就越大。
这张图清晰地展示了动量法如何通过平滑梯度更新,来抑制震荡并加速收敛。
自适应学习率:为每个参数量身定制步伐
动量法解决了方向问题,但学习率问题依然存在。自适应学习率方法的核心思想是:为网络中的每一个参数,都独立地维护一个学习率。
RMSProp (Root Mean Square Propagation) 是其中的一个代表。它通过记录每个参数梯度的平方的移动平均值,来调整其学习率。
更新规则的核心思想:
E [ g 2 ] t = γ E [ g 2 ] t − 1 + ( 1 − γ ) ( ∇ θ L ) 2 θ = θ − η E [ g 2 ] t + ϵ ∇ θ L E[g^2]_t = \gamma E[g^2]_{t-1} + (1-\gamma) (\nabla_{\theta} L)^2 \\ \theta = \theta - \frac{\eta}{\sqrt{E[g^2]_t + \epsilon}} \nabla_{\theta} L E[g2]t=γE[g2]t−1+(1−γ)(∇θL)2θ=θ−E[g2]t+ϵη∇θL
- E [ g 2 ] t E[g^2]_t E[g2]t 是梯度平方的移动平均值。
- γ \gamma γ 是衰减率(通常设为0.999)。
- ϵ \epsilon ϵ 是一个极小的数(如 1 0 − 8 10^{-8} 10−8),防止分母为零。
直观理解:如果某个参数的梯度一直很大,那么 E [ g 2 ] t E[g^2]_t E[g2]t 就会很大,导致其有效学习率 η E [ g 2 ] t + ϵ \frac{\eta}{\sqrt{E[g^2]_t + \epsilon}} E[g2]t+ϵη 变小,从而起到“刹车”作用。反之,如果某个参数的梯度很小,其有效学习率就会变大,起到“助推”作用。这使得RMSProp在处理不同尺度的梯度时非常有效。
Adam:集大成者,当今的王者
Adam (Adaptive Moment Estimation) 优化器,是目前深度学习领域应用最广泛、最成功的优化器之一。它的名字已经揭示了其本质:它结合了动量法(一阶矩估计)和RMSProp(二阶矩估计)的思想。
Adam为每个参数维护了两个移动平均值:
- m t m_t mt:梯度的一阶矩(均值)的指数移动平均,就像动量。
- v t v_t vt:梯度平方的二阶矩(方差)的指数移动平均,就像RMSProp。
完整的更新规则如下:
m t = β 1 m t − 1 + ( 1 − β 1 ) g t v t = β 2 v t − 1 + ( 1 − β 2 ) g t 2 m_t = \beta_1 m_{t-1} + (1-\beta_1) g_t \\ v_t = \beta_2 v_{t-1} + (1-\beta_2) g_t^2 mt=β1mt−1+(1−β1)gtvt=β2vt−1+(1−β2)gt2
由于 m t m_t mt 和 v t v_t vt 在初始时被初始化为0,它们在训练早期会偏向于0。因此,Adam引入了偏差修正 (Bias Correction):
m ^ t = m t 1 − β 1 t v ^ t = v t 1 − β 2 t \hat{m}_t = \frac{m_t}{1 - \beta_1^t} \\ \hat{v}_t = \frac{v_t}{1 - \beta_2^t} m^t=1−β1tmtv^t=1−β2tvt
最终的参数更新:
θ t + 1 = θ t − η v ^ t + ϵ m ^ t \theta_{t+1} = \theta_t - \frac{\eta}{\sqrt{\hat{v}_t} + \epsilon} \hat{m}_t θt+1=θt−v^t+ϵηm^t
Adam几乎结合了之前所有优化器的优点:它像动量法一样能累积速度冲过平坦区域,又像RMSProp一样能为每个参数自适应地调整学习率。这使得它在各种不同的模型和任务上都表现得非常鲁棒,通常只需要很少的超参数调整就能获得不错的效果。
第三部分:防止“走火入魔” —— 正则化与梯度控制
拥有了像Adam这样的强大优化器,我们就能在损失地貌上飞速前进了。但“快”并不总是好事。我们可能会因为模型过于强大而“用力过猛”,导致过拟合 (Overfitting);也可能因为网络过深,导致梯度在传播过程中“失控”。
正则化:给模型套上“缰绳”
正则化 (Regularization) 是任何用于避免过拟合、提升模型泛化能力的技术的总称。其核心思想是在优化原始损失函数的同时,对模型的复杂度进行惩罚。
-
L1/L2 正则化 (Weight Decay):这是最经典的正则化方法。我们在损失函数中加入一个与模型权重大小相关的项。
- L2正则化: L t o t a l = L o r i g i n a l + λ 2 ∑ i w i 2 L_{total} = L_{original} + \frac{\lambda}{2} \sum_i w_i^2 Ltotal=Loriginal+2λ∑iwi2。它惩罚大的权重,倾向于让所有权重都变得比较小,使得模型更“平滑”。在优化器中,这等价于权重衰减 (Weight Decay)。
- L1正则化: L t o t a l = L o r i g i n a l + λ ∑ i ∣ w i ∣ L_{total} = L_{original} + \lambda \sum_i |w_i| Ltotal=Loriginal+λ∑i∣wi∣。它倾向于让一些不重要的权重直接变为0,从而实现稀疏化和特征选择。
-
Dropout:随机失活的“集体智慧”
Dropout是深度学习中一种极其强大且独特的正则化技术。它的思想非常巧妙:在训练过程的每一步,都随机地“丢弃”(暂时忽略)网络中的一部分神经元。
上图展示了Dropout的作用。在右侧的网络中,第二个隐藏神经元被随机“丢弃”了。这意味着在本次前向和反向传播中,所有与它相关的连接都暂时失效。
- 工作原理:在每个训练批次,都相当于在训练一个不同的、“变瘦”了的子网络。这强迫网络不能过度依赖于任何一个神经元的特定特征,因为这个神经元随时可能“消失”。网络必须学习到更鲁棒、更分散的特征表示。
- 效果:Dropout可以被看作是一种高效的模型集成 (Model Ensemble) 方法。它在训练时仿佛训练了 2 N 2^N 2N 个(N为神经元数量)共享权重的子网络,在测试时则通过一种近似的方式将这些子网络的预测平均起来,从而获得极佳的正则化效果。
驯服梯度:处理梯度消失与爆炸
在非常深的网络中(尤其是循环神经网络RNN),梯度在反向传播经过许多层后,可能会出现两个极端问题:
- 梯度消失 (Vanishing Gradients):梯度值变得极其微小,接近于0。这导致靠近输入的层几乎无法接收到梯度信号,学习停滞。这在使用Sigmoid或Tanh等饱和激活函数时尤其严重。
- 梯度爆炸 (Exploding Gradients):梯度值变得异常巨大,导致参数更新过猛,模型权重变得混乱,甚至出现
NaN
(非数值)错误,训练崩溃。
解决方案:
- 明智的权重初始化:如Xavier/Glorot初始化或He初始化,它们根据层的输入输出维度来设定权重的初始方差,旨在让信号(和梯度)在网络中传播时,其方差能保持稳定。
- 使用非饱和激活函数:ReLU (Rectified Linear Unit) 及其变种(Leaky ReLU, PReLU等)的出现是深度学习的一大突破。当输入大于0时,ReLU的导数恒为1,这极大地缓解了梯度消失问题,使得训练非常深的网络成为可能。
- 批量归一化 (Batch Normalization):在每一层的激活函数之前,对该层的输出进行归一化(使其均值为0,方差为1),然后再进行缩放和平移。这强制稳定了每一层输入的分布,像是在网络的“高速公路”上设置了许多“服务区”,让数据流和梯度流都更加稳定,极大地加速了收敛并缓解了梯度问题。
- 梯度裁剪 (Gradient Clipping):这是专门针对梯度爆炸的“简单粗暴”但极其有效的方法。在参数更新前,检查梯度的范数(大小)。如果梯度的范数超过了一个预设的阈值,就按比例将其缩放回阈值大小。这就像给参数更新的速度设置了一个“上限”,防止其因梯度爆炸而失控。
融会贯通:深度学习训练的“仪表盘”
今天,我们为我们的优化理论知识进行了全面的深度学习升级。我们看到,训练一个庞大的深度模型,远不止是调用optimizer.step()
那么简单。它更像是在驾驶一架复杂的飞机:
- SGD、Momentum、Adam 是我们的引擎和导航系统,决定了我们前进的动力和方向。Adam通常是默认的最佳选择。
- 学习率 (Learning Rate) 是我们的油门。我们还需要学习率调度器 (Learning Rate Scheduler) 来在飞行过程中智能地调节油门(例如,在训练后期逐渐降低学习率进行微调)。
- L2正则化、Dropout 是我们的飞行稳定系统,防止飞机因速度过快或气流颠簸而偏离航道(过拟合)。
- ReLU、批量归一化、梯度裁剪 则是我们的安全系统和仪表盘,时刻监控着飞机的状态(梯度流),防止引擎熄火(梯度消失)或空中解体(梯度爆炸)。
掌握这些优化器和技术,并理解它们背后的原理,是每一个AI工程师从“能用”到“会用”再到“用好”深度学习模型的必经之路。至此,我们已经深入探讨了深度学习在核心数学领域的各项升级。接下来,我们将转向一些更具体的理论,如函数逼近理论,来回答一个终极问题:神经网络为什么如此强大?它到底能做什么,不能做什么?
习题
第1题:优化器选择
在一个深度学习项目中,你发现模型的训练损失在初期下降很快,但随后就在一个较高的值附近剧烈震荡,无法进一步收敛。相比于普通的SGD,改用Adam优化器可能会带来什么改善?请解释Adam中的哪个机制主要解决了这个问题。
第2题:Dropout的作用
在神经网络进行**推理(或测试)**时,为什么通常需要关闭Dropout层,或者对其进行调整?如果继续使用训练时的Dropout行为,会对预测结果产生什么影响?
第3题:梯度问题诊断
你正在训练一个非常深的循环神经网络(RNN)用于文本生成。训练了几轮后,损失突然变成了NaN
。这最可能是什么问题导致的?你会优先考虑采用哪种技术来解决这个问题?
答案
第1. 答案:
- 可能带来的改善:改用Adam优化器很可能会让损失的震荡减小,并帮助模型收敛到更低的值。
- 主要机制:Adam中主要解决这个问题的机制是自适应学习率(继承自RMSProp的部分)。损失在一个较高的值附近剧烈震荡,通常意味着优化器在某些方向上“步子迈得太大”,导致在峡谷状的地貌中来回“撞墙”。Adam通过计算梯度平方的移动平均值( v t v_t vt),能够识别出这些梯度较大的方向,并自动地为这些方向上的参数减小有效学习率。这种对每个参数“量身定制”的步长调整,能够有效地抑制震荡,使优化过程更稳定,从而能够沿着平缓的“谷底”方向继续前进。
第2. 答案:
- 为什么需要关闭或调整:Dropout的核心是引入随机性来进行正则化。在推理时,我们的目标是得到一个确定的、稳定的预测结果,而不是一个随机的结果。我们希望利用整个网络学到的所有知识,而不是一个随机的子网络。
- 继续使用的影响:如果在推理时继续使用训练时的Dropout行为,每次对同一个输入进行预测,都会因为随机丢弃不同的神经元而得到一个不同的输出。这使得模型的预测结果变得不确定和不可复现,这是在生产环境中无法接受的。
- 正确的做法:在推理时,通常有两种做法,它们是等价的:1) 直接关闭Dropout层,让所有神经元都参与计算。2) 不关闭Dropout,但将每一层(应用了Dropout的层)的输出乘以
(1 - p)
,其中p
是训练时使用的丢弃概率。这个缩放操作是为了保证该层输出的期望值与训练时保持一致。现代深度学习框架会自动处理好这一点。
第3. 答案:
- 最可能的问题:梯度爆炸 (Exploding Gradients)。在RNN中,梯度需要通过时间步进行长距离的反向传播,很容易因为重复乘以大于1的数而导致梯度值呈指数级增长,最终超出浮点数的表示范围,变成
NaN
。 - 优先采用的技术:梯度裁剪 (Gradient Clipping)。这是解决梯度爆炸最直接、最有效的方法。通过设置一个阈值,在梯度更新前,将所有超过该阈值的梯度范数强制缩放回阈值大小,可以从根本上防止因梯度过大导致的数值不稳定和训练崩溃。