pytorch torch.optim.lr_scheduler 调整学习率的六种策略
1. 为什么需要调整学习率
在深度学习训练过程中,最重要的参数就是学习率,通常来说,在整个训练过层中,学习率不会一直保持不变,为了让模型能够在训练初期快速收敛,学习率通常比较大,在训练末期,为了让模型收敛在更小的局部最优点,学习率通常要比较小。
2. 学习率的初始值设置
其实,不同的任务学习率的初始值是需要试验几次来获得的,使用的优化器不同,mini-batch 的 batch_size 大小不同,学习率的初始值也不太相同。
根据我的实验经验,同一个任务,如果使用Adam优化器,学习率初始值0.001比较好,Adam优化器对于初始值实际上不太敏感,基本都能做到快速收敛;如果使用SGD优化器的话,学习率需要在Adam的基础上乘以10倍或者100倍,也就是使用0.1或者0.01比较好。
需要注意的一点,通常 batch_size 扩大 n 倍,学习率也要相应扩大
(
n
)
\sqrt(n)
(n),虽然这么说,我自己实验的时候,没发现太多作用,可能是因为使用Adam的原因。
3. torch 学习率的调整策略
pytorch 提供了一些基础的学习率调整策略,在 torch.optim.lr_scheduler 模块里,下方的代码实现了5种可能用到(其实我自己用的都是最基础的阶梯下降)的学习率调整方法,图片可视化了学习率变小的过程,非常简单,对照图片和代码仔细看看就明白每个scheduler的具体参数含义了:
import torch
import matplotlib.pyplot as plt
lr = 0.001
# 20代表从lr从最大到最小的epoch数,0代表学习率的最小值
scheduler_cos = torch.optim.lr_scheduler.CosineAnnealingLR(torch.optim.SGD([torch.ones(1)], lr), 20, 0)
# 20 和 0.5 代表每走20个epoch,学习率衰减0.5倍,阶梯形式
scheduler_step = torch.optim.lr_scheduler.StepLR(torch.optim.SGD([torch.ones(1)], lr), 20, 0.5)
# 每走一个epoch,学习率衰减0.95倍
scheduler_exp = torch.optim.lr_scheduler.ExponentialLR(torch.optim.SGD([torch.ones(1)], lr), 0.95)
# 三角的形式,0.0001代表最小的学习率, 0.001代表最大的学习率, 20代表一个升降周期
scheduler_cyc = torch.optim.lr_scheduler.CyclicLR(torch.optim.SGD([torch.ones(1)], lr), 0.0001, 0.001, 20)
# 阶梯衰减,每次衰减的epoch数根据列表 [20, 30, 60, 80] 给出,0.8代表学习率衰减倍数
scheduler_mul = torch.optim.lr_scheduler.MultiStepLR(torch.optim.SGD([torch.ones(1)], lr), [20, 30, 60, 80], 0.8)
lr_cos = []
lr_step = []
lr_exp = []
lr_cyc = []
lr_mul = []
for i in range(100):
lr_cos += scheduler_cos.get_last_lr()
lr_step += scheduler_step.get_last_lr()
lr_exp += scheduler_exp.get_last_lr()
lr_cyc += scheduler_cyc.get_last_lr()
lr_mul += scheduler_mul.get_last_lr()
scheduler_cos.step()
scheduler_step.step()
scheduler_exp.step()
scheduler_cyc.step()
scheduler_mul.step()
plt.figure(figsize=(12,7))
plt.plot(list(range(len(lr_cos))), lr_cos,
list(range(len(lr_step))), lr_step,
list(range(len(lr_exp))), lr_exp,
list(range(len(lr_cyc))), lr_cyc,
list(range(len(lr_mul))), lr_mul,)
plt.legend(['cos','step','exp','cyc', 'mul'], fontsize=20)
plt.xlabel('epoch', size=15)
plt.ylabel('lr', size=15)
plt.show()
运行上面代码后的效果:
四种调整学习率详解
[pytorch] torch.optimizer.lr_scheduler调整学习率
orch.optim.lr_scheduler模块提供了一些根据epoch训练次数来调整学习率(learning rate)的方法。一般情况下我们会设置随着epoch的增大而逐渐减小学习率从而达到更好的训练效果。
学习率的调整应该放在optimizer更新之后,下面是一个参考蓝本:
scheduler = ...
for epoch in range(100):
train(...)
validate(...)
scheduler.step()
另外本文介绍的调整学习率的函数都是基于epoch大小变化进行调整的。
torch.optim.lr_scheduler.LambdaLR
class torch.optim.lr_scheduler.LambdaLR(optimizer, lr_lambda, last_epoch=-1)
更新的公式为:
其中new_lr
new_lr是得到的新的学习率,initial_lr
initial_lr是初始的学习率,λ
λ是通过参数lr_lambda和epoch得到的。
参数:
- optimizer (Optimizer):要更改学习率的优化器;
- lr_lambda(function or list):根据epoch计算λ的函数;或者是一个list的这样的function,分别计算各个parameter groups的学习率更新用到的λ;
- last_epoch (int):最后一个epoch的index,如果是训练了很多个epoch后中断了,继续训练,这个值就等于加载的模型的epoch。默认为-1表示从头开始训练,即从epoch=1开始。
import torch
import torch.nn as nn
from torch.optim.lr_scheduler import LambdaLR
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 = LambdaLR(optimizer_1, lr_lambda=lambda epoch: 1/(epoch+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.1
第1个epoch的学习率:0.100000
第2个epoch的学习率:0.050000
第3个epoch的学习率:0.033333
第4个epoch的学习率:0.025000
第5个epoch的学习率:0.020000
第6个epoch的学习率:0.016667
第7个epoch的学习率:0.014286
第8个epoch的学习率:0.012500
第9个epoch的学习率:0.011111
第10个epoch的学习率:0.010000
torch.optim.lr_scheduler.StepLR
class torch.optim.lr_scheduler.StepLR(optimizer, step_size, gamma=0.1, last_epoch=-1)
更新的公式:
参数:
- optimizer (Optimizer):要更改学习率的优化器;
- step_size(int):每训练step_size个epoch,更新一次参数;
- gamma(float):更新lr的乘法因子;
- last_epoch (int):最后一个epoch的index,如果是训练了很多个epoch后中断了,继续训练,这个值就等于加载的模型的epoch。默认为-1表示从头开始训练,即从epoch=1开始。
代码:
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.1
第1个epoch的学习率:0.100000
第2个epoch的学习率:0.100000
第3个epoch的学习率:0.100000
第4个epoch的学习率:0.010000
第5个epoch的学习率:0.010000
第6个epoch的学习率:0.010000
第7个epoch的学习率:0.001000
第8个epoch的学习率:0.001000
第9个epoch的学习率:0.001000
第10个epoch的学习率:0.000100
torch.optim.lr_scheduler.MultiStepLR
class torch.optim.lr_scheduler.MultiStepLR(optimizer, milestones, gamma=0.1, last_epoch=-1)
更新公式为:
参数:
- optimizer (Optimizer):要更改学习率的优化器;
- milestones(list):递增的list,存放要更新lr的epoch;
- gamma(float):更新lr的乘法因子;
- last_epoch (int):最后一个epoch的index,如果是训练了很多个epoch后中断了,继续训练,这个值就等于加载的模型的epoch。默认为-1表示从头开始训练,即从epoch=1开始。
代码:
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()
输出结果为:
初始化的学习率: 0.1
第1个epoch的学习率:0.100000
第2个epoch的学习率:0.100000
第3个epoch的学习率:0.100000
第4个epoch的学习率:0.010000
第5个epoch的学习率:0.010000
第6个epoch的学习率:0.010000
第7个epoch的学习率:0.010000
第8个epoch的学习率:0.001000
第9个epoch的学习率:0.001000
第10个epoch的学习率:0.001000
torch.optim.lr_scheduler.ExponentialLR
class torch.optim.lr_scheduler.ExponentialLR(optimizer, gamma, last_epoch=-1)
更新公式为:
参数:
- optimizer (Optimizer):要更改学习率的优化器;
- gamma(float):更新lr的乘法因子;
- gamma(float):更新lr的乘法因子;
last_epoch (int):最后一个epoch的index,如果是训练了很多个epoch后中断了,继续训练,这个值就等于加载的模型的epoch。默认为-1表示从头开始训练,即从epoch=1开始。
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.1
第1个epoch的学习率:0.100000
第2个epoch的学习率:0.010000
第3个epoch的学习率:0.001000
第4个epoch的学习率:0.000100
第5个epoch的学习率:0.000010
第6个epoch的学习率:0.000001
第7个epoch的学习率:0.000000
第8个epoch的学习率:0.000000
第9个epoch的学习率:0.000000
第10个epoch的学习率:0.000000