pytorch学习笔记十二:优化器

前言
机器学习中的五个步骤:数据 ——> 模型 ——> 损失函数 ——> 优化器 ——> 迭代训练,通过前向传播,得到模型的输出和真实标签之间的差异,也就是损失函数,有了损失函数之后,模型反向传播得到参数的梯度,接下来就是优化器根据这个梯度去更新参数,使得模型的损失不断降低,那么优化器是如何做到的呢?分别从三个方面了解一下,优化器的概念,优化器的属性和方法,常用的优化器。

一、什么是优化器

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

在更新参数时一般使用梯度下降的方式去更新,关于梯度下降,有如下几个相关的概念:

  • 导数:函数在指定坐标轴上的变化率;
  • 方向导数:指定方向上的变化率;
  • 梯度:一个向量,方向为方向导数取得最大值的方向。

所以梯度是一个向量,方向是导数取得最大值的方向,也就是增长最快的方向,而梯度下降是沿着梯度的负方向去变化,这样函数的下降也是最快的。所以采用梯度下降的方式来更新权值。
关于梯度下降的总结可参考:链接

二、优化器的属性和方法

pytorch中优化器的基本属性:
在这里插入图片描述
基本属性:
defaults:优化器超参数,存储学习率、momentum的值、衰减系数等;
state:参数的缓存,如momentum的缓存(使用前几次梯度进行平均)
param_groups:管理的参数组,这是个列表,每一个元素是一个字典,字典中key的值是真正的参数;
_step_count:记录更新的参数,学习率调整中使用,比如迭代100次之后更新学习率,这里记录100.

基本方法:
在这里插入图片描述
zero_grad():清空管理参数的梯度,pytorch中的参数的梯度在计算过程中是不自动清零的,所以需要这个方法将参数的梯度清零,具体实现步骤如上图所示;
step:执行一步更新
add_param_group():添加参数组,优化器可以管理很多参数,可以对这些参数分组,对不同组的参数设置不同的超参数,比如模型 finetune 中,希望前面特征提取的那些层学习率小一些,而后面新加的层学习率大一些更新快一点,就可以用这个方法
在这里插入图片描述
state_dict():获取优化器当前状态的字典
load_state_dict():加载状态信息字典,这两个方法用于模型断点的一个续训练,在模型训练时一般在设定epoch之后要保存当前的状态信息。
在这里插入图片描述
下面从人民币的二分类案例中学习优化器是如何构建的,以及是如何运行的
在train.py文件中设置断点

# 第一处 优化器的构建
optimizer = optim.SGD(net.parameters(), lr=LR, momentum=0.9)
# 第二处 
optimizer.zero_grad()
# 第三处
optimizer.step()

debug进入sgd.py文件中的SGD类:
在这里插入图片描述
SGD类是继承于optimizer的,所以将代码运行至父类初始化这一行,看是如何进行初始化的:
在这里插入图片描述
这个就是optimizer的__init__初始化部分了,可以看到它的属性和初始化方法,其中最重要的是参数组是如何添加的。另外defaults中存的超参数有:
在这里插入图片描述
跳出这个函数,执行完参数初始化后如下:
在这里插入图片描述
这就是优化器的初始化工作,初始化完之后,先进行梯度清空,然后更新梯度即可。
在这里插入图片描述
下面来了解一下优化器具体方法的使用
1、step():一次梯度下降更新参数

# 设置权重和梯度
weight = torch.randn((2, 2), requires_grad=True)
weight.grad = torch.ones((2, 2))
# 优化器 学习率设置为0.1
optimizer = optim.SGD([weight], lr=0.1)

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

输出结果:
在这里插入图片描述
2、zero_grad():将梯度清零

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

#这里打印optimizer中para_groups中的weight地址和weight本身的地址
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))

输出结果:
在这里插入图片描述
使用optimizer.zero_grad()之后可看到参数的梯度就会变为0;另外可看到optimizer的param_groups中的weight地址和weight本身的地址是一样的,这说明optimizer的param_groups存储的是weight的引用,并不是复制一份,这样可以节省内存,在pytorch学习的第一节中说过叶子结点不能进行原位操作,是因为正向传播过程中的参数反向传播中是要用的,并且反向传播过程中的存的是引用,所以正向传播之后参数是不能修改的。

3.add_param_group(): 添加参数组 ,这个是在模型的迁移学习中非常实用的一个方法

print("optimizer.param_groups is\n{}".format(optimizer.param_groups))

w2 = torch.randn((3, 3), requires_grad=True)
# 添加一个w2参数,设置学习率是0.0001
optimizer.add_param_group({
   "params": w2, 'lr': 0.0001})

print("optimizer.param_groups is\n{}".format(optimizer.param_groups))

输出结果:

optimizer.param_groups is
[{
   'params': [tensor([[0.6614, 0.2669],
        [0.0617, 0.6213]], requires_grad=True)], 'lr': 0.1, 'momentum': 0, 'dampening': 0, 'weight_decay': 0, 'nesterov': False}]
optimizer.param_groups is
[{
   'params': [tensor([[0.6614, 0.2669],
        [0.0617, 0.6213]], requires_grad=True)], 'lr': 0.1, 'momentum': 0, 'dampening': 0, 'weight_decay': 0, 'nesterov': False}, {
   'params': [tensor([[-0.4519, -0.1661, -1.5228],
        [ 0.3817, -1.0276, -0.5631],
        [-0.8923, -0.0583, -0.1955]], requires_grad=True)], 'lr': 0.0001, 'momentum': 0, 'dampening': 0, 'weight_decay': 0, 'nesterov': False}]

4.state_dict()和load_state_dict()

这两个方法用于保存和加载优化器的一个状态信息,通常用在断点的续训练, 比如训练一个模型,训练了10次停电了, 那么再来电的时候就得需要从头开始训练,但是如果有了这两个方法,那么就可以再训练的时候接着上次的次数继续, 所以这两个方法也非常实用。

首先是state_dict()

optimizer = optim.SGD([weight], lr=0.1, momentum=0.9)
opt_state_dict = optimizer.state_dict()

print("state_dict before step:\n", opt_state_dict)

for i in range(10):
    optimizer.step()

print("state_dict after step:\n", optimizer.state_dict())
# 训练10次之后将模型的参数保存下来
torch.save(optimizer.state_dict(), os.path.join(BASE_DIR, "optimizer_state_dict.pkl")
  • 14
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值