Pytorch深度学习—优化器

损失函数Loss是衡量模型输出与真实标签之间的差异的。有了损失函数Loss,通过Loss根据一定的策略 来更新模型中的参数使得损失函数Loss逐步降低;这便是优化器optimizer的任务。本节优化器optimizer主要包括3方面内容,分别是(1)什么是优化器,优化器的定义和作用是?(2)Pytorch中提供的优化器optimizer的基本属性是?(3)optimizer的方法是?
在这里插入图片描述
图1 机器学习模型训练步骤
了解优化器之前,可以通过机器学习模型训练步骤简略了解一下优化器。在损失函数模块,根据模型输出与真实标签之间的差异得到Loss损失函数;有了Loss,通常采用Pytorch中的 autograd自动梯度求导 得到 模型中每个可学习参数的梯度grad;有了梯度grad,优化器optimizer获取梯度grad,通过一系列优化策略来更新模型参数,该参数就可使得Loss值下降,进而达到模型输出值与真实标签之间的差异减小的目的。

什么是优化器

Pytorch的优化器:
管理并更新模型中可学习参数的值,使得模型输出更接近真实标签。

分析
其中,可学习参数指 权值 和 偏置bias;
其次,优化器最主要的2大功能:
(1)管理:指优化器管理哪一部分参数;
(2)更新:优化器当中具有一些优化策略,优化器可采用这些优化策略更新模型中可学习参数的值;这一更新策略,在神经网络中通常都会采用梯度下降法。

解释
那么,什么是梯度下降法?为什么梯度下降法就可以使得Loss降低呢?
在这里插入图片描述
<预备知识>
根据图2例(1)中的一元函数曲线,可以看出函数y=4x2在x=2处的导数(变化率/切线斜率)为16;这说明导数是函数在指定坐标轴上的变化率。由于一元函数只有1个自变量,其方向受限。只有在二元函数或三元函数才考虑方向导数。在图2例(2)中“红色线”标注的,发现∂z∂x是固定y轴,求在x轴方向上的变化率;而∂z∂y是固定x,求在y轴方向上的变化率。那么,由于方向l是任意的,该红色点应当具有无穷多个方向导数。由此引出梯度概念。所谓梯度,就是在无穷多个方向导数中,其方向为方向导数取得最大的一个向量。

梯度,是1个向量(应当具有方向/模长2个属性)。①其方向是方向导数取得最大的方向;②模长为方向导数的值。简言之,梯度向量的方向使得当前点增长达到最快的方向,模长为增长速度。

采用梯度下降法的原因—Loss损失函数朝着梯度的负方向去变化:由于梯度方向是增长最快的,那么梯度的负方向就是下降最快的。因此,在模型更新参数时,采用“梯度下降法”朝着梯度的负方向去变化,可以使得损失函数Loss逐步降低。

<总结>
Pytorch中优化器optimizer 管理着模型中的可学习参数,并采用梯度下降法 更新着可学习参数的值。

optimizer的属性

class Optimizer(object)
      def_init_(self,params,defaults):
      self.defaults=defaults
      self.state=defaultdict(dict)
      self.param_groups=[ ]
      ...
      param_groups=[{
    
    'params':param_groups}]
 基本属性:
(1)defaults:优化器超参数;
(2)state:参数的缓存,如momentum的缓存;
(3)param_groups:管理的参数组;
(4)_step_count:记录更新次数,学习率调整中使用;

基本属性:
(1)defaults:优化器超参数;用来存储学习率、momentum的值等等;
(2)state:参数的缓存,如momentum的缓存;采用momentum时会使用前几次更新时使用的梯度,也就是前几次的梯度,把前几次的梯度值缓存下来,在本次更新中使用;
(3)param_groups:管理的参数组;优化器最重要的属性,已经知道优化器是管理可学习参数,这一系列可学习参数就放在param_groups这一属性中,同时,这一参数组定义为list。
param_groups=[{‘params’:param_groups}]
因此,param_groups是1个list。而在list[ ] 中,每一个元素又是1个字典{ } ,这些字典中有很多key,其中最重要的key是-‘params’,只有’params’当中才会存储训练模型的参数。

(4)_step_count:记录更新次数,学习率调整中使用;比如更新在第100次,就下降学习率,更新到200次,又下降学习率。

optimizer的方法

一、zero_grad()

1、理论

zero_grad():清空所管理参数的梯度
Pytorch中tensor特性:tensor张量梯度不自动清零

已知参数param是1个特殊的张量,张量当中都会有梯度grad。由于Pytorch中张量tensor的梯度grad是不会自动清零的,它会在每一次backward反向传播时采用autograd计算梯度,并把梯度值累加到张量的grad属性中的。
如下图3所示,由于Pytorch中的grad属性不自动清零,因此每计算1次梯度就自动累加到grad属性中造成错误;因此,一定要在使用完梯度后或者进行梯度求导(反向传播)之间通过zero_grad进行清零。
在这里插入图片描述

2、操作

Pytorch中优化器对梯度清零zero_grad()的具体实现
在这里插入图片描述

二、step()

step():执行一步更新

当计算得到Loss,利用Loss进行backward反向传播计算各个参数的梯度之后,采用step()进行一步更新,更新权值参数。step()会采用梯度下降的策略,具体方法有很多种,比如随机梯度下降法、momentum+动量方法、autograd自适应学习率等等一系列优化方法。

扫描二维码关注公众号,回复: 14768465 查看本文章

三、add_param_group()

add_param_group():添加参数组

add_param_group()添加一组参数到优化器中。已知优化器管理很多参数,这些参数是可以分组;对于不同组的参数,有不同的超参数设置,例如在某一模型中,希望特征提取部分的权值参数的学习率小一点,学习更新慢一点,这时可以把特征提取的参数设置为一组参数,而对于后面全连接层,希望其学习率大一点,学习快一点。这时,可以把整个模型参数设置为两组,一组为特征提取部分的参数,另一部分是全连接层的参数,对这两组设置不同的学习率或超参数,这时就需要用到参数组概念。

四、state_dict()、load_state_dict()

state_dict():获取优化器当前状态信息字典
load_state_dict():加载状态信息字典

这两个方法是一组操作,用于模型断点的续训练。例如训练1个模型需要10天,第5天停电或者意外原因,模型终止了训练。为了再次训练可以从断点开始,可以利用该组操作state_dict()和load_state_dict()保存在第5天模型以及优化器中的信息,在模型训练时会一定间隔如10epoch保存当前状态信息,用来在断点可以恢复训练。

实验分析optimizer的方法

(1)step()
//构建1个2×2大小的随机张量,并开放“梯度”
weight = torch.randn((2, 2), requires_grad=True)
//构建12×2大小的全1梯度张量
weight.grad = torch.ones((2, 2))
//将可学习参数[weight]传入优化器
optimizer = optim.SGD([weight], lr=1)  //0.1

# -------------------- step ------------------
 flag = 1
if flag:
    print("weight before step:{}".format(weight.data))
    optimizer.step()        # 修改lr=1 0.1观察结果
    print("weight after step:{}".format(weight.data))
实验结果

在这里插入图片描述

(2)zero_grad()
  # ------------------------ zero_grad --------------------
flag = 0
# flag = 1
if flag:

    print("weight before step:{}".format(weight.data))
    optimizer.step()        # 修改lr=1 0.1观察结果
    print("weight after step:{}".format(weight.data))

    print("weight in optimizer:{}\nweight in weight:{}\n".format(id(optimizer.param_groups[0]['params'][0]), id(weight)))

    print("weight.grad is {}\n".format(weight.grad))
    optimizer.zero_grad()
    print("after optimizer.zero_grad(), weight.grad is\n{}".format(weight.grad))

在这里插入图片描述

实验结果

在这里插入图片描述

(3)add_param_group
 # ------------------- add_param_group -------------------
flag = 0
# flag = 1
if flag:
//这一优化器当前已经管理了一组参数 weight
    print("optimizer.param_groups is\n{}".format(optimizer.param_groups))
//想再添加一组参数,设置这组参数学习率lr=1e-04
//首先设置好要添加的这一组参数 w2
    w2 = torch.randn((3, 3), requires_grad=True)
//构建字典,key中 params中放置参数 w2 ,利用方法 add_param_group把这一组参数加进来
    optimizer.add_param_group({
    
    "params": w2, 'lr': 0.0001})

    print("optimizer.param_groups is\n{}".format(optimizer.param_groups))
实验结果

在这里插入图片描述

(4)state_dict和load_state_dict
# ------------------------ state_dict ------------
# flag = 0
flag = 1
if flag:

    optimizer = optim.SGD([weight], lr=0.1, momentum=0.9)
    opt_state_dict = optimizer.state_dict()
//打印step()更新之前的状态信息字典
    print("state_dict before step:\n", opt_state_dict)
//10步更新
    for i in range(10):
        optimizer.step()
//打印step()更新之后的状态信息字典
    print("state_dict after step:\n", optimizer.state_dict())
//保存更新之后的状态信息字典在当前文件夹下名为 optimizer_state_dict.pkl的文件
    torch.save(optimizer.state_dict(), os.path.join(BASE_DIR, "optimizer_state_dict.pkl"))
    
# -------------------------load state_dict --------------
flag = 0
# flag = 1
if flag:
//重新构建优化器
    optimizer = optim.SGD([weight], lr=0.1, momentum=0.9)
//创建加载状态名
    state_dict = torch.load(os.path.join(BASE_DIR, "optimizer_state_dict.pkl"))

    print("state_dict before load state:\n", optimizer.state_dict())
//利用load_state_dict方法加载状态信息,接着当前状态往下训练
    optimizer.load_state_dict(state_dict)
    print("state_dict after load state:\n", optimizer.state_dict())

在这里插入图片描述
图7 state_dict创建的状态信息文件

猜你喜欢

转载自blog.csdn.net/qq_43263543/article/details/123611459
今日推荐