0%

[DL] PyTorch 折桂 10:torch.optim

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
2
3
4
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,所以它们的属性相同。我们先构建一个优化器的实例:

1
2
3
4
5
6
7
8
9
10
11
12
>>> 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
    返回优化器的参数组。参数组是一个列表,每个元素是一个组的字典。
    1
    2
    3
    >>> 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)
    添加参数组。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    >>> 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() 的返回值包含了梯度的状态。
    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
    28
    >>> 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 实例里。
    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.
    >>> 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):设置每个参数组的学习率为 $lr*\lambda^n,n=\frac{epoch}{step_size}$;
    1
    2
    3
    4
    5
    6
    >>> 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 时变化一次;
    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
    >>> # ...
    >>> 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 时变化。
    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
    >>> scheduler = MultiStepLR(optimizer, milestones=[30,80], gamma=0.1)
    >>> for epoch in range(100):
    >>> train(...)
    >>> validate(...)
    >>> scheduler.step()
    它们的公共参数:
  • lr_lambda:描述学习率变化的匿名函数;
  • gamma:倍数系数;
  • last_epoch:最后一次 epoch 的索引,若为 -1 则为初始 epoch。

欢迎关注我的微信公众号“花解语 NLP”:
在这里插入图片描述

欢迎关注我的其它发布渠道