优化器Optimizer

什么是优化器

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

导数:函数在指定坐标轴上的变化率
方向导数:指定方向上的变化率(二元及以上函数,偏导数)
梯度:一个向量,方向是使得方向导数取得最大值的方向

Pytorch的Optimizer

参数

  • defaults:优化器超参数
  • state:参数的缓存,如momentum的缓存
  • param_groups:管理的参数组
  • _step_count:记录更新次数,学习率调整中使用

基本方法:

  • zero_grad():清空所管理参数的梯度

pytorch特性:张量梯度不会自动清零

  • step():执行一步更新

  • add_param_group():添加参数组

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

  • load_state_dict():加载状态信息字典

使用代码帮助理解和学习

import os
import torch
import torch.optim as optimBASE_DIR = os.path.dirname(os.path.abspath(__file__))weight = torch.randn((2, 2), requires_grad=True)
weight.grad = torch.ones((2, 2))# 需要传入一个可迭代对象
optimizer = optim.SGD([weight], lr=1)print("weight before step:{}".format(weight.data))
optimizer.step()
print("weight after step:{}".format(weight.data))weight before step:tensor([[-0.0606, -0.3197],[ 1.4949, -0.8007]])
weight after step:tensor([[-1.0606, -1.3197],[ 0.4949, -1.8007]])

weight = weight - lr * weight.grad
上面学习率是1,把学习率改为0.1试一下

optimizer = optim.SGD([weight], lr=0.1)weight before step:tensor([[ 0.3901,  0.2167],[-0.3428, -0.7151]])
weight after step:tensor([[ 0.2901,  0.1167],[-0.4428, -0.8151]])

接着上面的代码,我们再看一下add_param_group方法

# add_param_group方法
print("optimizer.param_groups is \n{}".format(optimizer.param_groups))w2 = torch.randn((3, 3), requires_grad=True)
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.1749, -0.2018],[ 0.0080,  0.3517]], requires_grad=True)], 'lr': 0.1, 'momentum': 0, 'dampening': 0, 'weight_decay': 0, 'nesterov': False}]optimizer.param_groups is
[{'params': [tensor([[ 0.1749, -0.2018],[ 0.0080,  0.3517]], requires_grad=True)], 'lr': 0.1, 'momentum': 0, 'dampening': 0, 'weight_decay': 0, 'nesterov': False}, {'params': [tensor([[ 0.4538, -0.8521, -1.3081],[-0.0158, -0.2708,  0.0302],[-0.3751, -0.1052, -0.3030]], requires_grad=True)], 'lr': 0.0001, 'momentum': 0, 'dampening': 0, 'weight_decay': 0, 'nesterov': False}]

关于zero_grad()step()state_dict()load_state_dict()这几个方法比较简单就不再赘述。

SGD随机梯度下降

learning_rate学习率

这里学习率为1,可以看到并没有达到梯度下降的效果,反而y值越来越大,这是因为更新的步伐太大。

我们以y = 4*x^2这个函数举例,将y值作为要优化的损失值,那么梯度下降的过程就是为了找到y的最小值(即此函数曲线的最小值);如果我们把学习率设置为0.2,就可以得到这样一个梯度下降的图

def func(x):return torch.pow(2*x, 2)x = torch.tensor([2.], requires_grad=True)
iter_rec, loss_rec, x_rec = list(), list(), list()
lr = 0.2
max_iteration = 20for i in range(max_iteration):y = func(x)y.backward()print("iter:{}, x:{:8}, x.grad:{:8}, loss:{:10}".format(i, x.detach().numpy()[0], x.grad.detach().numpy()[0], y.item()))x_rec.append(x.item())x.data.sub_(lr * x.grad)x.grad.zero_()iter_rec.append(i)loss_rec.append(y.item())plt.subplot(121).plot(iter_rec, loss_rec, '-ro')
plt.xlabel("Iteration")
plt.ylabel("Loss value")x_t = torch.linspace(-3, 3, 100)
y = func(x_t)
plt.subplot(122).plot(x_t.numpy(), y.numpy(), label="y = 4*x^2")
plt.grid()y_rec = [func(torch.tensor(i)).item() for i in x_rec]
plt.subplot(122).plot(x_rec, y_rec, '-ro')
plt.legend()
plt.show()

这里其实存在一个下降速度更快的学习率,那就是0.125,一步就可以将loss更新为0,这是因为我们已经了这个函数表达式,而在实际神经网络模型训练的过程中,是不知道所谓的函数表达式的,所以只能选取一个相对较小的学习率,然后以训练更多的迭代次数来达到最优的loss。

动量(Momentum,又叫冲量)

结合当前梯度与上一次更新信息,用于当前更新

为什么会出现动量这个概念?

当学习率比较小时,往往更新比较慢,通过引入动量,使得后续的更新受到前面更新的影响,可以更快的进行梯度下降。

指数加权平均:当前时刻的平均值(Vt)与当前参数值(θ)和前一时刻的平均值(Vt-1)的关系。

根据上述公式进行迭代展开,因为0<β<1,当前时刻的平均值受越近时刻的影响越大(更近的时刻其所占的权重更高),越远时刻的影响越小,我们可以通过下面作图来看到这一变化。

import numpy as np
import matplotlib.pyplot as pltdef exp_w_func(beta, time_list):return [(1-beta) * np.power(beta, exp) for exp in time_list]beta = 0.9
num_point = 100
time_list = np.arange(num_point).tolist()weights = exp_w_func(beta, time_list)plt.plot(time_list, weights, '-ro', label="Beta: {}\n = B * (1-B)^t".format(beta))
plt.xlabel("time")
plt.ylabel("weight")
plt.legend()
plt.title("exponentially weighted average")
plt.show()

这里β是一个超参数,设置不同的值,其对于过去时刻的权重计算如下图

beta_list = [0.98, 0.95, 0.9, 0.8]
w_list = [exp_w_func(beta, time_list) for beta in beta_list]
for i, w in enumerate(w_list):plt.plot(time_list, w, label="Beta: {}".format(beta_list[i]))plt.xlabel("time")plt.ylabel("weight")
plt.legend()
plt.show()

从图中可以得到这一结论:β值越小,记忆周期越短,β值越大,记忆周期越长

pytorch中带有momentum参数的更新公式

对于y=4*x^2这个例子,在没有momentum时,我们对比学习率分别为0.01和0.03会发现,0.03收敛的更快。

如果我们给learning_rate=0.01增加momentum参数,会发现其可以先一步0.03的学习率到达loss的较小值,但是因为动量较大的因素,在达到了最小值后还会反弹到一个大的值。

Pytorch中的优化器

optim.SGD

主要参数:

  • params:管理的参数组
  • lr:学习率
  • momentum:动量系数,贝塔
  • weight_decayL2正则化系数
  • nesterov:是否采用NAG,默认False

optim.Adagrad:自适应学习率梯度下降法

optim.RMSprop:Adagrad的改进

optim.Adadelta:Adagrad的改进

optim.Adam:RMSprop结合Momentum

optim.Adamax:Adam增加学习率上限

optim.SparseAdam:稀疏版的Adam

optim.ASGD:随机平均梯度下降

optim.Rprop:弹性反向传播

optim.LBFGS:BFGS的改进

学习率调整

前期学习率大,后期学习率小

pytorch中调整学习率的基类

class _LRScheduler

主要属性:

  • optimizer:关联的优化器
  • last_epoch:记录epoch数
  • base_lrs:记录初始学习率

主要方法:

  • step():更新下一个epoch的学习率
  • get_lr():虚函数,计算下一个epoch的学习率

StepLR

等间隔调整学习率

主要参数:

  • step_size:调整间隔数
  • gamma:调整系数

调整方式:lr = lr * gamma

import torch
import torch.optim as optim
import matplotlib.pyplot as pltLR = 0.1
iteration = 10
max_epoch = 200weights = torch.randn((1,), requires_grad=True)
target = torch.zeros((1, ))optimizer = optim.SGD([weights], lr=LR, momentum=0.9)scheduler_lr = optim.lr_scheduler.StepLR(optimizer, step_size=50, gamma=0.1)  # 设置学习率下降策略lr_list, epoch_list = list(), list()
for epoch in range(max_epoch):lr_list.append(scheduler_lr.get_lr())epoch_list.append(epoch)for i in range(iteration):loss = torch.pow((weights-target), 2)loss.backward()optimizer.step()optimizer.zero_grad()scheduler_lr.step()plt.plot(epoch_list, lr_list, label='Step LR Scheduler')
plt.xlabel('Epoch')
plt.ylabel('Learning Rate')
plt.legend()
plt.show()

MultiStepLR

功能:按给定间隔调整学习率

主要参数:

  • milestones:设定调整时刻数
  • gamma:调整系数

调整方式:lr = lr * gamma

# MultiStepLR
milestones = [50, 125, 160]
scheduler_lr = optim.lr_scheduler.MultiStepLR(optimizer, milestones=milestones, gamma=0.1)

只需要改变这里代码,其他部分与StepLR中基本一致

ExponentialLR

功能:按指数衰减调整学习率

主要参数:

  • gamma:指数的底

调整方式:lr = lr * gamma ** epoch

# Exponential LR
gamma = 0.95
scheduler_lr = optim.lr_scheduler.ExponentialLR(optimizer, gamma=gamma)

CosineAnnealingLR

功能:余弦周期调整学习率

主要参数:

  • T_max:下降周期
  • eta_min:学习率下限

调整方式:

# CosineAnnealingLR
t_max = 50
scheduler_lr = optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=t_max, eta_min=0)

ReduceLRonPlateau

功能:监控指标,当指标不再变化则调整学习率

主要参数:

  • mode:min/max,两种模式,min观察下降,max观察上升
  • factor:调整系数
  • patience:“耐心”,接受几次不变化
  • cooldown:“冷却时间”,停止监控一段时间
  • verbose:是否打印日志
  • min_lr:学习率下限
  • eps:学习率衰减最小值
# Reduce LR on Plateau
loss_value = 0.5
accuray = 0.9factor = 0.1
mode = 'min'
patience = 10
cooldown = 10
min_lr = 1e-4
verbose = Truescheduler_lr = optim.lr_scheduler.ReduceLROnPlateau(optimizer, factor=factor, mode=mode, patience=patience,cooldown=cooldown, min_lr=min_lr, verbose=verbose)
for epoch in range(max_epoch):for i in range(iteration):optimizer.step()optimizer.zero_grad()# if epoch == 5:# loss_value = 0.4# 把要监控的指标传进去scheduler_lr.step(loss_value)Epoch    12: reducing learning rate of group 0 to 1.0000e-02.
Epoch    33: reducing learning rate of group 0 to 1.0000e-03.
Epoch    54: reducing learning rate of group 0 to 1.0000e-04.

LambdaLR

功能:自定义调整策略

主要参数:

  • lr_lambda:function or list
# lambda LRlr_init = 0.1
weights_1 = torch.randn((6, 3, 5, 5))
weights_2 = torch.ones((5, 5))optimizer = optim.SGD([{'params': [weights_1]},{'params': [weights_2]}
], lr=lr_init)lambda1 = lambda epoch: 0.1 ** (epoch // 20)
lambda2 = lambda epoch: 0.95 ** epochscheduler = torch.optim.lr_scheduler.LambdaLR(optimizer, lr_lambda=[lambda1, lambda2])lr_list, epoch_list = list(), list()
for epoch in range(max_epoch):for i in range(iteration):optimizer.step()optimizer.zero_grad()scheduler.step()lr_list.append(scheduler.get_lr())epoch_list.append(epoch)print('epoch: {:5d}, lr:{}'.format(epoch, scheduler.get_lr()))

transformer包中的warmup scheduler

我们都知道模型在开始时可以采用较大的学习率,然后快速的收敛,随着模型训练到后期需要减少学习率才能使得模型进一步学习,如果持续使用较大的学习率会使得模型最后无法走到一个局部的最优点,所以有一种叫做warmup的学习率调整策略,偏向于前期采用较大的学习率,后期逐渐减小学习率。

在transformer包中就已经封装了类似的方法:

import transformersdef _get_scheduler(self, optimizer, scheduler: str, warmup_steps: int, t_total: int):"""Returns the correct learning rate scheduler"""scheduler = scheduler.lower()if scheduler == 'constantlr':return transformers.get_constant_schedule(optimizer)elif scheduler == 'warmupconstant':return transformers.get_constant_schedule_with_warmup(optimizer, num_warmup_steps=warmup_steps)elif scheduler == 'warmuplinear':return transformers.get_linear_schedule_with_warmup(optimizer, num_warmup_steps=warmup_steps, num_training_steps=t_total)elif scheduler == 'warmupcosine':return transformers.get_cosine_schedule_with_warmup(optimizer, num_warmup_steps=warmup_steps, num_training_steps=t_total)elif scheduler == 'warmupcosinewithhardrestarts':return transformers.get_cosine_with_hard_restarts_schedule_with_warmup(optimizer, num_warmup_steps=warmup_steps, num_training_steps=t_total)else:raise ValueError("Unknown scheduler {}".format(scheduler))

这里我们详细看一下get_linear_schedule_with_warmup来进一步理解该方法是如何调整学习率的。

from transformers import AdamW, get_linear_schedule_with_warmuplr_init = 0.1
weights_1 = torch.randn((6, 3, 5, 5))optimizer = AdamW([{"params": [weights_1]}], lr=lr_init, eps=1e-4)
epochs = 10
batch_size = 32
total_steps = 1000
warm_up_ratio = 0.1  # 定义要预热的step
scheduler = get_linear_schedule_with_warmup(optimizer,num_warmup_steps=warm_up_ratio * total_steps,num_training_steps=total_steps)
lr_list = []
for epoch in range(epochs):for i in range(100):optimizer.step()scheduler.step()optimizer.zero_grad()lr_list.append(scheduler.get_lr())plt.plot(range(total_steps), lr_list)
plt.xlabel("steps")
plt.ylabel("learning_rate")
plt.legend()
plt.show()

可以看到,根据我们预先设置在总的训练步数的一个比例上,这里是1000的10%,那就是前100步,学习率是从0线性的增长到我们预先设定的学习率,之后线性的减小到0。

num_warmup_steps参数设置为0时,learning rate没有预热的上升过程,只有从初始设定的learning rate 逐渐衰减到0的过程,也就是get_constant_schedule方法。

那其实还有一个问题,类似Adam之类的优化器,本身不是自适应学习率的吗?为什么还需要再引入相关方法来调整学习率呢?

因为常用的神经网络优化器Adam的自适应学习率并不是真正意义上的自适应。

从统计的角度看,Adam的自适应原理也是根据统计对梯度进行修正,但依然离不开前面设置的学习率。如果学习率设置的过大,则会导致模型发散,造成收敛较慢或陷入局部最小值点,因为过大的学习率会在优化过程中跳过最优解或次优解。
所以Adam在调整学习率时也是根据预先设置的学习率来调整的,如果我们本身可以控制学习率的变化,再通过Adam来微调可以得到更好的效果。

Pytorch优化器Optimizer相关推荐

  1. Pytorch —— 优化器Optimizer(二)

    1.learning rate学习率 梯度下降:wi+1=wi−LR∗g(wi)w_{i+1}=w_{i}-LR*g\left(w_{i}\right)wi+1​=wi​−LR∗g(wi​)梯度是沿着 ...

  2. 【PyTorch基础教程9】优化器optimizer和训练过程

    学习总结 (1)每个优化器都是一个类,一定要进行实例化才能使用,比如: class Net(nn.Moddule):··· net = Net() optim = torch.optim.SGD(ne ...

  3. Pytorch框架之优化器 Optimizer

    Pytorch框架之优化器 Optimizer 基本用法 优化器主要是在模型训练阶段对模型可学习参数进行更新, 常用优化器有 SGD,RMSprop,Adam等 优化器初始化时传入传入模型的可学习参数 ...

  4. 一文详解Pytorch中的优化器Optimizer

    本文将结合源码与代码示例详细解析Optimizer的五大方法. 1. 前言 优化器主要用在模型训练阶段,用于更新模型中可学习的参数.torch.optim提供了多种优化器接口,比如Adam.RAdam ...

  5. pytorch优化器与学习率设置详解

    点击上方"视学算法",选择加"星标"或"置顶" 重磅干货,第一时间送达 作者 | 小新 来源 | https://a.3durl.cn/Yr ...

  6. Pyotrch —— 优化器Optimizer(一)

    目录 1.什么是优化器 2.optimizer的属性 3.optimizer的方法 4.代码分析 5.优化器基本方法的使用 1.什么是优化器 Pytorch优化器:管理并更新模型中可学习参数的值,使得 ...

  7. pytorch优化器学习率调整策略以及正确用法

    优化器 optimzier优化器的作用:优化器就是需要根据网络反向传播的梯度信息来更新网络的参数,以起到降低loss函数计算值的作用. 从优化器的作用出发,要使得优化器能够起作用,需要主要两个东西: ...

  8. Pytorch优化器全总结(三)牛顿法、BFGS、L-BFGS 含代码

    目录 写在前面 一.牛顿法 1.看图理解牛顿法 2.公式推导-三角函数 3.公式推导-二阶泰勒展开 二.BFGS公式推导 三.L-BFGS 四.算法迭代过程 五.代码实现 1.torch.optim. ...

  9. Pytorch优化器全总结(四)常用优化器性能对比 含代码

    目录 写在前面 一.优化器介绍 1.SGD+Momentum 2.Adagrad 3.Adadelta 4.RMSprop 5.Adam 6.Adamax 7.AdaW 8.L-BFGS 二.优化器对 ...

最新文章

  1. aspx 与 ashx cs
  2. log4j用于读取.xml文件的出现了错误,类加载器.getResource(user.xml).getPath()返回路径空格变成了%20...
  3. python反射机制_详解python之反射机制
  4. html 未来元素绑定事件,jquery on如何给未来元素绑定事件?
  5. 【学术相关】CVPR2021最新接收论文合集!22个方向100+篇论文汇总|持续更新
  6. 25行代码AC_蓝桥杯 2017A组省赛第九题 分巧克力(暴力优化)
  7. C 程序的存储空间记录
  8. Kibana安装配置
  9. jpa和hibernate_JPA和Hibernate级联类型的初学者指南
  10. “PE文件格式”1.9版 完整译文
  11. python 中空NULL的表示
  12. java1.8 64位,jdk 1.8 64位 下载_jdk 1.8 64位 官方版_魅蓝下载
  13. U盘刻录ubuntu系统
  14. GBK/GBK2312字库寻址及使用原理
  15. 计算机网络实验指导书 pdf,计算机网络实验指导书(新版).pdf
  16. PWA系列 - Web Push 技术
  17. 浏览器主页被篡改解决方法
  18. thermal系列(6)-thermal-engine守护进程
  19. 20组免费的Photoshop渐变效果素材
  20. 设计水杯,门锁的测试用例

热门文章

  1. JSON对象转java对象 JSON数组转LIST数组
  2. html里用js实现随机抽奖,js实现简单随机抽奖的方法
  3. Mybase使用教程-不古出品
  4. EPI晶片的表面微观粗糙度对湿化学处理的依赖性
  5. 研华PCI1716L的C#编程
  6. 软技能入门《质量》系列 -- 密切追踪
  7. 【信息系统项目管理师】第十五章 知识产权与法律法规(考点汇总篇)
  8. mysql重新初始化
  9. Win7 各种语言包下载
  10. 读取gmsl接口数据,rosbag录制话题