Pytorch中的学习率衰减及其用法详解


Posted in Python onJune 05, 2021

Pytorch 学习率衰减及其用法

学习率衰减是一个非常有效的炼丹技巧之一,在神经网络的训练过程中,当accuracy出现震荡或loss不再下降时,进行适当的学习率衰减是一个行之有效的手段,很多时候能明显提高accuracy。

Pytorch中有两种学习率调整(衰减)方法:

使用库函数进行调整;

手动调整。

1. 使用库函数进行调整:

Pytorch学习率调整策略通过 torch.optim.lr_sheduler 接口实现。pytorch提供的学习率调整策略分为三大类,分别是:

(1)有序调整:等间隔调整(Step),多间隔调整(MultiStep),指数衰减(Exponential),余弦退火(CosineAnnealing);

(2)自适应调整:依训练状况伺机而变,通过监测某个指标的变化情况(loss、accuracy),当该指标不怎么变化时,就是调整学习率的时机(ReduceLROnPlateau);

(3)自定义调整:通过自定义关于epoch的lambda函数调整学习率(LambdaLR)。

在每个epoch的训练中,使用scheduler.step()语句进行学习率更新,此方法类似于optimizer.step()更新模型参数,即一次epoch对应一次scheduler.step()。但在mini-batch训练中,每个mini-bitch对应一个optimizer.step()。即用法如下:

optimizer = torch.optim.SGD(model.parameters(), lr=0.1)
scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=30, gamma=0.1)
 
def train(...):
    for i, data in enumerate(train_loader):
        ......
        y_ = model(x)
        loss = criterion(y_,y)
        loss.backward()
        optimizer.step()
        ......
 
for epoch in range(epochs):
    scheduler.step()
    train(...)
    test(...)

(1) 等间隔调整学习率 StepLR

torch.optim.lr_scheduler.StepLR(optimizer, step_size, gamma=0.1, last_epoch=-1)

每训练step_size个epoch,学习率调整为lr=lr*gamma.

以下内容中都将epoch和step对等,因为每个epoch中只进行一次scheduler.step(),实则该step指scheduler.step()中的step, 即step_size指scheduler.step()进行的次数。

参数:

optimizer: 神经网络训练中使用的优化器,如optimizer=torch.optim.SGD(...)

step_size(int): 学习率下降间隔数,单位是epoch,而不是iteration.

gamma(float): 学习率调整倍数,默认为0.1

last_epoch(int): 上一个epoch数,这个变量用来指示学习率是否需要调整。当last_epoch符合设定的间隔时,就会对学习率进行调整;当为-1时,学习率设置为初始值。

(2) 多间隔调整学习率 MultiStepLR

跟(1)类似,但学习率调整的间隔并不是相等的,如epoch=10时调整一次,epoch=30时调整一次,epoch=80时调整一次…

torch.optim.lr_sheduler.MultiStepLR(optimizer, milestones, gamma=0.1, last_epoch=-1)

参数:

milestone(list): 一个列表参数,表示多个学习率需要调整的epoch值,如milestones=[10, 30, 80].

其它参数同(1)。

(3) 指数衰减调整学习率 ExponentialLR

学习率呈指数型衰减,每训练一个epoch,lr=lrgamma*epoch,即

torch.optim.lr_sheduler.ExponentialLR(optimizer, gamma, last_epoch)

参数:

gamma(float):学习率调整倍数的底数,指数为epoch,初始值我lr, 倍数为

其它参数同上。

(4) 余弦退火函数调整学习率:

学习率呈余弦函数型衰减,并以2*T_max为余弦函数周期,epoch=0对应余弦型学习率调整曲线的,epoch=T_max对应余弦型学习率调整曲线的eta_min处,随着epoch>T_max,学习率随epoch增加逐渐上升,整个走势同cos(x)。

torch.optim.lr_sheduler.CosineAnnealingLR(optimizer, T_max, eta_min=0, last_epoch=-1)

参数:

T_max(int): 学习率下降到最小值时的epoch数,即当epoch=T_max时,学习率下降到余弦函数最小值,当epoch>T_max时,学习率将增大;

eta_min: 学习率调整的最小值,即epoch=T_max时,eta_min, 默认为0.

其它参数同上。

(5) 根据指标调整学习率 ReduceLROnPlateau

当某指标(loss或accuracy)在最近几个epoch中都没有变化(下降或升高超过给定阈值)时,调整学习率。

如当验证集的loss不再下降是,调整学习率;或监察验证集的accuracy不再升高时,调整学习率。

torch.optim.lr_sheduler.ReduceLROnPlateau(optimizer, mode='min', factor=0.1, patience=10,
 verbose=False, threshold=0.0001, threshold_mode='rel', cooldown=0, min_lr=0, eps=1e-08)

参数:

mode(str): 模式选择,有min和max两种模式,min表示当指标不再降低(如监测loss),max表示当指标不再升高(如监测accuracy)。

factor(float): 学习率调整倍数,同前面的gamma,当监测指标达到要求时,lr=lr×factor。

patience(int): 忍受该指标多少个epoch不变化,当忍无可忍时,调整学习率。

verbose(bool): 是否打印学习率信息,print( 'Epoch {:5d} reducing learning rate of group {} to {:.4e}.'.format(epoch, i, new_lr), 默认为False, 即不打印该信息。

threshold_mode (str): 选择判断指标是否达最优的模式,有两种模式:rel 和 abs.

当threshold_mode == rel, 并且 mode == max时,dynamic_threshold = best * (1 + threshold);

当threshold_mode == rel, 并且 mode == min时,dynamic_threshold = best * (1 - threshold);

当threshold_mode == abs, 并且 mode == max时,dynamic_threshold = best + threshold;

当threshold_mode == abs, 并且 mode == min时,dynamic_threshold = best - threshold;

threshold(float): 配合threshold_mode使用。

cooldown(int): “冷却时间”,当调整学习率之后,让学习率调整策略冷静一下,让模型在训练一段时间,再重启监测模式。

min_lr(float or list): 学习率下限,可为float,或者list,当有多个参数组时,可用list进行设置。

eps(float): 学习率衰减的最小值,当学习率的变化值小于eps时,则不调整学习率。

optimizer = torch.optim.SGD(model.parameters(), args.lr,
 momentum=args.momentum, weight_decay=args.weight_decay)
scheduler = ReducelROnPlateau(optimizer,'min')
for epoch in range( args.start epoch, args.epochs ):
    train(train_loader , model, criterion, optimizer, epoch )
    result_avg, loss_val = validate(val_loader, model, criterion, epoch)
    # Note that step should be called after validate()
    scheduler.step(loss_val )

(6) 自定义调整学习率 LambdaLR

为不同参数组设定不同学习率调整策略。调整规则为:

lr = base_lr * lambda(self.last_epoch)

在fine-tune中特别有用,我们不仅可以为不同层设置不同的学习率,还可以为不同层设置不同的学习率调整策略。

torch.optim.lr_scheduler.LambdaLR(optimizer, lr_lambda, last_epoch=-1)

参数:

lr_lambda(function or list): 自定义计算学习率调整倍数的函数,通常时epoch的函数,当有多个参数组时,设为list.

其它参数同上。

例:

ignored_params = list(map(id, net.fc3.parameters()))
base_params = filter(lambda p: id(p) not in ignored_params, net.parameters())
optimizer = optim.SGD([
        {'params': base_params},
        {'params': net.fc3.parameters(), 'lr': 0.001*100}], 0.001,          momentum=0.9,weight_decay=1e-4)
 # Assuming optimizer has two groups.
lambda1 = lambda epoch: epoch // 3
lambda2 = lambda epoch: 0.95 ** epoch
scheduler = LambdaLR(optimizer, lr_lambda=[lambda1, lambda2])
for epoch in range(100):
    train(...)
    validate(...)
    scheduler.step()
    print('epoch: ', i, 'lr: ', scheduler.get_lr())
    
输出:
epoch: 0 lr: [0.0, 0.1]
epoch: 1 lr: [0.0, 0.095]
epoch: 2 lr: [0.0, 0.09025]
epoch: 3 lr: [0.001, 0.0857375]
epoch: 4 lr: [0.001, 0.081450625]
epoch: 5 lr: [0.001, 0.07737809374999999]
epoch: 6 lr: [0.002, 0.07350918906249998]
epoch: 7 lr: [0.002, 0.06983372960937498]
epoch: 8 lr: [0.002, 0.06634204312890622]
epoch: 9 lr: [0.003, 0.0630249409724609]
为什么第一个参数组的学习率会是 0 呢? 来看看学习率是如何计算的。
第一个参数组的初始学习率设置为 0.001, 
lambda1 = lambda epoch: epoch // 3,
第 1 个 epoch 时,由 lr = base_lr * lmbda(self.last_epoch),
可知道 lr = 0.001 *(0//3) ,又因为 1//3 等于 0,所以导致学习率为 0。
第二个参数组的学习率变化,就很容易看啦,初始为 0.1, lr = 0.1 * 0.95^epoch ,当
epoch 为 0 时, lr=0.1 , epoch 为 1 时, lr=0.1*0.95。
# -*- coding:utf-8 -*-
'''本文件用于测试pytorch学习率调整策略'''
__author__ = 'puxitong from UESTC'
 
import torch
import torch.optim as optim
from torch.optim import lr_scheduler
from torchvision.models import AlexNet
import matplotlib.pyplot as plt 
 
model = AlexNet(num_classes=2)
optimizer = optim.SGD(params=model.parameters(), lr=0.1)
 
# 等间隔调整学习率,每训练step_size个epoch,lr*gamma
# scheduler = lr_scheduler.StepLR(optimizer, step_size=30, gamma=0.1)
 
# 多间隔调整学习率,每训练至milestones中的epoch,lr*gamma
# scheduler = lr_scheduler.MultiStepLR(optimizer, milestones=[10, 30, 80], gamma=0.1)
 
# 指数学习率衰减,lr*gamma**epoch
# scheduler = lr_scheduler.ExponentialLR(optimizer, gamma=0.9)
 
# 余弦退火学习率衰减,T_max表示半个周期,lr的初始值作为余弦函数0处的极大值逐渐开始下降,
# 在epoch=T_max时lr降至最小值,即pi/2处,然后进入后半个周期,lr增大
# scheduler = lr_scheduler.CosineAnnealingLR(optimizer, T_max=100, eta_min=0)
 
plt.figure()
x = list(range(100))
y = []
for epoch in range(100):
    scheduler.step()
    y.append(scheduler.get_lr()[0])
 
plt.plot(x, y)
plt.show()

2. 手动调整学习率:

def adjust_learning_rate(optimizer, epoch):
    """Sets the learning rate to the initial LR decayed by 10 every 30 epochs"""
    lr = args.lr * (0.1 ** (epoch // 30))
    for param_group in optimizer.param_groups:
        param_group['lr'] = lr
def adjust_learning_rate(epoch, lr):
    if epoch <= 81:  # 32k iterations
      return lr
    elif epoch <= 122:  # 48k iterations
      return lr/10
    else:
      return lr/100
for epoch in range(epochs):
    lr = adjust_learning_rate(optimizer, epoch)  # 调整学习率
    optimizer = optim.SGD(net.parameters(), lr=lr, momentum=0.9, weight_decay=5e-4)
    ......
    optimizer.step()  # 采用新的学习率进行参数更新

什么是param_groups?

optimizer通过param_group来管理参数组.param_group中保存了参数组及其对应的学习率,动量等等.所以我们可以通过更改param_group['lr']的值来更改对应参数组的学习率

# 例1:有两个`param_group`即,len(optim.param_groups)==2
optim.SGD([
                {'params': model.base.parameters()},
                {'params': model.classifier.parameters(), 'lr': 1e-3}
            ], lr=1e-2, momentum=0.9)
 
# 例2:一个参数组
optim.SGD(model.parameters(), lr=1e-2, momentum=.9)

上面第一个例子中,我们分别为 model.base 和 model.classifier 的参数设置了不同的学习率,即此时 optimizer.param_grops 中有两个不同的param_group:

param_groups[0]: {'params': model.base.parameters()},
param_groups[1]: {'params': model.classifier.parameters(), 'lr': 1e-3}

每一个param_group都是一个字典,它们共同构成了param_groups,所以此时len(optimizer.param_grops)==2,aijust_learning_rate() 函数就是通过for循环遍历取出每一个param_group,然后修改其中的键 'lr' 的值,称之为手动调整学习率。

第二个例子中len(optimizer.param_grops)==1,利用for循环进行修改同样成立。

如果想要每次迭代都实时打印学习率,这样可以每次step都能知道更新的最新学习率,可以使用

scheduler.get_lr()

它返回一个学习率列表,由参数组中的不同学习率组成,可通过列表索引来得到不同参数组中的学习率。

如何在 PyTorch 中设定学习率衰减(learning rate decay)

Pytorch中的学习率衰减及其用法详解

很多时候我们要对学习率(learning rate)进行衰减,下面的代码示范了如何每30个epoch按10%的速率衰减:

很多时候我们要对学习率(learning rate)进行衰减,下面的代码示范了如何每30个epoch按10%的速率衰减:

def adjust_learning_rate(optimizer, epoch):
    """Sets the learning rate to the initial LR decayed by 10 every 30 epochs"""
    lr = args.lr * (0.1 ** (epoch // 30))
    for param_group in optimizer.param_groups:
        param_group['lr'] = lr

什么是param_groups?

optimizer通过param_group来管理参数组.param_group中保存了参数组及其对应的学习率,动量等等.所以我们可以通过更改param_group[‘lr']的值来更改对应参数组的学习率。

# 有两个`param_group`即,len(optim.param_groups)==2
optim.SGD([
                {'params': model.base.parameters()},
                {'params': model.classifier.parameters(), 'lr': 1e-3}
          ], lr=1e-2, momentum=0.9)
 
#一个参数组
optim.SGD(model.parameters(), lr=1e-2, momentum=.9)

以上为个人经验,希望能给大家一个参考,也希望大家多多支持三水点靠木。

Python 相关文章推荐
python生成指定长度的随机数密码
Jan 23 Python
Python去除列表中重复元素的方法
Mar 20 Python
详解Python中__str__和__repr__方法的区别
Apr 17 Python
详谈Python基础之内置函数和递归
Jun 21 Python
python 利用浏览器 Cookie 模拟登录的用户访问知乎的方法
Jul 11 Python
python实现知乎高颜值图片爬取
Aug 12 Python
Python socket非阻塞模块应用示例
Sep 12 Python
pandas将多个dataframe以多个sheet的形式保存到一个excel文件中
Oct 10 Python
Python3 main函数使用sys.argv传入多个参数的实现
Dec 25 Python
python如何导出微信公众号文章方法详解
Aug 31 Python
Pytho爬虫中Requests设置请求头Headers的方法
Sep 22 Python
Python日志模块logging用法
Jun 05 Python
pytorch finetuning 自己的图片进行训练操作
Jun 05 #Python
Python 如何将integer转化为罗马数(3999以内)
Jun 05 #Python
刚学完怎么用Python实现定时任务,转头就跑去撩妹!
OpenCV中resize函数插值算法的实现过程(五种)
Jun 05 #Python
OpenCV全景图像拼接的实现示例
opencv 分类白天与夜景视频的方法
python如何利用traceback获取详细的异常信息
Jun 05 #Python
You might like
使用PHPMYADMIN操作mysql数据库添加新用户和数据库的方法
2010/04/02 PHP
Window 7/XP 安装Apache 2.4与PHP 5.4 的过程详解
2013/06/02 PHP
简单解析PHP程序的运行流程
2016/06/23 PHP
用Laravel Sms实现laravel短信验证码的发送的实现
2018/11/29 PHP
PHP抽象类与接口的区别实例详解
2019/05/09 PHP
js下关于onmouseout、事件冒泡的问题经验小结
2010/12/09 Javascript
JS实现随机化快速排序的实例代码
2013/08/01 Javascript
Javascript实现苹果悬浮虚拟按钮
2016/04/10 Javascript
浅谈DOCTYPE对$(window).height()取值的影响
2016/07/21 Javascript
node+express制作爬虫教程
2016/11/11 Javascript
微信小程序教程之本地图片上传(leancloud)实例详解
2016/11/16 Javascript
微信小程序 实现列表项滑动显示删除按钮的功能
2017/04/13 Javascript
为输入框加入数字js校验代码分享
2017/11/02 Javascript
基于vue cli重构多页面脚手架过程详解
2018/01/23 Javascript
vue.js单文件组件中非父子组件的传值实例
2018/09/13 Javascript
JS/HTML5游戏常用算法之碰撞检测 像素检测算法实例详解
2018/12/12 Javascript
微信小程序上传图片并等比列压缩到指定大小的实例代码
2019/10/24 Javascript
Python中格式化format()方法详解
2017/04/01 Python
使用python实现tcp自动重连
2017/07/02 Python
Python编写登陆接口的方法
2017/07/10 Python
python获取程序执行文件路径的方法(推荐)
2018/04/26 Python
解决PyCharm同目录下导入模块会报错的问题
2018/10/13 Python
一百行python代码将图片转成字符画
2021/02/19 Python
django 消息框架 message使用详解
2019/07/22 Python
Python+OpenCV实现实时眼动追踪的示例代码
2019/11/11 Python
python pyecharts 实现一个文件绘制多张图
2020/05/13 Python
Python 实现RSA加解密文本文件
2020/12/30 Python
意大利领先的线上奢侈品销售电商:Eleonora Bonucci
2017/10/17 全球购物
美国最佳在线航班预订网站:LookupFare
2019/03/26 全球购物
高考自主招生自荐信
2013/10/20 职场文书
珍惜水资源建议书
2014/03/12 职场文书
小学班主任寄语大全
2014/04/04 职场文书
2014小学生国庆65周年演讲稿
2014/09/21 职场文书
查摆问题自查报告范文
2014/10/13 职场文书
安全学习心得体会范文
2016/01/18 职场文书
《艾尔登法环》Boss腐烂树灵很有可能是《黑暗之魂3》的一个废案
2022/04/11 其他游戏