关于九游会 联系我们

咨询电话: 020-88888888

当前位置: 主页 > 新闻资讯 > 新政解读

10种优化迭代算法基础详解及其pytorch实现!

发布于 2024-02-28 00:40 阅读(


该篇笔记整理自余庭嵩的讲解。如果主要看优化器在pytorch中如何使用则可以直接看optim.SGD这一部分的讲解,该部分对优化器pytorch实现时的各变量解释详细,并且其他优化器的变量与其大同小异,在其他部分里就不再重新说明了。本文重点是探讨其中一种优化器的全部数学原理,其他优化器都是基于不同程度上的改进,最后一部分进行了罗列,如果需要详细了解原理可根据所给论文去阅读。

优化器的功能:管理更新模型中可学习参数的值,使得模型输出更接近真实标签。
这里的可学习参数一般就是指权值和偏置了,管理指优化器可以修改哪一部分参数。更新就是优化器的更新策略,每个不同的优化器会采取不同的策略去更新参数的值,这里策略通常是梯度下降。
在展开讨论之前,先明确下面几个基本概念:

  • 导数:函数在指定坐标轴上的变化率
  • 方向导数:指定方向上的变化率
  • 梯度:一个向量。方向为使得方向导数取得最大值的方向(各个方向上变化率最大的那个方向),模长就是这个最大的变化率。
  • 梯度下降:一种策略。梯度是增长最快的方向,所以取梯度的负方向就是下降最快的那个方向。函数值就是loss值,所以用梯度下降来更新权值,使得函数值最快地降低。

在pytorch中优化器类名定义为Optimizer,其继承于object父类。优化器中包括如下几个主要基本属性:

  • defaults:优化器的超参数。里面通常存储学习率,momentum的值以及decay的值等等
  • state:用于存储参数的缓存。例如momentum会使用到前几次更新时所使用的梯度,那前几次的梯度就需要缓存在state中
  • param_groups:管理的参数组。以list形式存储,list中的每一个元素是一个字典,字典的key(源代码里是’params’)对应的value才是真正的参数,这里的value其实又是一个list,里面包含了多个参数值。前面基本概念中提到了管理的概念,param_groups中存储的参数就是优化器可以修改的全部参数
  • _step_count:记录更新次数,学习率调整中会使用到。例如100轮下降一次学习率,200轮再下降一次学习率,下降了几次就保存在这个属性中

还包含如下几个主要的基本方法(函数):

  • zero_grad():清空所管理参数的梯度。参数都是tensor类型,tensor中会自动存储梯度的信息,而tensor的梯度信息是不会自动清零的。每次反向传播会把梯度值加到tensor的grad里面(累加),因此需要在求导前或者更新后进行清零。该函数判断优化器管理的参数组每个张量的梯度是否是None,如果不是就清零,是就不操作。
  • step():执行一步更新。当计算得loss,再用loss.backward()反向传播计算得到梯度之后,就需要使用step()进行权值参数的更新。更新的策略根据优化器的变化而变化。
  • add_param_group():添加参数组。优化器管理的参数是分组的,对于不同组的参数有不同的超参数的设置。例如NLP文本特征提取部分的参数,我们希望其学习得慢一些,后面自己添加的全连接层,我们希望其学习得快一些,由此就可以分两组参数,对这两组分别设置不同的学习率或者别的超参数,因此需要参数组的概念。
  • state_dict():获取优化器当前状态信息字典。这个函数的返回值是一个字典,字典中只有两个key。一个是state,一个是param_groups。
  • load_state_dict():将状态信息字典加载到优化器当中。这个函数和state_dict()函数使得模型可以断点续训练。比如训练模型需要一个月,如果写了隔几个epoch执行一次state_dict(),那断电也没事,开机找到存好的dict用load加载进来就可以接着前面的进度继续训练了。

优化器方法使用实例

首先我们手动创建一个可学习的参数,给其梯度赋值,然后定义好随机梯度下降优化器准备以其为例:

 

之后我们依次观察优化器的每个方法都做了些什么。

optimizer.step()

通过下面代码构造使用实例

 

输出结果为

 

以第一个值为例,0.6614变成了0.5614,为什么?因为梯度设置的是1,学习率是0.1,所以一次更新1*0.1就等于0.1,由于更新策略定义了是随机梯度下降,所以要在梯度的反方向上更新,所以就在原权值的基础上减去0.1,由此得到最后结果。这里这么简单是因为我们直接赋值好了梯度,实际情况中梯度是要通过求解得出来的。具体的求解机制可粗浅理解入下,矩阵中每一个元素都影响着下一步的几个特定的输出,那矩阵中某个特定元素的梯度其实就是计算其影响的每个输出对其的偏导之和。
总结一下,step就是利用已经求好的梯度对权值进行一次更新。

optimizer.zero_grad()

通过下面代码构造使用实例

 

输出结果为

 

输出结果中值得注意的是,优化器中的权重地址和实际的权重地址是一样的,所以优化器中的权重和实际权重共享内存。

optimizer.add_param_group()

通过下面代码构造使用实例,给优化器以字典的形式添加一组参数

 

输出结果为

 

就可以看见param_group中有两个字典了。

optimizer.state_dict()

通过代码构造下面使用实例:

 

输出结果为

 

可见,初次定义state_dict时,里面的state栏还没有信息。进行了十步更新之后,state_dict中就随时存储信息了。之后用torch.save把训练十步之后的优化器信息存在电脑硬盘上,后面就可以随时加载了。

optimizer.load_state_dict()

通过代码构造下面使用实例,构建一个优化器,读取进来pkl,之后调用函数:

 

输出结果为

 

可见该函数加载进来了信息,可以接着这个信息进行训练了。

学习率

直接通过实例来理解学习率,例如目标函数如下:
y=4 x 2 y=4x^2 y=4x2
把这里的x看成是权重,y看成是loss,下面通过代码来理解学习率的作用。
首先构造这个函数:

 

然后画出-10到10区间上的均匀分布的500个点的函数图像如下:
在这里插入图片描述
接下来模拟一下优化的过程,假如不考虑学习率的概念,直接令其为1即可,迭代四次。

 

平台注册入口