文章目录
- 1. 优化器
- 1.1 [优化器的种类](https://zhuanlan.zhihu.com/p/64885176 "PyTorch 学习笔记(七):PyTorch的十个优化器")
- 1.2 创建优化器
- 1.3 优化器的属性
- 2. 改变学习率
1. 优化器
优化器就是根据导数对参数进行更新的类,不同的优化器本质上都是梯度下降法,只是在实现的细节上有所不同。类似的,PyTorch 里的所有优化器都继承自 torch.optim.Optimizer
这个基类。
torch.optim.Optimizer(params, defaults)
params
是优化器要优化的权重,是一个迭代器;defaults
是优化器在参数以外的默认参数,根据所被继承的类有所不同。
1.1 优化器的种类
torch.optim.SGD(params, lr=<required parameter>, momentum=0, dampening=0, weight_decay=0, nesterov=False)
基础优化器,可以使用 momentum
来避免陷入 local minima。
-
torch.optim.ASGD
:SGD 的改进版,使用平均随机梯度下降。
下面的若干种优化器都来自于同一个算法:Adaptive Gradient estimation,自适应梯度估计。
-
torch.optim.Rprop
:实现 resilient backpropagation algorithm,弹性方向传播。不适用于 mini-batch,因此现在较少使用。 -
torch.optim.Adagrad
:Adagrad 是一种自适应优化方法,是自适应的为各个参数分配不同的学习率。这个学习率的变化,会受到梯度的大小和迭代次数的影响。梯度越大,学习率越小;梯度越小,学习率越大。缺点是训练后期,学习率过小,因为 Adagrad 累加之前所有的梯度平方作为分母。 -
torch.optim.Adadelta
:实现 Adadelta 优化方法。Adadelta 是 Adagrad 的改进。Adadelta分母中采用距离当前时间点比较近的累计项,这可以避免在训练后期,学习率过小。 -
torch.optim.RMSprop
:实现 RMSprop 优化方法(Hinton提出),RMS 是均方根(root meam square)的意思。RMSprop 和 Adadelta 一样,也是对 Adagrad 的一种改进。RMSprop 采用均方根作为分母,可缓解 Adagrad 学习率下降较快的问题。并且引入均方根,可以减少摆动。 -
torch.optim.Adam
:Adam 是对上面的自适应算法的改进,是一种自适应学习率的优化方法,Adam 利用梯度的一阶矩估计和二阶矩估计动态的调整学习率。吴老师课上说过,Adam 是结合了 Momentum 和 RMSprop,并进行了偏差修正。 -
torch.optim.Adamax
:Adamax对Adam增加了一个学习率上限的概念。 -
torch.optim.SparseAdam
:由于稀疏张量的优化器。 -
torch.optim.LBFGS
:实现L-BFGS(Limited-memory Broyden–Fletcher–Goldfarb–Shanno)优化方法。L-BFGS属于拟牛顿算法。L-BFGS是对BFGS的改进,特点就是节省内存。
1.2 创建优化器
可以看出,Adam
优化器是集大成的优化器,一般无脑使用 Adam
即可。本文以 Adam
为例。
torch.optim.Adam(params, lr=0.001, betas=(0.9, 0.999), eps=1e-08, weight_decay=0, amsgrad=False)
-
params (iterable)
:可用于迭代优化的参数或者定义参数组的dicts。 -
lr (float, optional)
:学习率(默认: 1e-3) -
betas (Tuple[float, float], optional)
:用于计算梯度的平均和平方的系数(默认:(0.9, 0.999)) -
eps (float, optional)
:为了提高数值稳定性而添加到分母的一个项(默认:1e-8) -
weight_decay (float, optional)
:权重衰减(如 L2 惩罚,默认: 0)
对于 torch.optim.Adam
来说,只有 params
是必要的属性。可以以如下方法进行创建:
optimizer = optim.Adam(model.parameters(), lr=0.0001)
也可以指定每个参数选项。 只需传递一个可迭代的 dict 来替换先前可迭代的 Variable。dict 中的每一项都可以定义为一个单独的参数组,参数组用一个 params 键来包含属于它的参数列表。其他键应该与优化器接受的关键字参数相匹配,才能用作此组的优化选项。比如:
optim.SGD([
{'params': model.base.parameters()},
{'params': model.classifier.parameters(), 'lr': 1e-3}
], lr=1e-2, momentum=0.9)
如上,model.base.parameters()
将使用 1e-2 的学习率,model.classifier.parameters()
将使用 1e-3 的学习率。0.9 的 momentum
作用于所有的 parameters。
1.3 优化器的属性
因为优化器都继承自 torch.optim.Optimizer
,所以它们的属性相同。我们先构建一个优化器的实例:
>>> weight1 = torch.ones((2, 2))
>>> optimizer = torch.optim.Adam([weight], lr=1e-2)
>>> print(optimizer)
Adam (
Parameter Group 0
amsgrad: False
betas: (0.9, 0.999)
eps: 1e-08
lr: 0.01
weight_decay: 0
)
-
param_group
返回优化器的参数组。参数组是一个列表,每个元素是一个组的字典。
>>> print(optimizer.param_groups)
[{'params': [tensor([[1., 1.],
[1., 1.]], requires_grad=True)], 'lr': 0.01, 'betas': (0.9, 0.999), 'eps': 1e-08, 'weight_decay': 0, 'amsgrad': False}]
-
add_param_group(param_group)
添加参数组。
>>> weight2 = torch.zeros(2,2)
>>> optimizer.add_param_group({'params':weight2, 'lr':0.01})
>>> optimizer.param_groups
[{'params': [tensor([[1., 1.],
[1., 1.]], requires_grad=True)],
'lr': 0.01,
'betas': (0.9, 0.999),
'eps': 1e-08,
'weight_decay': 0,
'amsgrad': False},
{'params': [tensor([[0., 0.],
[0., 0.]])],
'lr': 0.01,
'betas': (0.9, 0.999),
'eps': 1e-08,
'weight_decay': 0,
'amsgrad': False}]
-
state_dict()
返回优化器的状态。这个属性与param_group
的区别在于state_dict()
的返回值包含了梯度的状态。
>>> optimizer.state_dict()
{'state': {}, # 进行反向传播以前梯度为空
'param_groups': [{'lr': 0.01,
'betas': (0.9, 0.999),
'eps': 1e-08,
'weight_decay': 0,
'amsgrad': False,
'params': [140685302219312]}]}
>>> optimizer.step()
>>> optimizer.state_dict()
{'state': {140685302219312: {'step': 1, # 反向传播以后有了状态
'exp_avg': tensor([[0.1000, 0.1000],
[0.1000, 0.1000]]),
'exp_avg_sq': tensor([[0.0010, 0.0010],
[0.0010, 0.0010]])}},
'param_groups': [{'lr': 0.01,
'betas': (0.9, 0.999),
'eps': 1e-08,
'weight_decay': 0,
'amsgrad': False,
'params': [140685302219312]},
{'lr': 0.01,
'betas': (0.9, 0.999),
'eps': 1e-08,
'weight_decay': 0,
'amsgrad': False,
'params': [140685312958784]}]}
load_state_dict(state_dict)
- 载入已经保存的参数组。这个属性与模型的保存于载入一并介绍。
-
step()
执行一次反向传播。 -
zero_grad()
将优化器内存储的梯度清零。
2. 改变学习率
torch.optim.lr_scheduler
中提供了基于多种 epoch 数目调整学习率的方法。优化器需要被包含进 scheduler 实例里。调整学习率的函数有很多,这里仅列举了常用的 4 种。
>>> scheduler = ...(optimizer, ...) #优化器被包含进来
>>> for epoch in range(100):
>>> train(...)
>>> validate(...)
>>> scheduler.step()
-
torch.optim.lr_scheduler.LambdaLR(optimizer, lr_lambda, last_epoch=-1)
:将每一个参数组的学习率设置为初始学习率 lr 的某个函数倍;
>>> # Assuming optimizer has two groups.
>>> lambda1 = lambda epoch: epoch // 30
>>> lambda2 = lambda epoch: 0.95 ** epoch
>>> scheduler = LambdaLR(optimizer, lr_lambda=[lambda1, lambda2])
>>> for epoch in range(100):
>>> train(...)
>>> validate(...)
>>> scheduler.step()
-
torch.optim.lr_scheduler.MultiplicativeLR(optimizer, lr_lambda, last_epoch=-1)
:设置每个参数组的学习率为 ;
>>> lmbda = lambda epoch: 0.95
>>> scheduler = MultiplicativeLR(optimizer, lr_lambda=lmbda)
>>> for epoch in range(100):
>>> train(...)
>>> validate(...)
>>> scheduler.step()
-
torch.optim.lr_scheduler.StepLR(optimizer, step_size, gamma=0.1, last_epoch=-1)
:设置每个参数组的学习率在每 step_size 时变化一次;
>>> # Assuming optimizer uses lr = 0.05 for all groups
>>> # lr = 0.05 if epoch < 30
>>> # lr = 0.005 if 30 <= epoch < 60
>>> # lr = 0.0005 if 60 <= epoch < 90
>>> # ...
>>> scheduler = StepLR(optimizer, step_size=30, gamma=0.1)
>>> for epoch in range(100):
>>> train(...)
>>> validate(...)
>>> scheduler.step()
-
torch.optim.lr_scheduler.MultiStepLR(optimizer, milestones, gamma=0.1, last_epoch=-1)
:设置每个参数组的学习率在达到 milestone 时变化。
>>> # Assuming optimizer uses lr = 0.05 for all groups
>>> # lr = 0.05 if epoch < 30
>>> # lr = 0.005 if 30 <= epoch < 80
>>> # lr = 0.0005 if epoch >= 80
>>> scheduler = MultiStepLR(optimizer, milestones=[30,80], gamma=0.1)
>>> for epoch in range(100):
>>> train(...)
>>> validate(...)
>>> scheduler.step()
-
torch.optim.lr_scheduler.ExponentialLR(optimizer, gamma, last_epoch=-1)
:按照指数方式调整学习率: - `
它们的公共参数: -
lr_lambda
:描述学习率变化的匿名函数; -
gamma
:倍数系数; -
last_epoch
:最后一次 epoch 的索引,若为 -1 则为初始 epoch。