Pytorch中如何动态设置学习率

Pytorch中动态设置学习率

在深度神经网络中,学习率是最重要的超参数之一。深度学习的过程类似于八卦炉炼丹,加什么配方、配方加多少量、火候多大等等因素都会影响练出来的丹药的药效,感觉是很玄学的东西。学历率过大和过小也会导致模型出现一些问题。如何调整学习率是“炼丹玄学”中最重要的药方之一。

在很多网络中,学习率一般是固定的。Pyorch中的torch.optim.lr_scheduler为我们封装好了一些在训练过程中动态调整学习率的方法。我们不妨试着用动态学习率,对比一下二者的不同,模型是否因为学习率的动态调整而变得更优秀。

一、一般过程

torch.optim.lr_scheduler 设置的动态学习率是基于每个epoch的。根据每个epoch的学习状态,来设置当前epoch的学习率。需要注意的是学习率的调整需要应用在优化器参数更新之后。如以下代码所示的动态调整学习率的一般过程。

...
optimizer = torch.optim.Adam(...)        # 选择一种优化器
scheduler = torch.optim.lr_scheduler.... # 选择一种动态调整学习率的方法,可以是下面几种之一
for epoch in range(epochs):
    train(...)
    validate(...)

    optimizer.step()
    scheduler.step() # 需要在优化器参数更新之后再动态调整学习率
    ...
...

二、不同调整方法介绍

2.1 torch.optim.lr_scheduler.LambdaLR方法

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

参数解释:

optimizer  : 之前定义好的优化器的实例名;
lr_lambda  : 可以是function或是function list,一般是一个关于epoch数目的函数,从而计算出一个乘数因子,并根据该乘数因子调整初始学习率;
last_epoch : 默认为-1,它一般不用设置,为-1时的作用是将人为设置的学习率设定为调整学习率的基础值lr。
这里需要注意的是,last_epoch默认为-1只能保证第一次调整学习率时,原始待调整的值为人工设定的初始学习率,而第二次调整学习率时,调整的基值就变成了第一次调整后的学习率。

理论:
LambdaLR 更新学习率方式是 lr = lr * lr_lambda(其中,lr 由 optim 系列优化器提供,lr_lambda 由 lr_scheduler 中的 lambdaLR 提供)。

实例:
假设,lr 初始值为0.4, 更新学习率函数lambda表达式为:lr_lambda = 0.1 * epoch。而 epoch 的初始值为 0,则有

epoch = 0, lr = 0.4 * 0.1 * 0 = 0
epoch = 1, lr = 0.4 * 0.1 * 1 = 0.04
epoch = 2, lr = 0.4 * 0.1 * 2 = 0.08
epoch = 3, lr = 0.4 * 0.1 * 3 = 0.12
...

感兴趣的同学可由以下完整代码来验证:

import torch
from torch import nn
 
torch.manual_seed(0)
class model(nn.Module):
    def __init__(self):
        super().__init__()
        self.conv1 = nn.Conv2d(in_channels=1,out_channels=1,kernel_size=2,stride=1,padding=0)
        self.conv2 = nn.Conv2d(in_channels=1, out_channels=1, kernel_size=2, stride=1, padding=0)
    def forward(self,x):
        out = self.conv1(x)
        return out
 
net1 = model()
 
optimizer_1 = torch.optim.SGD(net1.parameters(),lr = 0.4)
scheduler_1 = torch.optim.lr_scheduler.LambdaLR(optimizer_1,lr_lambda = lambda epoch:0.1 * epoch)
 
print('\n当前学习率')
print(scheduler_1.get_lr())
 
for i in range(10):
    scheduler_1.step()              # 更新学习率
    print(scheduler_1.get_lr())

2.2 torch.optim.lr_scheduler.StepLR 方法

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

参数解释:

optimizer  : 要更改学习率的优化器;
step_size  : 每训练step_size个epoch,更新一次参数;
gamma      : 更新lr的乘法因子;
last_epoch : 最后一个epoch的index,如果是训练了很多个epoch后中断了,继续训练,这个值就等于加载的模型的epoch。
		    默认为-1表示从头开始训练,即从epoch=0开始。

理论过程:
torch.optim.lr_scheduler.StepLR方法是每过step_size个epoch,做一次更新。更新公式如下:
StepLR函数

其中new_lr是得到的新的学习率,initial_lr是初始的学习率,step_size是参数step_size,γ 是参数gamma。

实例:
感兴趣的同学可以用以下代码进行实例验证:

import torch
import torch.nn as nn
from torch.optim.lr_scheduler import StepLR
import itertools
 
 
initial_lr = 0.1
 
class model(nn.Module):
    def __init__(self):
        super().__init__()
        self.conv1 = nn.Conv2d(in_channels=3, out_channels=3, kernel_size=3)
 
    def forward(self, x):
        pass
 
net_1 = model()
 
optimizer_1 = torch.optim.Adam(net_1.parameters(), lr = initial_lr)
scheduler_1 = StepLR(optimizer_1, step_size=3, gamma=0.1)
 
print("初始化的学习率:", optimizer_1.defaults['lr'])
 
for epoch in range(1, 11):
    # train
 
    optimizer_1.zero_grad()
    optimizer_1.step()
    print("第%d个epoch的学习率:%f" % (epoch, optimizer_1.param_groups[0]['lr']))
    scheduler_1.step()

结果如下:

初始化的学习率: 0.11个epoch的学习率:0.1000002个epoch的学习率:0.1000003个epoch的学习率:0.1000004个epoch的学习率:0.0100005个epoch的学习率:0.0100006个epoch的学习率:0.0100007个epoch的学习率:0.0010008个epoch的学习率:0.0010009个epoch的学习率:0.00100010个epoch的学习率:0.000100

2.3 torch.optim.lr_scheduler.ExponetialLR方法

torch.optim.lr_scheduler.ExponentialLR(optimizer, gamma, last_epoch = -1)

参数解释:

optimizer  : 要更改学习率的优化器;
gamma      : 更新lr的乘法因子;
last_epoch : 最后一个epoch的index,如果是训练了很多个epoch后中断了,继续训练,这个值就等于加载的模型的epoch。
		    默认为-1表示从头开始训练,即从epoch=1开始。

理论过程:
和StepLR方法不同,ExponetialLR每个epoch都需要重新计算学习率。
在这里插入图片描述
其中new_lr是得到的新的学习率,initial_lr是初始的学习率,γ是参数gamma。

实例:
感兴趣的同学可以用以下代码进行实例验证:

import torch
import torch.nn as nn
from torch.optim.lr_scheduler import ExponentialLR
import itertools


initial_lr = 0.1

class model(nn.Module):
    def __init__(self):
        super().__init__()
        self.conv1 = nn.Conv2d(in_channels=3, out_channels=3, kernel_size=3)

    def forward(self, x):
        pass

net_1 = model()

optimizer_1 = torch.optim.Adam(net_1.parameters(), lr = initial_lr)
scheduler_1 = ExponentialLR(optimizer_1, gamma=0.1)

print("初始化的学习率:", optimizer_1.defaults['lr'])

for epoch in range(1, 11):
    # train

    optimizer_1.zero_grad()
    optimizer_1.step()
    print("第%d个epoch的学习率:%f" % (epoch, optimizer_1.param_groups[0]['lr']))
    scheduler_1.step()

结果:

初始化的学习率: 0.11个epoch的学习率:0.1000002个epoch的学习率:0.0100003个epoch的学习率:0.0010004个epoch的学习率:0.0001005个epoch的学习率:0.0000106个epoch的学习率:0.0000017个epoch的学习率:0.0000008个epoch的学习率:0.0000009个epoch的学习率:0.00000010个epoch的学习率:0.000000

2.4 torch.optim.lr_scheduler.MultiStepLR方法

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

参数解释:

optimizer  : 要更改学习率的优化器;
milestones : 递增的list,存放要更新lr的epoch;
gamma      : 更新lr的乘法因子;
last_epoch : 最后一个epoch的index,如果是训练了很多个epoch后中断了,继续训练,这个值就等于加载的模型的epoch。
			默认为-1表示从头开始训练,即从epoch=1开始。

理论过程:

每次遇到milestones序列中的epoch,做一次更新。

在这里插入图片描述
其中new_lr是得到的新的学习率,initial_lr是初始的学习率,γ是参数gamma, bisect_right(milestones, epoch)就是bisect模块中的bisect_right函数,返回值是把epoch插入排序好的列表milestones式的位置。

实例:
如果上述过程比较难懂,咱们直接上手用实际代码来理解此方法:

import torch
import torch.nn as nn
from torch.optim.lr_scheduler import MultiStepLR
import itertools


initial_lr = 0.1

class model(nn.Module):
    def __init__(self):
        super().__init__()
        self.conv1 = nn.Conv2d(in_channels=3, out_channels=3, kernel_size=3)

    def forward(self, x):
        pass

net_1 = model()

optimizer_1 = torch.optim.Adam(net_1.parameters(), lr = initial_lr)
scheduler_1 = MultiStepLR(optimizer_1, milestones=[3, 7], gamma=0.1)

print("初始化的学习率:", optimizer_1.defaults['lr'])

for epoch in range(1, 11):
    # train

    optimizer_1.zero_grad()
    optimizer_1.step()
    print("第%d个epoch的学习率:%f" % (epoch, optimizer_1.param_groups[0]['lr']))
    scheduler_1.step()

结果:可以看到,第4个和第8个epoch开始变化,其他的epoch时就保持不变。

初始化的学习率: 0.11个epoch的学习率:0.1000002个epoch的学习率:0.1000003个epoch的学习率:0.1000004个epoch的学习率:0.0100005个epoch的学习率:0.0100006个epoch的学习率:0.0100007个epoch的学习率:0.0100008个epoch的学习率:0.0010009个epoch的学习率:0.00100010个epoch的学习率:0.001000

2.5 torch.optim.lr_scheduler.ReduceLROnPlateau方法

torch.optim.lr_scheduler.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)

参数解释:

optimizer : 要更改学习率的优化器;
mode      : 只能是‘min’或'max',默认’min':
			‘min'模式下,当metric不再下降时减小lr;
			'max'模式下,当metric不再增长时减小lr;
factor    : lr减小的乘法因子,默认为0.1;
patience  : 在metric停止优化patience个epoch后减小lr,例如,如果patience=2,那metric不再优化的前两个epoch不做任何事,第三个epoch后metric仍然没有优化,那么更新lr,默认为10;
verbose   : 如果为True,在更新lr后print一个更新信息,默认为False;

理论过程: 该方法是不依赖epoch来更新lr学习率的。给定一个metric,当metric停止优化时减小学习率。也就是在发现loss不再降低或者acc不再提高之后,降低学习率

在这里插入图片描述
其中new_lr是得到的新的学习率,old_lr是上一次优化使用的学习率,λ是通过参数factor。

实例:

import torch
import torch.nn as nn
from torch.optim.lr_scheduler import ReduceLROnPlateau
import itertools


initial_lr = 0.1

class model(nn.Module):
    def __init__(self):
        super().__init__()
        self.conv1 = nn.Conv2d(in_channels=3, out_channels=3, kernel_size=3)

    def forward(self, x):
        pass

net_1 = model()

optimizer_1 = torch.optim.Adam(net_1.parameters(), lr = initial_lr)
scheduler_1 = ReduceLROnPlateau(optimizer_1, mode='min', factor=0.1, patience=2)

print("初始化的学习率:", optimizer_1.defaults['lr'])

for epoch in range(1, 15):
    # train

    test = 2
    optimizer_1.zero_grad()
    optimizer_1.step()
    print("第%d个epoch的学习率:%f" % (epoch, optimizer_1.param_groups[0]['lr']))
    scheduler_1.step(test)

结果输出:

初始化的学习率: 0.11个epoch的学习率:0.1000002个epoch的学习率:0.1000003个epoch的学习率:0.1000004个epoch的学习率:0.1000005个epoch的学习率:0.0100006个epoch的学习率:0.0100007个epoch的学习率:0.0100008个epoch的学习率:0.0010009个epoch的学习率:0.00100010个epoch的学习率:0.00100011个epoch的学习率:0.00010012个epoch的学习率:0.00010013个epoch的学习率:0.00010014个epoch的学习率:0.000010

可以看到后面的epoch每隔2个发现没有变化之后,就开始降低学习率来优化模型。

2.6 torch.optim.lr_scheduler.CosineAnnealingLR方法

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

参数解释:

optimizer  : 要更改学习率的优化器;
T_max      : lr的变化是周期性的,T_max是周期的1/4;
eta_min    : lr的最小值,默认为0;
last_epoch : 最后一个epoch的index,如果是训练了很多个epoch后中断了,继续训练,这个值就等于加载的模型的epoch。默认为-1表示从头开始训练,即从epoch=1开始

理论过程:
在这里插入图片描述

让lr随着epoch的变化图类似于cos,其中new_lr是得到的新的学习率,initial_lr是初始的学习率,eta_min表示最小学习率,T_max表示cos的周期的1/4。

实例:

import torch
import torch.nn as nn
from torch.optim.lr_scheduler import CosineAnnealingLR
import itertools

import matplotlib.pyplot as plt


initial_lr = 0.1

class model(nn.Module):
    def __init__(self):
        super().__init__()
        self.conv1 = nn.Conv2d(in_channels=3, out_channels=3, kernel_size=3)

    def forward(self, x):
        pass

net_1 = model()

optimizer_1 = torch.optim.Adam(net_1.parameters(), lr = initial_lr)
scheduler_1 = CosineAnnealingLR(optimizer_1, T_max=20)

print("初始化的学习率:", optimizer_1.defaults['lr'])

lr_list = [] # 把使用过的lr都保存下来,之后画出它的变化

for epoch in range(1, 101):
    # train

    optimizer_1.zero_grad()
    optimizer_1.step()
    print("第%d个epoch的学习率:%f" % (epoch, optimizer_1.param_groups[0]['lr']))
    lr_list.append(optimizer_1.param_groups[0]['lr'])
    scheduler_1.step()

# 画出lr的变化
plt.plot(list(range(1, 101)), lr_list)
plt.xlabel("epoch")
plt.ylabel("lr")
plt.title("learning rate's curve changes as epoch goes on!")
plt.show()

结果如下所示:
在这里插入图片描述

当然还有一些其他的动态学习率的函数,如:

torch.optim.lr_scheduler.CyclicLR

torch.optim.lr_scheduler.OneCycleLR

torch.optim.lr_scheduler.CosineAnnealingWarmRestarts

等等,可自行查阅相关资料。

PyTorch中,可以使用torch.optim模块来设置动态学习率动态学习率的概念是在训练过程中根据模型的性能自适应地调整学习率,以提高训练效果。以下是一种常见的设置动态学习率的方法: 通常情况下,可以使用torch.optim中的某个优化器,例如Adam或SGD,并将其与torch.optim.lr_scheduler中的某个学习率调度器结合使用。 首先,定义一个优化器,并选择一个适合的学习率: ``` optimizer = torch.optim.Adam(model.parameters(), lr=0.01) ``` 接下来,可以选择一个学习率调度器来动态调整学习率。这里以StepLR调度器为例,该调度器在每个给定的步长(step_size)时将学习率乘以gamma的值: ``` scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=10, gamma=0.1) ``` 然后,在每次训练迭代中,先调用学习率调度器的`step()`方法,然后再进行前向传播和反向传播的过程: ``` for epoch in range(num_epochs): # 进行一次训练迭代 for inputs, labels in dataloader: optimizer.zero_grad() outputs = model(inputs) loss = criterion(outputs, labels) loss.backward() optimizer.step() # 在每个epoch之后,调用学习率调度器的step方法 scheduler.step() ``` 通过调用学习率调度器的`step()`方法,可以使优化器根据预定义的策略更新学习率。 需要注意的是,学习率调度器的step_size和gamma需要根据具体情况进行调整,可以根据模型在训练集上的性能来调整这些参数,以达到更好的训练效果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值