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实现把xml或xsl转换为html格式
Apr 08 Python
Python selenium 三种等待方式解读
Sep 15 Python
深入理解NumPy简明教程---数组3(组合)
Dec 17 Python
浅谈Scrapy框架普通反爬虫机制的应对策略
Dec 28 Python
sublime python3 输入换行不结束的方法
Apr 19 Python
python读取excel指定列数据并写入到新的excel方法
Jul 10 Python
PySide和PyQt加载ui文件的两种方法
Feb 27 Python
Python3网络爬虫开发实战之极验滑动验证码的识别
Aug 02 Python
python中bytes和str类型的区别
Oct 21 Python
python3实现往mysql中插入datetime类型的数据
Mar 02 Python
Matplotlib绘制混淆矩阵的实现
May 27 Python
分享Python异步爬取知乎热榜
Apr 12 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
深入PHP5中的魔术方法详解
2013/06/17 PHP
探讨:array2xml和xml2array以及xml与array的互相转化
2013/06/24 PHP
微信公众号开发客服接口实例代码
2016/10/21 PHP
PHP实现求两个字符串最长公共子串的方法示例
2017/11/17 PHP
PHP基于SPL实现的迭代器模式示例
2018/04/22 PHP
经典的带阴影的可拖动的浮动层
2006/06/26 Javascript
TopList标签和JavaScript结合两例
2007/08/12 Javascript
jQuery子窗体取得父窗体元素的方法
2015/05/11 Javascript
checkbox 选中一个另一个checkbox也会选中的实现代码
2016/07/09 Javascript
javascript学习笔记_浅谈基础语法,类型,变量
2016/09/19 Javascript
微信小程序 教程之wxapp视图容器 swiper
2016/10/19 Javascript
jquery代码规范让代码越来越好看
2017/02/03 Javascript
jQuery中绑定事件bind() on() live() one()的异同
2017/02/23 Javascript
详解基于vue的移动web app页面缓存解决方案
2017/08/03 Javascript
详解微信小程序中的页面代码中的模板的封装
2017/10/12 Javascript
微信小程序websocket聊天室的实现示例代码
2019/02/12 Javascript
在Koa.js中实现文件上传的接口功能
2019/10/08 Javascript
微信小程序实现星级评价
2019/11/20 Javascript
利用JS如何获取form表单数据
2019/12/19 Javascript
caffe binaryproto 与 npy相互转换的实例讲解
2018/07/09 Python
python 下 CMake 安装配置 OPENCV 4.1.1的方法
2019/09/30 Python
如何基于python操作json文件获取内容
2019/12/24 Python
Python线程条件变量Condition原理解析
2020/01/20 Python
简单了解Python write writelines区别
2020/02/27 Python
pytorch中index_select()的用法详解
2021/01/06 Python
Html5 webRTC简单实现视频调用的示例代码
2020/09/23 HTML / CSS
基本款天堂:Everlane
2017/05/13 全球购物
澳大利亚领先的美容护肤品零售商之一:SkincareStore
2018/01/22 全球购物
Myprotein意大利官网:欧洲第一运动营养品牌
2018/11/22 全球购物
少儿节目主持串词
2014/04/02 职场文书
农村党建工作汇报材料
2014/10/27 职场文书
国庆庆典邀请函
2015/02/02 职场文书
2015高三毕业寄语赠言
2015/02/27 职场文书
董事长秘书工作总结
2015/08/14 职场文书
python中的None与NULL用法说明
2021/05/25 Python
Canvas绘制像素风图片的示例代码
2021/09/25 HTML / CSS