文章目录
前言
文章目标:
1、理解优化器的在训练中的作用
2、以adam优化器为例分析,优化器功能的优劣
3、对adam优化器展开内存分析
4、分析adam优化器的pytorch实现
一、优化器是什么?
一言以蔽之,优化器指导梯度下降方向与步长,目标是调整的权重更快、更准确的使目标函数达到最优值(最大值或在最小值)。
以一个最小化的典型训练脚步为例:
import torch
import torch.nn as nn
import torch.optim as optim
# 定义一个简单的线性模型
class LinearModel(nn.Module):
def __init__(self):
super(LinearModel, self).__init__()
# 初始化权重和偏置,这里使用matmul作为线性变换
self.weight = nn.Parameter(torch.randn(4, 4))
self.bias = nn.Parameter(torch.randn(4))
def forward(self, x):
# 使用matmul进行前向传播
return torch.matmul(x, self.weight) + self.bias
# 实例化模型
model = LinearModel()
# 定义损失函数,这里使用均方误差
criterion = nn.MSELoss()
# 定义优化器,这里使用Adam
optimizer = optim.Adam(model.parameters(), lr=0.001)
# 准备一些假数据进行演示,这里使用均匀分布的随机数
inputs = torch.randn(4, 4)
targets = torch.randn(4)
# 训练循环
num_epochs = 10
for epoch in range(num_epochs):
# 前向传播
outputs = model(inputs)
loss = criterion(outputs, targets)
# 清零梯度
optimizer.zero_grad()
# 反向传播
loss.backward()
# 更新权重
optimizer.step()
# 打印损失信息
print(f'Epoch [{
epoch+1}/{
num_epochs}], Loss: {
loss.item():.4f}')
# 训练结束
print("Training complete")
训练过程大致分为以下步骤循环:
1、前向传播
2、计算前向传播的误差(loss)
3、反向传播(以loss值为指导,计算梯度)
4、优化器通过梯度更新权重
一般我们使用梯度下降法更新参数:
因此一个最基础的权重更新公式,其中r为学习率:
W(t+1) = W(t) - ε * g(t)
公式中唯一与优化器相关的参数就是学习率ε,一个标量。实际上述公式表达的是随机梯度下降法(Stochastic Gradient Descent,SGD)。随机指的是一次梯度更新使用的是一个batch的数据集,而非完整的数据集。
二、优化器算法的演进
优化器算法的演进围绕着学习率ε与梯度值g(t)两个方面不断改进。
1.SGD算法—梯度值方向的改进
在使用SGD算法时,我们有时会遇到震荡的问题(每次迭代后梯度变化过于剧烈),导致模型收敛过慢。因此引入动量(Momentum)的概念,动量是数学领域的概念,在物理领域的动量可以被理解为惯性。
在优化器中引入动量的好处是,可以抵消梯度中那些变化剧烈的分量,从而减小振荡、加快收敛速度。
- 未引入动量时,权重更新过程中振荡的向极值逼近
* 引入动量后,向极值逼近的振荡减小
加入动量后的SGD算法的权重更新公式(SGD-M)为:
W(t+1) = W(t) + V(t+1)
其中V(t+1) = μV(t ) - ε * g(t+1)
表示携带动量后需要更新的梯度; 该方法可以减小更早时刻梯度对当前梯度的影响,μ参数通常取值为0.9。
动量的存在带来两个影响,(1)振荡的权重更新被平滑了;(2)非振荡的权重更新的被加速了。
[拓展–继续演进]
但是第二点影响有个副作用:由于动量的存在,权重的更新首前一个step的gard的影响,导致它在最后的收敛阶段不停震荡,从而浪费很多时间。
针对这个问题,NAG(Nesterov’s Accelerated Gradient)算法被提出的,它的基本思想是在梯度更新之前往前看一步,让优化器可以预知未来的情况,从而对现在的梯度进行调整。
NAG算法中的
W(t+1) = W(t) + V(t + 1)
V(t + 1) = μV(t) - ε * g (W(t) + μ * V(t) )
根据当前梯度方向向前走一步之后再计算出来的梯度, 再用当前梯度进行修正。通过这种方式,在接近最优解时,Nesterov优化器比标准动量SGD算法有更快的收敛速度。因为这种“前瞻性”的操作能帮助优化器避免走得太远,就像是提前刹车,所以在接近最优解时更加稳定。On the importance of initialization and momentum in deep learning
动量法首先计算当前的梯度值(图中的小的蓝色向量),然后在更新的累积梯度(大的蓝色向量)方向上前进一大步,Nesterov加速梯度下降法NAG首先在先前累积梯度(棕色的向量)方向上前进一大步,计算梯度值,然后做一个修正(绿色的向量)。
pytorch源码实现:注意这里实现的是简化版本的NAG 算法,有助于降低计算量,推导过程参考
2.AdaGrad – 优化学习率方向
学习率代表了优化的步长,如果设定过大,可能会导致参数在最优点附近震荡,如果设置过小,则会导致模型收敛速度过慢。所以通常情况下我们会选择一种让学习率逐步减小的策略,例如余弦退火算法、StepLr策略等等。
但是即使是这样,我们还有一个问题没有解决,那就是不同参数应该有不同的学习率。
那是不是可以让模型自动地调整学习率呢?
AdaGrad方法就可以实现这个愿望。
AdaGrad的公式为:
W ( t + 1 ) = W ( t ) − l r S ( t ) + ε g ( t ) W(t + 1) = W(t) - \frac{lr}{\sqrt{S(t)} + ε}g(t) W(t+1)=W(t)−S(t)+ε