提示:本文更新于2024年8月24日
1、Adam优化器简介
Adam 是一种可以替代传统随机梯度下降过程的一阶优化算法,它能基于训练数据迭代地更新神经网络权重。Adam 最开始是由 OpenAI 的 Diederik Kingma 和多伦多大学的 Jimmy Ba 在 2015 年 ICLR 论文(Adam: A Method for Stochastic Optimization)中提出的。该算法名为「Adam」,其并不是首字母缩写,也不是人名,它的名称来源于适应性矩估计(adaptive moment estimation)。
2、Adam提出动机
在 Adam 提出之前,人们已经在使用像 Momentum 和 RMSProp 这样的优化算法来加速深度神经网络的训练过程。然而,这些算法各有优势和局限。Momentum 善于处理梯度的方向和大小,而 RMSProp 善于调整学习率以应对数据的稀疏性。Adam 的提出是为了结合这两种算法的优点,同时减少它们的缺点,提供一种更加鲁棒的优化解决方案。
3、Adam使用过程
首先,我们要定义一个神经网络,如下所示:
class MyModel(nn.Module):
def __init__(self):
super().__init__()
self.nn1 = nn.Linear(2, 3)
self.nn2 = nn.Linear(3, 6)
def forward(self, x):
x = F.relu(self.nn1(x))
return F.relu(self.nn2(x))
接着,我们可以通过model.named_parameters()函数查看一下这个网络内部的权重的名称和内部参数是什么。
model = MyModel()
for name, param in model.named_parameters():
print(name)
print('-' * 24)
print(param)
print('=' * 24)
结果如下所示:
nn1.weight
------------------------
Parameter containing:
tensor([[ 0.4568, 0.2095],
[-0.6798, 0.1150],
[-0.6007, -0.0654]], requires_grad=True)
========================
nn1.bias
------------------------
Parameter containing:
tensor([-0.2651, 0.1305, 0.4654], requires_grad=True)
========================
nn2.weight
------------------------
Parameter containing:
tensor([[-0.1951, -0.1204, 0.1378],
[-0.1610, -0.4839, -0.0099],
[ 0.3376, -0.1957, 0.5075],
[-0.1851, -0.2538, -0.0336],
[ 0.5438, -0.1405, -0.1241],
[ 0.4294, 0.0081, -0.2110]], requires_grad=True)
========================
nn2.bias
------------------------
Parameter containing:
tensor([-0.5548, 0.1145, 0.1029, -0.2623, -0.0360, -0.0181],
requires_grad=True)
========================
最后,可以针对上面的权重参数,设置Adam优化器进行迭代更新:
optimizer_Adam = torch.optim.Adam(model.parameters(), lr=0.1)
至此,我们完成了Adam优化器的设置,下一步可以在训练中使用它进行权重参数的更新了。优化器的固定用法如下:
loss.backward()
optimizer.step()
optimizer.zero_grad()
其中backward()是反向计算各参数梯度,step()表示更新网络参数,zero_grad()表示清空当前的梯度更新,防止影响下一轮更新。
4、Adam进阶使用
(1)只训练模型的一部分参数,排除bias层的参数训练
param_optimizer = list(model.named_parameters())
no_decay = ['nn1.bias', 'nn2.bias']
param_optimizer2 = [p for n, p in param_optimizer if n not in no_decay]
optimizer_grouped_parameters = [{'params': param_optimizer2, 'weight_decay': 0.01}]
optimizer_Adam = torch.optim.Adam(optimizer_grouped_parameters, lr=0.1)
(2)不同网络部分的参数设置不同的学习率。
optimizer_grouped_parameters = [{'params': model.nn1.parameters(), 'lr': 0.1},{'params': model.nn2.parameters(), 'lr': 0.2}]
optimizer_Adam = torch.optim.Adam(optimizer_grouped_parameters)
(3)根据迭代次数,手动设置学习率。
optimizer_Adam = torch.optim.Adam(model.parameters(), lr=0.1)
for epoch in range(100):
if epoch % 5 == 0:
for params in optimizer_Adam.param_groups:
# 遍历Optimizer中的每一组参数,将该组参数的学习率 * 0.9
params['lr'] *= 0.9
扩展:PyTorch Optimizer优化器的底层介绍
PyTorch优化器在构建完成后,具备一个param_groups列表,这个列表包含了哪些变量需要更新,这些变量更新的学习率如何,这个属性列表可通过param_groups()方法获取。param_groups()方法返回一个List列表,它的每个元素都是一组独立的参数,以dict的方式进行存储,结构参见下图:
-param_groups
-0(dict) # 第一组参数
params: # 维护要更新的参数
lr: # 该组参数的学习率
weight_decay: # 该组参数的权重衰减系数
-1(dict) # 第二组参数
-2(dict) # 第三组参数
...
注意,每个dict中会因为optimizer的不同,参数有所差别,比如Adam优化器中存在betas参数,而SGD优化器中存在moumentum参数。下面可以看看本例子的Adam优化器的内部参数:
[
{
'params': [Parameter containing:tensor([[-0.0386, 0.3979],
[ 0.2451, -0.5477],
[ 0.2848, -0.6663]], requires_grad=True),
Parameter containing:tensor([-0.6260, 0.6027, 0.0412], requires_grad=True),
Parameter containing:tensor([[-0.2931, -0.3993, 0.1601],
[ 0.1608, 0.1821, 0.4538],
[ 0.3516, -0.4239, -0.5256],
[ 0.4598, 0.1838, -0.4019],
[-0.4469, 0.4455, 0.1316],
[-0.1232, 0.3769, -0.1184]], requires_grad=True),
Parameter containing:tensor([ 0.1404, -0.0542, -0.0085, 0.0995, 0.3741, -0.0223],requires_grad=True)],
'lr': 0.1,
'betas': (0.9, 0.999),
'eps': 1e-08,
'weight_decay': 0,
'amsgrad': False,
'maximize': False,
'foreach': None,
'capturable': False,
'differentiable': False,
'fused': None
}
]