
LSTM & GRU —— 门控循环神经网络
系统讲解LSTM与GRU的门控机制设计哲学与数学原理。从LSTM的“双轨”架构(细胞状态+隐藏状态)与三门结构(遗忘门、输入门、输出门)出发,推导细胞状态更新公式如何通过按元素加法路径缓解梯度消失,并深入剖析GRU的两大简化及其参数量优势。附带讨论双向LSTM与深层堆叠LSTM的应用场景。
阅读文章ZHY's Blog
A UNIVERSE OF IDEAS · BY ZHANG HAOYI
让好奇心 点亮知识宇宙
在代码、模型与思想之间自由漫游。这里持续记录人工智能、机器学习、软件工程与成长实践,让每次阅读都成为一次新的发现。
ARTICLE NOTE
在上一篇博客中,我们亲手推导了参数梯度的完整计算公式 —— 从输出层的误差信号 δ(3) 逐层回传至输入层,最终得到了每一层权重与偏置的梯度。至此,模型“学什么”与“梯度怎么算”均已明晰。然而,最后一个关键问题浮出水面:有了梯度,如何用它来更新参数,才能让模型又快又稳地收敛?
本篇博客正是回答这一“最终落地”问题的核心章节。我们从最基础的批量梯度下降(BGD) 出发,审视其全量计算的稳健性与大规模数据下的效率瓶颈;随后引入随机梯度下降(SGD) 与小批量梯度下降(MBGD),揭示“效率与噪声”之间的经典权衡——而这正是优化器演进的起点。
随着对损失景观理解的深入,我们将逐次展开优化器的完整进化图谱:动量法与NAG如何通过惯性积累抑制峡谷震荡;AdaGrad如何为稀疏特征定制参数级学习率;RMSProp如何用指数加权平均解决AdaGrad的学习率消亡问题;以及Adam如何集一阶矩(动量)与二阶矩(自适应学习率)之大成。最后,我们将深入剖析AdamW的核心改进——通过解耦权重衰减,修复了Adam中L2正则化被自适应学习率缩放的隐藏缺陷,使其成为Transformer等现代架构的首选。
值得注意的是,本篇对 优化器“方向与步长” 的深刻理解,将直接服务于后续卷积神经网络中大规模参数的高效训练——当您面对百万级参数时,选择何种优化器将决定模型能否在有限时间内达到最优。现在,请带着“如何用梯度驱动参数走向最低点”的问题进入正文——理解了本篇,您就掌握了让模型真正“学进去”的最后一环。
批量梯度下降(Batch Gradient Descent, BGD) 每次迭代使用全部训练数据来计算损失函数的梯度,然后更新模型参数。
gt=n1∑i=1n∇θℓ(fθt(xi),yi)
θt+1=θt−η⋅gt
其中 n 是样本总数,η 是学习率。
优点:由于使用的是全量数据的真实梯度方向,BGD的收敛曲线相对平滑,对于凸函数能够保证收敛到全局最优解。
缺点:当数据集规模庞大时,每次迭代的计算量巨大,对内存要求高。在动辄百万、千万甚至亿级样本的数据集上,计算一次真实梯度的成本是无法接受的。
随机梯度下降(Stochastic Gradient Descent, SGD) 每次迭代只随机选取一个样本,根据该样本计算梯度并更新参数。
gt=∇θℓ(fθt(xi),yi) θt+1=θt−η⋅gt
优点:每次只计算一个样本的梯度,计算量小、更新速度快,对内存需求低,适合处理大规模数据集和在线学习场景。
缺点:梯度方向波动大,收敛路径曲折。SGD每次只依据一个样本更新,导致收敛过程具有较大的随机性和波动性,可能在最优解附近震荡。
不过,这种随机性也使得SGD有更大机会跳出局部最优解,尤其是在处理非凸函数时——这反而成了它的一个优势。
小批量梯度下降(Mini-Batch Gradient Descent, MBGD) 是BGD和SGD的折中方案——每次迭代使用一小部分样本(一个小批量)来计算梯度和更新参数。
gt=b1∑i=1b∇θℓ(fθt(xi),yi) θt+1=θt−η⋅gt
其中 b 是批量大小(batch size),通常取 16 到 256 之间。
优点:结合了BGD的稳定性和SGD的随机性,通常能更稳定地收敛,且收敛速度比BGD快。同时,小批量的随机性也带来了一定的跳出局部最优的能力。此外,小批量计算可以充分利用GPU的向量化并行计算能力。
实际应用:MBGD在实际的深度学习应用中最为广泛,如图像分类、自然语言处理等领域。
1import numpy as np2
3def gradient_descent(X, y, theta, learning_rate, batch_size, n_epochs, method='mbgd'):4 """梯度下降的三种变体实现"""5 n_samples = X.shape[0]6
7 for epoch in range(n_epochs):8 if method == 'bgd':9 # 批量梯度下降:使用全部数据10 gradients = compute_gradients(X, y, theta)11 theta -= learning_rate * gradients12
13 elif method == 'sgd':14 # 随机梯度下降:每次一个样本15 for i in range(n_samples):16 idx = np.random.randint(n_samples)17 gradients = compute_gradients(X[idx:idx+1], y[idx:idx+1], theta)18 theta -= learning_rate * gradients19
20 elif method == 'mbgd':21 # 小批量梯度下降:打乱数据后分批22 indices = np.random.permutation(n_samples)23 for start in range(0, n_samples, batch_size):24 end = min(start + batch_size, n_samples)25 batch_indices = indices[start:end]26 gradients = compute_gradients(X[batch_indices], y[batch_indices], theta)27 theta -= learning_rate * gradients28
29 return theta| 特性 | BGD | SGD | MBGD |
|---|---|---|---|
| 每次使用的样本数 | 全部 n | 1个 | b 个(16∼256) |
| 梯度准确性 | 最准确 | 噪声最大 | 适中 |
| 收敛速度(每次迭代) | 慢 | 快 | 适中 |
| 收敛稳定性 | 最稳定 | 波动大 | 较稳定 |
| 跳出局部最优能力 | 弱 | 强 | 中等 |
| 计算效率(大规模数据) | 低 | 高 | 高 |
从SGD到MBGD,我们解决了“效率与噪声的权衡”问题。但SGD/MBGD仍然面临一个根本性的挑战:在“峡谷”地形中震荡严重。
标准梯度下降法在参数空间存在 “峡谷”地形(即某一维度梯度远大于另一维度)时,会沿着陡峭方向来回震荡,导致有效前进速度大幅降低。
这种“近视”的更新方式存在两个本质缺陷:
动量法(Momentum) 由Rumelhart等人在1986年提出,其核心思想是引入 “速度”变量来累积历史梯度信息。
我们可以将动量算法视为模拟连续时间下牛顿动力学下的粒子。想象一个在冰面上滑行的冰球——每当它沿着表面最陡的部分下降时,它会积累继续在该方向上滑行的速度。负梯度是推动粒子的力,速度是粒子的动量。
动量法的更新规则分为两步:
vt+1=γvt+η∇θJ(θt)
θt+1=θt−vt+1
其中 γ∈(0,1) 是动量系数(通常取 0.9),控制历史信息的衰减速率。
从展开式可以更清楚地看到动量的“记忆”本质:
vt=η∑i=1tγt−i∇θJ(θi)
速度 vt 是所有历史梯度的指数加权移动平均——近期梯度权重更大,但长期趋势也被保留。
动量系数的物理意义:如果将动量系数 γ=0.9,则最大速度可以达到纯梯度下降的 1/(1−γ)=10 倍。这意味着在梯度方向一致的维度上,动量可以积累动能实现加速;而在梯度方向频繁变化的维度上,动量效应会抵消震荡分量。
1def sgd_with_momentum(theta, grad, v, lr=0.01, momentum=0.9):2 """带动量的SGD更新"""3 v = momentum * v + lr * grad4 theta = theta - v5 return theta, v6
7# 模拟在峡谷地形中的对比8# 纯SGD会在陡峭方向来回震荡,而动量法能平滑震荡、加速前进Nesterov加速梯度(Nesterov Accelerated Gradient, NAG) 是动量的一个改进版本。
两者的关键区别在于计算梯度的位置:
NAG的更新规则:
vt+1=γvt+η∇θJ(θt+γvt)
θt+1=θt−vt+1
物理直觉:标准动量像一个滑雪者,每次到弯道才根据当前坡度调整方向;而Nesterov动量像一个有前瞻性的滑雪者——他先“看向”前方(预期位置),根据前方的坡度预先调整方向。
这种“前瞻”机制使得NAG在理论上具有更快的收敛速度,其收敛速率可以达到 O(1/t2),而标准梯度下降仅为 O(1/t)。
尽管动量和NAG解决了方向的问题,但所有参数仍然共享同一个全局学习率。这带来了新的问题:
AdaGrad(Adaptive Gradient) 由Duchi等人在2011年提出,首次引入了参数级学习率调整的机制。
AdaGrad的核心思想是:为每个参数维护一个累积梯度平方和,用其开方来缩放当前梯度。
Gt=Gt−1+gt2(逐元素平方和)
θt+1=θt−Gt+ϵη⊙gt
其中 ⊙ 表示逐元素乘法,ϵ 是一个防止除零的小常数。
直观理解:
致命缺陷:Gt 是所有历史梯度平方的无衰减累加。随着训练进行,Gt 不断增大,学习率单调递减,最终趋近于零——模型将停止学习。
RMSProp由Geoff Hinton在2012年的课程笔记中提出,其核心改进是用指数加权移动平均替代累积平方和。
E[g2]t=βE[g2]t−1+(1−β)gt2
θt+1=θt−E[g2]t+ϵη⊙gt
其中 β 通常取 0.9 或 0.99。
关键改进:指数加权平均使得远距离的历史梯度信息被逐渐“遗忘” 。学习率不再单调递减到零,而是能够根据近期梯度的变化动态调整。
1def rmsprop(theta, grad, cache, lr=0.001, beta=0.9, epsilon=1e-8):2 """RMSProp更新"""3 cache = beta * cache + (1 - beta) * grad**24 theta = theta - lr * grad / (np.sqrt(cache) + epsilon)5 return theta, cacheAdam(Adaptive Moment Estimation) 由Kingma和Ba在2015年提出,它融合了动量法(一阶矩)和RMSProp(二阶矩)的思想。
Adam同时维护两个变量:
一阶矩估计 mt (动量的角色):梯度的指数加权平均 mt=β1mt−1+(1−β1)gt
二阶矩估计 vt (自适应学习率的角色):梯度平方的指数加权平均 vt=β2vt−1+(1−β2)gt2
在训练初期,mt 和 vt 被初始化为 0,导致估计值严重偏小。Adam通过偏差修正来解决这个问题:
m^t=1−β1tmt,v^t=1−β2tvt
最终的参数更新为:
θt+1=θt−v^t+ϵη⊙m^t
Adam的默认超参数:β1=0.9,β2=0.999,ϵ=10−8。这些默认值在大多数任务中表现稳定,这也是Adam广受欢迎的原因之一。
1def adam(theta, grad, m, v, t, lr=0.001, beta1=0.9, beta2=0.999, epsilon=1e-8):2 """Adam优化器更新"""3 # 更新一阶矩和二阶矩4 m = beta1 * m + (1 - beta1) * grad5 v = beta2 * v + (1 - beta2) * grad**26
7 # 偏差修正8 m_hat = m / (1 - beta1**t)9 v_hat = v / (1 - beta2**t)10
11 # 参数更新12 theta = theta - lr * m_hat / (np.sqrt(v_hat) + epsilon)13 return theta, m, vAdam虽然强大,但在泛化能力上常常不如带动量的SGD。研究发现,问题出在Adam对权重衰减(Weight Decay) 的处理上。
在标准Adam中,L2正则化(权重衰减)是通过在梯度中直接加上 λθ 项来实现的:
gt=∇θJ(θt)+λθt
问题在于:这个 λθt 项也会被 vt(梯度平方的指数加权平均)缩放。
具体来说,Adam的参数更新为:
θt+1=θt−vt+ϵη⋅(mt+λθt)
权重衰减项 λθt 除以了 vt ——这意味着:
这种耦合导致L2正则化在Adam中无法起到预期的效果。
AdamW由Loshchilov和Hutter在2017年提出,其核心思想非常直接:将权重衰减从自适应更新中解耦出来。
在AdamW中,权重衰减不经过 vt 的缩放,而是在参数更新之后独立施加:
Step 1:正常的Adam更新(不包含权重衰减) θt+1′=θt−v^t+ϵη⊙m^t
Step 2:独立施加权重衰减 θt+1=θt+1′−ηλθt+1′
关键差异:
1def adamw(theta, grad, m, v, t, lr=0.001, beta1=0.9, beta2=0.999,2 epsilon=1e-8, weight_decay=0.01):3 """AdamW优化器更新"""4 # 更新一阶矩和二阶矩(与Adam相同)5 m = beta1 * m + (1 - beta1) * grad6 v = beta2 * v + (1 - beta2) * grad**27
8 # 偏差修正9 m_hat = m / (1 - beta1**t)10 v_hat = v / (1 - beta2**t)11
12 # 参数更新(Adam风格,不含权重衰减)13 theta = theta - lr * m_hat / (np.sqrt(v_hat) + epsilon)14
15 # 解耦的权重衰减(关键区别!)16 theta = theta - lr * weight_decay * theta17
18 return theta, m, v更有效的正则化:由于权重衰减不再被自适应学习率缩放,它对所有参数施加一致且可预测的惩罚力度。
更好的泛化性能:实验表明,AdamW在大型Transformer架构(如BERT、GPT)上取得了显著的性能提升。
更稳定的训练动态:解耦使得训练过程更加可预测和稳定,这对大规模模型尤为重要。
实际应用:目前在自然语言处理(NLP)领域,AdamW已成为事实上的标准优化器。在计算机视觉(CV)领域,带动量的SGD仍然广泛使用。
回顾整条进化路径,每一个优化器都是对前代特定痛点的精准回应:
| 优化器 | 核心贡献 | 解决的问题 |
|---|---|---|
| BGD | 全量数据梯度 | 基础框架 |
| SGD | 单样本梯度 | 计算效率 |
| MBGD | 小批量梯度 | 效率与稳定性的平衡 |
| Momentum | 速度变量 + 指数移动平均 | 峡谷地形的震荡 |
| NAG | 前瞻梯度计算 | 动量方向的“预见性” |
| AdaGrad | 参数级学习率 | 稀疏特征的处理 |
| RMSProp | 指数加权平均(二阶矩) | AdaGrad的学习率消亡 |
| Adam | 一阶矩 + 二阶矩 + 偏差修正 | 集动量与自适应于一身 |
| AdamW | 解耦权重衰减 | Adam中正则化失效 |
这条进化路线清晰地展示了深度学习中“问题驱动”的创新模式:每一个新算法的诞生,都是为了解决前一代在特定场景下的不足。
在实际选择优化器时,可以参考以下经验:
理解每种优化器背后的数学原理与设计动机,远比记住调参公式更重要——它能帮助你在面对新问题时,做出更明智的选择。
按顺序完成这组文章,循序渐进地掌握主题
发现错误、内容过时或有改进想法?欢迎告诉我
根据本文分类与标签,为你推荐可能感兴趣的内容

系统讲解LSTM与GRU的门控机制设计哲学与数学原理。从LSTM的“双轨”架构(细胞状态+隐藏状态)与三门结构(遗忘门、输入门、输出门)出发,推导细胞状态更新公式如何通过按元素加法路径缓解梯度消失,并深入剖析GRU的两大简化及其参数量优势。附带讨论双向LSTM与深层堆叠LSTM的应用场景。
阅读文章
系统讲解循环神经网络(RNN)的数学原理与随时间反向传播(BPTT)算法。从RNN的循环结构与共享参数出发,推导BPTT的梯度表达式,揭示梯度消失与梯度爆炸的数学根源,并介绍梯度裁剪作为应对梯度爆炸的工程解法。附带讨论LSTM如何通过门控机制缓解梯度消失。
阅读文章
系统讲解ResNet残差网络的设计哲学与数学原理:从退化问题的本质出发,推导残差块F(x)+x如何通过恒等映射直通路径缓解梯度消失,详解Bottleneck块如何将参数量减少约94%,并对比ResNet(加法融合)与DenseNet(拼接融合)的梯度流动差异。
阅读文章请使用微信扫描二维码分享
当前文章会保持在原页面