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线程详解
Jun 24 Python
Python实现在线暴力破解邮箱账号密码功能示例【测试可用】
Sep 06 Python
Tensorflow卷积神经网络实例进阶
May 24 Python
Python PyAutoGUI模块控制鼠标和键盘实现自动化任务详解
Sep 04 Python
python 制作自定义包并安装到系统目录的方法
Oct 27 Python
解决pycharm 远程调试 上传 helpers 卡住的问题
Jun 27 Python
python分布式编程实现过程解析
Nov 08 Python
浅谈pytorch池化maxpool2D注意事项
Feb 18 Python
Python opencv相机标定实现原理及步骤详解
Apr 09 Python
详解anaconda离线安装pytorchGPU版
Sep 08 Python
Python并发编程实例教程之线程的玩法
Jun 20 Python
python中super()函数的理解与基本使用
Aug 30 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
PHP实现图片简单上传
2006/10/09 PHP
asp和php下textarea提交大量数据发生丢失的解决方法
2008/01/20 PHP
PHP获取某个月最大天数(最后一天)的方法
2015/07/29 PHP
十大使用PHP框架的理由
2015/09/26 PHP
windows环境下使用Composer安装ThinkPHP5
2018/05/18 PHP
键盘控制事件应用教程大全
2006/11/24 Javascript
javascript下数值型比较难点说明
2010/06/07 Javascript
javascript温习的一些笔记 基础常用知识小结
2011/06/22 Javascript
Javascript中arguments对象详解
2014/10/22 Javascript
js实现两点之间画线的方法
2015/05/12 Javascript
基于js实现微信发送好友如何分享到朋友圈、微博
2015/11/30 Javascript
React.js入门实例教程之创建hello world 的5种方式
2016/05/11 Javascript
分享JS数组求和与求最大值的方法
2016/08/11 Javascript
Vue学习笔记进阶篇之vue-cli安装及介绍
2017/07/18 Javascript
Vue.js项目模板搭建图文教程
2017/09/20 Javascript
node vue项目开发之前后端分离实战记录
2017/12/13 Javascript
jQuery实现图片切换效果
2020/10/19 jQuery
python的else子句使用指南
2016/02/27 Python
Python视频爬虫实现下载头条视频功能示例
2018/05/07 Python
python和shell获取文本内容的方法
2018/06/05 Python
pycharm中显示CSS提示的知识点总结
2019/07/29 Python
使用pytorch搭建AlexNet操作(微调预训练模型及手动搭建)
2020/01/18 Python
python golang中grpc 使用示例代码详解
2020/06/03 Python
Python+Dlib+Opencv实现人脸采集并表情判别功能的代码
2020/07/01 Python
如何使用pycharm连接Databricks的步骤详解
2020/09/23 Python
Python爬取股票信息,并可视化数据的示例
2020/09/26 Python
美国知名的家庭连锁百货商店:Boscov’s
2017/07/27 全球购物
.NET笔试题(20个问题)
2016/02/02 面试题
计算机大学生的自我评价
2013/10/15 职场文书
高中军训感言800字
2014/03/05 职场文书
学风建设演讲稿
2014/09/12 职场文书
财务个人年度总结范文
2015/02/26 职场文书
机修车间主任岗位职责
2015/04/08 职场文书
祝福语集锦:给百岁老人祝寿贺词
2019/11/19 职场文书
「魔导具师妲莉亚永不妥协~从今天开始的自由职人生活~」1、2卷发售宣传CM公开
2022/03/21 日漫
Win11 22H2 2022怎么更新? 获得Win1122H22022版本升级技巧
2022/09/23 数码科技