目录
梯度下降
梯度下降(Gradient Descent)是一种用于找到函数最小值的优化算法,它的思路是沿着函数梯度的反方向进行迭代,从而不断地逼近函数的最小值。在机器学习和深度学习中,梯度下降被广泛应用于训练模型的参数,使得损失函数最小化。
梯度下降的公式如下:
其中:
- θt 是当前时刻的参数向量
- J(θt) 是需要被最小化的目标函数(如损失函数)
- ∇θJ(θt) 是目标函数关于参数 θ 的梯度(一般∇符号表示梯度)
- η 是学习率(learning rate),是一个正的常数
这里只是简单的提及,网上相关资料有很多想学习还请自行搜索(毕竟我们这次的主题是优化器哈哈)
SGD
SGD(Stochastic Gradient Descent, 随机梯度下降)是梯度下降算法的一种变体,它在每次迭代时只使用一个数据样本(或一小批数据样本)来计算梯度,而不是使用整个训练数据集。
SGD的公式如下:
其中:
- x^(i) 是训练数据集中的第i个样本
- η∇θJ(θt;x(i)) 是损失函数关于当前参数 θt 在样本 x^(i) 上的梯度
是不是好像明白了,但是仔细一想感觉思路还不是很清晰?没关系,我们接下来用一个例子来说明SGD的细节。
假设我们想要基于房屋面积(x)来预测房屋价格(y),使用线性模型:
y = θ0 + θ1 * x
其中:
- x 是房屋面积,是模型的输入变量
- y 是房屋价格,是模型需要预测的目标变量
- θ0是模型的偏置参数(bias)
- θ1是模型的权重参数,表示面积每增加一单位,价格的变化率
我们有一个训练数据集D,包含N个房屋样本:
D = {(x^(1), y^(1)), (x^(2), y^(2)), ..., (x^(N), y^(N))}其中:
- x^(i)是第i个房屋的面积
- y^(i)是第i个房屋的实际价格
我们的目标是找到最优的θ0和θ1,使得对于任意一个面积x,我们的模型y = θ0 + θ1 * x能够很好地预测出对应的房屋价格y。
在随机梯度下降(SGD)中,每一次迭代我们随机采样一个训练样本(x^(i), y^(i)),计算该样本的损失函数关于θ0和θ1的梯度,然后更新θ0和θ1的值。
具体来说,在第t次迭代中:
- 随机选取一个训练样本(x^(i), y^(i)) (注意!这就是Stochastic的体现之处!随机选择xy,而不是传统的全部xy)
- 计算该样本的预测值 y_pred = θ0 + θ1 * x^(i)
- 计算损失函数(如均方误差) J = (y^(i) - y_pred)^2
- 计算J关于θ0和θ1的梯度:
∇J(θ0) = -2 * (y^(i) - y_pred)
∇J(θ1) = -2 * (y^(i) - y_pred) * x^(i)- 根据学习率η,更新θ0和θ1:
θ0 = θ0 - η * ∇J(θ0)
θ1 = θ1 - η * ∇J(θ1)重复上述步骤,直到收敛或满足停止条件。
通过这个例子,我们可以看到x^(i)和y^(i)分别是训练数据样本的输入和目标值,而θ0和θ1是我们要学习的模型参数。SGD通过不断地在训练数据上迭代,更新模型参数θ,来找到最优解。
import torch.optim as optim
optimizer = optim.SGD(model.parameters(), lr=0.01)
Momentum动量法
传统的梯度下降法在更新参数时只考虑了当前梯度的方向,而增加Momentum的梯度下降法则引入了一个"动量"的概念,使参数更新时不仅考虑了当前梯度方向,还考虑了之前的"速度"或"动量"。
Momentum(动量)的概念借鉴自物理学,特别是在描述物体运动时,动量是质量和速度的乘积,用于描述物体运动的“惯性”。在梯度下降中引入Momentum的想法,是为了模拟这种惯性效果,使参数更新时能够在一定程度上保留之前更新的方向和速度,从而加速学习过程,同时帮助克服优化过程中可能遇到的一些问题,如摆脱局部最小值或横跨平坦区域。
其中:
- θt 为当前时刻的参数
- vt 为当前时刻的动量向量
- γ 为动量参数,通常取0.9这个经验值
- η 为学习率
- ∇θJ(θt−1) 为损失函数关于参数的梯度
对比普通梯度下降:
可以看出,普通梯度下降直接使用当前梯度更新参数,而Momentum则融合了历史梯度信息。
对比SGD:
SGD是对单个数据样本计算梯度,可看作是数据级的随机噪声。而Momentum也引入了一种更新过程的扰动,具有类似SGD部分噪声避免陷入局部最优的作用。
Momentum方法的优点:
- 指数加权融合了历史梯度,平滑了更新方向
- 加速了梯度下降过程,可以跳出局部最优解
- 在一定程度上抑制了振荡和oscilation
缺点:
- 动量参数γ需要调优,对参数比较敏感
- 在较平坦区域可能导致越过最优解
后续的优化算法如RMSProp、Adam等也融合了动量思想,并在此基础上做了改进。
总之,Momentum是梯度下降法的一种加速优化方式,通过融合历史梯度信息平滑更新方向、加快收敛,且具有一定噪声避免陷入局部最优的优点。合理使用时能够显著提升模型训练效率。
optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.9)
NAG好像也被称为牛顿法
NAG(Nesterov Accelerated Gradient)是Nesterov提出的一种加速梯度下降的变体,相比经典的动量法,它在计算梯度时更看重当前的动量方向,从而获得了更好的加速效果。
NAG的更新公式如下:
与经典动量法的区别在于,NAG在计算梯度 η∇θJ 时,使用的参数值为 θ{t-1} - γv{t-1},即在当前参数值的基础上,先沿当前动量方向移动一步,再评估该位置的梯度。
这样做相当于能够让算法提前看到前方的地形梯度,如果前面的梯度比当前位置的梯度大,那我就可以把步子迈得比原来大一些,如果前面的梯度比现在的梯度小,那我就可以把步子迈得小一些。
但当我们讲上式展开,则会发现存在二阶梯度的影响 (参考文章)。所以相当于牛顿法因为考虑了二阶导所以才可以更快的收敛。
NAG方法的优点:
- 加速效果更好,比经典动量法收敛更快
- 结合了当前动量方向的信息,更贴近实际最优解的下降路径
缺点:
- 计算开销较大,需要多次计算损失函数梯度
- 动量参数γ设置不当,可能会越过最优解
NAG被认为是经典动量法的改进和升华版本。诸如Adam等现代优化算法也借鉴了NAG思想,即在更新梯度时考虑当前动量方向,以获得更快更好的收敛性能。
在实际应用中,NAG相比经典动量法收敛更快,是最常用的加速梯度下降方法之一,尤其在非平滑的错误面上表现出色。
参考文章(强烈建议去读):
比Momentum更快:揭开Nesterov Accelerated Gradient的真面目 - 知乎 (zhihu.com)
#PyTorch内置的动量优化器默认实现了NAG变体,只需设置nesterov=True即可:
optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.9, nesterov=True)
Adagrad 自适应学习率优化算法
Adagrad(Adaptive Gradient Algorithm)的核心思想是:为不同的参数适应性地分配不同的学习率,使学习率随着训练时间的推移而自适应地衰减。对于较高梯度的参数,学习率下降较快;对于较低梯度的参数,学习率下降较慢。
对学习率进行优化操作
- gt 是当前时间步的梯度
- rt 是截至当前时间步的所有梯度平方的累加和
- η 是初始学习率
- ϵ 是一个很小的平滑项,防止分母为0
对比梯度下降:
仔细看其实就是多了个 1/sqrt(r_t+ε) .
此外,Adagrad公式中分母的平滑项 ϵ 也非常小,主要是为了数值稳定性,确保分母不为0。
可以看到Adagrad算法对梯度下降的改进就是增加了rt这部分。但你知道为什么是η/sqrt(r_t+ε)这种形式吗?
主要原因有两点:
- 自适应学习率
在Adagrad和RMSProp等算法中,r_t实际上是对每个参数维度的历史梯度平方的累积(Adagrad)或指数加权累积(RMSProp)。较大的r_t意味着该维度的梯度值较大,参数在该方向上可能有较大的振荡,因此需要较小的学习率以保证稳定性。
相反,较小的r_t意味着该维度的梯度较小,参数在该方向上的更新量可以相对较大一些。因此,η/sqrt(r_t+ε)实现了对不同参数维度自适应的学习率,避免了单一的全局学习率带来的问题。
- 数值稳定性
将学习率η除以sqrt(r_t+ε)还有一个作用是防止分母为0导致数值不稳定。ε是一个很小的正数,确保分母不会为0。
此外,开平方根的作用是约束学习率的数量级,使其呈现一种下降的趋势,有利于算法收敛。如果直接除以r_t,学习率会下降过快,收敛性能可能会变差。
综上所述,η/sqrt(r_t+ε)提供了自适应学习率和数值稳定性的优点,是Adagrad/RMSProp等优化算法中一种常见且理论合理的设计选择。通过调节η的初始值和ε,可以控制整体学习率的下降速率。
optimizer = optim.Adagrad(model.parameters(), lr=0.01)
RMSProp
RMSProp(Root Mean Square Propagation)是一种自适应学习率的优化算法,由Geoff Hinton提出。RMSProp是为了解决梯度下降算法在训练深度神经网络时遇到的一些问题,特别是在处理非稳定目标函数时的效率问题。RMSProp通过调整每个参数的学习率来尝试减少梯度下降的震荡,使得训练过程更加稳定和快速。
在Adagrad的基础上进一步在学习率的方向上进行优化:
对比Adagrad:
与Adagrad的不同之处:
Adagrad是基于所有过去梯度平方的累加和,即:
G_t = G_(t-1) + g_t^2而RMSProp是基于梯度平方的指数加权移动平均值,即:
s_t = β * s_(t-1) + (1 - β) * g_t^2Adagrad由于是直接累加,会导致学习率持续快速衰减,后期收敛会变慢
RMSProp 在计算 rt 时引入了衰减率 β,降低了陈旧梯度的权重,这样就避免了学习率过多衰减的问题。一般取 β 接近但小于1的值,如0.9。
因此,RMSProp相比Adagrad,在保留了自适应调整学习率的优点的同时,也解决了后期收敛变慢的缺陷,所以在实际应用中更为广泛。
简单来说就是RMSProp 相比 Adagrad 在保留历史梯度信息的策略上更加合理,这使得其在实际应用中表现更加优异。
optimizer = optim.RMSprop(model.parameters(), lr=0.01, alpha=0.99, eps=1e-08)
Adam
Adam(Adaptive Moment Estimation)是目前深度学习中应用最广泛的优化算法之一。它本质上是将动量(Momentum)和RMSprop两种思想结合到一种算法中。
Adam算法的更新公式如下:
第一个动量向量,对梯度做指数加权
第二个动量向量,对梯度平方做指数加权
修正的原因:由于我们一般将参数β设为接近于1,而再mt和vt初始值为0时 η/sqrt(v_t)+ε * mt 部分会趋近于0导致θ无法更新或更新速度极慢。
Adam与其他优化器效果对比:
例2.
Adam优点:
- 整合了动量和自适应学习率思想,收敛快且能够自动调整
- 在很多问题上表现优于其他算法
- 超参数的默认值在很多情况下都能有不错的效果
缺点:
- 在某些情况下,Adam可能会过早收敛到次优解
- 算法较为复杂,存在理论上的一些争议(具体见Adam那么棒,为什么还对SGD念念不忘 (2)—— Adam的两宗罪 - 知乎 (zhihu.com))
总的来说,Adam是一种综合了多种优化技术的"缝合怪"算法,巧妙地结合了不同思路的优点,在很多问题上都有出色的表现。因此虽然它理论基础存疑,但在实践中仍被广泛使用作为深度学习模型的默认优化算法之一。未来也可能有更先进的优化算法出现。
optimizer = optim.Adam(model.parameters(), lr=0.001, betas=(0.9, 0.999), eps=1e-08)
参考视频:
优化器 |SGD |Momentum |Adagrad |RMSProp |Adam_哔哩哔哩_bilibili
想看相关论文请点击这里:
https://arxiv.org/pdf/1609.04747.pdf
AdamW
虽然Adam从直觉上好像很符合我们的逻辑,但在实验中表明,SGD+momentum 可能比复杂的 Adam 表现更好。于是Adam又惨遭抛弃。
但是到了 2017 年末,Adam 似乎又重获新生。Ilya Loshchilov 和 Frank Hutter 在他们的论文《Fixing Weight Decay Regularization in Adam》中指出,每个库在 Adam 上实施的权重衰减似乎都是错误的,并提出了一种简单的方法(他们称之为 AdamW)来修复它。AdamW的参数更新公式如下:
对比Adam:
非常明显的,AdamW 与 Adam 的区别主要在于最后一步参数更新: AdamW在计算出θt后又增加了权重衰减项−ηλθt。
其实具体来说论文中将正则项与权重衰减方法进行了对比,大概意思如下:
在AdamW中,正则项被添加在参数更新公式中而不是损失函数中,这是因为在自适应梯度算法中,如Adam,L2正则化并不像在标准SGD中那样有效。具体来说,L2正则化和权重衰减对于自适应梯度算法是不同的。L2正则化会导致历史参数和/或梯度幅度较大的权重受到的正则化较少,而权重衰减会以相同的速率对所有权重进行正则化,实际上对具有较大梯度幅度的权重进行更多的正则化。这种差异导致了在Adam中添加正则项以改善泛化性能的必要性。不了解L2正则项的请点击这里
现在让我们看看两者的效果对比
AdamW中的添加正则项是为了改善Adam的正则化效果,通过将权重衰减与基于梯度的更新分离,从而实现更好的泛化性能。添加正则项的目的是为了提高Adam的性能,使其在某些问题上与SGD with momentum竞争力相当,减少从Adam到SGD之间的切换,简化选择数据集/任务特定训练算法和超参数的常见问题。
相关论文:
[1711.05101] Decoupled Weight Decay Regularization (arxiv.org)
更多AdamW细节请看:当前训练神经网络最快的方式:AdamW优化算法+超级收敛 - 知乎 (zhihu.com)
#PyTorch中没有直接内置AdamW,但可以通过给Adam优化器设置weight_decay参数来近似实现:
optimizer = optim.Adam(model.parameters(), lr=0.001, betas=(0.9, 0.999), eps=1e-08, weight_decay=0.01)
代码中:
lr
是学习率momentum
是动量系数weight_decay
是权重衰减系数(即L2正则化系数)alpha
、eps
等是各个优化器的其他超参数