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 相关文章推荐
Django查找网站项目根目录和对正则表达式的支持
Jul 15 Python
轻松实现python搭建微信公众平台
Feb 16 Python
如何用python整理附件
May 13 Python
python同时遍历数组的索引和值的实例
Nov 15 Python
Python实现定期检查源目录与备份目录的差异并进行备份功能示例
Feb 27 Python
python实现微信防撤回神器
Apr 29 Python
Python 转换文本编码实现解析
Aug 27 Python
python实现指定ip端口扫描方式
Dec 17 Python
pytorch AvgPool2d函数使用详解
Jan 03 Python
Python如何使用turtle库绘制图形
Feb 26 Python
python生成并处理uuid的实现方式
Mar 03 Python
pandas 像SQL一样使用WHERE IN查询条件说明
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
php 文件上传代码(限制jpg文件)
2010/01/05 PHP
详解PHP归并排序的实现
2016/10/18 PHP
PHP实现APP微信支付的实例讲解
2018/02/10 PHP
php取出数组单个值的方法
2018/03/12 PHP
PHP检查端口是否可以被绑定的方法示例
2018/08/09 PHP
在js中单选框和复选框获取值的方式
2009/11/06 Javascript
基于jquery的大众点评,分类导航实现代码
2011/08/23 Javascript
一款jquery特效编写的大度宽屏焦点图切换特效的实例代码
2013/08/05 Javascript
javascript实例分享---具有立体效果的图片特效
2014/06/08 Javascript
jQuery截取指定长度字符串代码
2014/08/21 Javascript
JS实现选项卡实例详解
2015/11/17 Javascript
jquery弹出遮掩层效果【附实例代码】
2016/04/28 Javascript
详解vuejs之v-for列表渲染
2017/06/22 Javascript
jQuery 禁止表单用户名、密码自动填充功能
2017/10/30 jQuery
浅谈vue.js导入css库(elementUi)的方法
2018/03/09 Javascript
react-router4按需加载(踩坑填坑)
2019/01/06 Javascript
JavaScript中CreateTextFile函数
2020/08/30 Javascript
[02:52]2017DOTA2国际邀请赛中国区预选赛晋级之路
2017/07/03 DOTA
【Python】Python的urllib模块、urllib2模块批量进行网页下载文件
2016/11/19 Python
Python操作MySQL模拟银行转账
2018/03/12 Python
python使用Pandas库提升项目的运行速度过程详解
2019/07/12 Python
python实现的读取网页并分词功能示例
2019/10/29 Python
python模拟哔哩哔哩滑块登入验证的实现
2020/04/24 Python
PyPDF2读取PDF文件内容保存到本地TXT实例
2020/05/12 Python
Django-imagekit的使用详解
2020/07/06 Python
html5响应式开发自动计算fontSize的方法
2020/01/13 HTML / CSS
教师网络培训心得体会
2016/01/09 职场文书
员工给公司的建议书
2019/06/24 职场文书
酒店工程部的岗位职责汇总大全
2019/10/23 职场文书
八年级作文之感悟亲情
2019/11/20 职场文书
CSS3 制作的书本翻页特效
2021/04/13 HTML / CSS
原生Javascript+HTML5一步步实现拖拽排序
2021/06/12 Javascript
python图片灰度化处理的几种方法
2021/06/23 Python
为什么MySQL选择Repeatable Read作为默认隔离级别
2021/07/26 MySQL
vue自定义右键菜单之全局实现
2022/04/09 Vue.js
SQL Server删除表中的重复数据
2022/05/25 SQL Server