1. 优化器
优化器就是根据导数对参数进行更新的类,不同的优化器本质上都是梯度下降法,只是在实现的细节上有所不同。类似的,PyTorch 里的所有优化器都继承自 torch.optim.Optimizer
这个基类。
1 | 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
为例。1
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
是必要的属性。可以以如下方法进行创建:
1 | optimizer = optim.Adam(model.parameters(), lr=0.0001) |
也可以指定每个参数选项。 只需传递一个可迭代的 dict 来替换先前可迭代的 Variable。dict 中的每一项都可以定义为一个单独的参数组,参数组用一个 params 键来包含属于它的参数列表。其他键应该与优化器接受的关键字参数相匹配,才能用作此组的优化选项。比如:
1 | optim.SGD([ |
如上,model.base.parameters()
将使用 1e-2 的学习率,model.classifier.parameters()
将使用 1e-3 的学习率。0.9 的 momentum
作用于所有的 parameters。
1.3 优化器的属性
因为优化器都继承自 torch.optim.Optimizer
,所以它们的属性相同。我们先构建一个优化器的实例:
1 | 2, 2)) weight1 = torch.ones(( |
param_group
返回优化器的参数组。参数组是一个列表,每个元素是一个组的字典。1
2
3print(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)
添加参数组。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
182,2) weight2 = torch.zeros(
'params':weight2, 'lr':0.01}) optimizer.add_param_group({
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()
的返回值包含了梯度的状态。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28optimizer.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 实例里。1
2
3
4
5#优化器被包含进来 scheduler = ...(optimizer, ...)
for epoch in range(100):
train(...)
validate(...)
scheduler.step()torch.optim.lr_scheduler.LambdaLR(optimizer, lr_lambda, last_epoch=-1)
:将每一个参数组的学习率设置为初始学习率 lr 的某个函数倍;1
2
3
4
5
6
7
8# Assuming optimizer has two groups.
lambda epoch: epoch // 30 lambda1 =
lambda epoch: 0.95 ** epoch lambda2 =
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)
:设置每个参数组的学习率为 $lr*\lambda^n,n=\frac{epoch}{step_size}$;1
2
3
4
5
6lambda epoch: 0.95 lmbda =
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 时变化一次;1
2
3
4
5
6
7
8
9
10# 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
# ...
30, gamma=0.1) scheduler = StepLR(optimizer, step_size=
for epoch in range(100):
train(...)
validate(...)
scheduler.step()torch.optim.lr_scheduler.MultiStepLR(optimizer, milestones, gamma=0.1, last_epoch=-1)
:设置每个参数组的学习率在达到 milestone 时变化。它们的公共参数:1
2
3
4
5
6
7
8
9# 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
30,80], gamma=0.1) scheduler = MultiStepLR(optimizer, milestones=[
for epoch in range(100):
train(...)
validate(...)
scheduler.step()lr_lambda
:描述学习率变化的匿名函数;gamma
:倍数系数;last_epoch
:最后一次 epoch 的索引,若为 -1 则为初始 epoch。
欢迎关注我的微信公众号“花解语 NLP”: