5.2 十三个优化器

Optimizer 简介

有了数据、模型和损失函数,就要选择一个合适的优化器(Optimizer)来优化该模型,使loss不断降低,直到模型收敛。本节将介绍pytorch中优化器——Optimizer。 优化器的实现在torch.optim中,torch.optim is a package implementing various optimization algorithms. 在其中有一个核心类是Optimizer,Optimizer在pytorch提供的功能是所有具体优化器的基类,它对优化器进行了抽象与定义,约定了一个优化器应有的功能,Optimizer与十三个优化器的关系如下图所示:

通过上图可知道Optimizer定义了优化器应当具备的基础功能,如获取状态数据,加载状态数据,梯度清零,执行一步优化和添加参数组。

不同的优化方法会继承Optimizer,同时只需要实现不同的step()即可。这一点与损失函数类似,不同的损失函数只需要在forward()函数中定义好不同的公式计算即可。

本小节将以SGD为例,深入讲解优化器的以下函数,并具体地观察SGD算法的实现过程。

state_dict(self)

load_state_dict(self, state_dict)

zero_grad(self, set_to_none: bool = False)

step(self, closure)

add_param_group(self, param_group)

优化器工作方式

优化器如何工作,使得模型精度逐渐提高的?在此就不详细讲解,请大家自行补充机器学习基础概念。

众所周知,优化器是根据权重的梯度作为指导,定义权重更新的力度,对权重进行更新

过程很简单,实现起来却是复杂的,上述过程涉及几个问题:

  1. 梯度哪里来?
  2. 更新哪些权重?

  3. 怎么执行权重更新?

依次回答上述问题,便可熟悉优化器工作方式。

梯度哪里来? 梯度通过loss的反向传播,得到每个权重的梯度值,其中利用pytorch的autograd机制自动求导获得各权重的梯度。 (如果对autograd机制不熟悉,请查看第二章第六节)

更新哪些权重?通过loss的反向传播,模型(nn.Module)的权重(Parameter)上有了梯度(.grad)值,但是优化器对哪些权重进行操作呢?实际上优化器会对需要操作的权重进行管理,只有被管理的权重,优化器才会对其进行操作。在Optimizer基类中就定义了add_param_group()函数来实现参数的管理。通常在实例化的时候,第一个参数就是需要被管理的参数。

怎么执行权重更新?通过上述UML类图不难发现,step()函数是进行优化操作的,step()函数中实现了对所管理的参数进行更新的步骤。

总结一下:优化器在实例化时告诉它,需要对哪些参数进行管理,然后再每个iteration迭代时,借助loss.backward()得到梯度,接着优化器干活 optimizer.step()完成一步参数更新。

(此过程可以回顾第二章第二节的模型训练代码

优化器基类 Optimizer

Optimizer类是所有具体优化器的基类(Base class for all optimizers.

下面分别介绍Optimizer的基础属性及方法。

属性

  1. 参数组(param_groups):

在finetune、某层定制学习率,某层学习率置零操作中,都会设计参数组的概念,因此首先了解参数组的概念非常有必要。

参数组是用于管理需要进行优化的那些参数,例如权值weight,偏置bias,BN的alpha/beta等。

注意,这里是参数组不是参数,表明可以将所有参数进行分组,区别对待。

例如在finetune过程中,通常让前面层的网络采用较小的学习率,后面几层全连接层采用较大的学习率,

这是我们就要把网络的参数划分为两组,每一组有它对应的学习率。正是因为这种针对不同参数需要不同的更新策略的需求,才有了参数组的概念。

参数组是一个list,其元素是一个dict,dict中包含,所管理的参数,对应的超参,例如学习率,momentum,weight_decay等等。

案例:chapter-5/02_optimizer.py

  1. state:

用于存储优化策略中需要保存的一些缓存值,例如在用momentum时,需要保存之前的梯度,这些数据保存在state中。

  1. defaults:

优化方法默认的超参数;

方法:

  1. zero_grad()

功能:清零所管理参数的梯度。由于pytorch不会自动清零梯度,因此需要再optimizer中手动清零,然后再执行反向传播,得出当前iteration的loss对权值的梯度。

  1. step()

功能:执行一步更新,依据当前的梯度进行更新参数

  1. add_param_group(param_group)

功能:给optimizer管理的参数组中增加一组参数,可为该组参数定制lr,momentum,weight_decay等,在finetune中常用。

例如:optimizer_1.add_param_group({'params': w3, 'lr': 0.001, 'momentum': 0.8})

  1. state_dict()

功能:获取当前state属性。

通常在保存模型时同时保存优化器状态,用于断点保存,下次继续从当前状态训练;

  1. load_state_dict(state_dict)

功能:加载所保存的state属性,恢复训练状态。

对优化器工作方式熟悉后,再看Optimizer的属性和方法就简单了,下面通过一个具体的优化算法来熟悉完整的优化器使用。

SGD概念

torch.optim.SGD详情请参照[官方文档](

https://pytorch.org/docs/stable/generated/torch.optim.SGD.html#torch.optim.SGD)

SGD(stochastic gradient descent,随机梯度下降)是深度学习模型优化过程中最基础、最受欢迎、最稳定的一个,即使优化算法层出不穷,仅pytorch就提供了十三个,但目前绝大多数论文中仍旧采用SGD进行训练,因此SGD必须掌握。

SGD核心理论知识是梯度下降( gradient descent),即沿着梯度的负方向,是变化最快的方向。

而随机则指的是一次更新中,采用了一部分样本进行计算,即一个batch的数据可以看作是整个训练样本的随机采样。更多关于SGD的理论知识请自行学习机器学习基础。

SGD更新公式可简化为 w新 = w旧 - w_grad,即参数减去梯度(学习率为1)

不过通常会加入学习率来调整更新的步伐:w_新 = w_旧 - (lr * w_grad)

对于加入L2正则(weight decay)时,变为:w_新 = w_旧 - (lr (w_grad + weight_decay w_旧))

L2正则称之为weight decay的原因,对比公式1发现W需要乘以一个小于1的系数,因此是衰减的:w_新 = w_旧 - (lr (w_grad + weight_decay w_旧)) = w_旧(1 - lrweight_decay) - (lr w_grad)

对于其它momentum 、dampening 和nesterov 的加入就不详细展开,可通过官方文档查阅:

SGD使用

请结合代码观察SGD的使用

第一步:实例化:optimizer = optim.SGD(model.parameters(), lr=0.1, momentum=0.9, weight_decay=5e-4)

第二步:loss.backward()之前进行梯度清零:optimizer.zero_grad()

第三步:loss.backward()之后执行一步更新:optimizer.step()

在代码中还有一处用到了optimizer,那就是学习率调整模块:

scheduler = optim.lr_scheduler.StepLR(optimizer, gamma=0.1, step_size=50)

原理是将optimizer放到lr_scheduler进行管理,lr_scheduler会修改optimizer中的学习率

SGD代码实现

SGD类继承于Optimizer类,并重写step函数实现核心功能。

SGD类中step函数首先对各参数组、参数进行超参数的获取确定:for group in self.param_groups:

然后借助functional.sgd函数实现公式计算:

            F.sgd(params_with_grad,
                  d_p_list,
                  momentum_buffer_list,
                  weight_decay=weight_decay,
                  momentum=momentum,
                  lr=lr,
                  dampening=dampening,
                  nesterov=nesterov,
                  maximize=maximize,)

下面重点进入/torch/optim/_functional.py 的sgd()观察:这里为了讲解过程,省略了momentum的代码:

d_p:是梯度

weight_decay:是权重衰减系数,如 0.0001

param:是具体的权重参数

lr:是学习率

依194行代码: param = param + (alpha)* d_p

依193行代码: param = param + (-lr) d_p = **param - lr \ d_p**

依177行代码: param = param - lr (d_p + weight_decay param) = param(1 - lr * weight_decay) - lr*d_p

到这里,就可以与上述理论部分对应上了。

小结

其余十二个优化器可通过官方文档查阅,相信大家熟悉SGD以及优化器使用逻辑,其它的优化器都可轻松掌握。

Adadelta Implements Adadelta algorithm.
Adagrad Implements Adagrad algorithm.
Adam Implements Adam algorithm.
AdamW Implements AdamW algorithm.
SparseAdam Implements lazy version of Adam algorithm suitable for sparse tensors.
Adamax Implements Adamax algorithm (a variant of Adam based on infinity norm).
ASGD Implements Averaged Stochastic Gradient Descent.
LBFGS Implements L-BFGS algorithm, heavily inspired by minFunc.
NAdam Implements NAdam algorithm.
RAdam Implements RAdam algorithm.
RMSprop Implements RMSprop algorithm.
Rprop Implements the resilient backpropagation algorithm.
SGD Implements stochastic gradient descent (optionally with momentum).

下面梳理pytorch整个优化器实现的逻辑关系:

Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10

results matching ""

    No results matching ""