一文搞懂pytorch中的学习率优化策略,torch.optim.lr_scheduler
torch.optim.lr_scheduler
提供了三种根据epoch训练数来调整学习率(learning rate)的方法。具体方法如下:
一、有序调整:
1.等间隔调整(Step):torch.optim.lr_scheduler.StepLR
n e w − l r = i n i t i a l − l r ∗ γ e p o c h s t e p − s i z e new_-lr=initial_-lr*\gamma^{\frac{epoch}{step_-size}} new−lr=initial−lr∗γstep−sizeepoch
更新策略:
每过step_size个epoch,学习率进行一次更新
n
e
w
−
l
r
new_-lr
new−lr:新得到的学习率。
i
n
i
t
i
a
l
−
l
r
initial_-lr
initial−lr:初始学习率。
γ
\gamma
γ:参数gamma
,更新
l
r
lr
lr的乘法因子。
s
t
e
p
−
s
i
z
e
step_-size
step−size:每训练step_size个epoch,更新一次
l
r
lr
lr。
l
a
s
t
−
e
p
o
c
h
last_-epoch
last−epoch:最后一个epoch的index,如果是训练了很多个epoch后中断了,继续训练,这个值就等于加载的模型的epoch。默认为-1表示从头开始训练,即从epoch=1开始。
代码如下:
import matplotlib.pyplot as plt
import torch
import torch.optim as optim
from torch.optim.lr_scheduler import StepLR
from torchvision.models import AlexNet
num_epochs = 100
#定义2分类网络
model = AlexNet(num_classes=2)
optimizer = optim.SGD(params=model.parameters(), lr=0.05)
scheduler = StepLR(
optimizer=optimizer,
step_size=20, # 设定调整的间隔数
gamma=0.5, # 系数
last_epoch=-1
)
# 迭代训练
lrs, epochs = [], []
for epoch in range(num_epochs):
lrs.append(scheduler.get_lr()) #.get_lr()获取当前学习率
epochs.append(epoch)
pass # 在这里进行迭代训练
#学习率更新
scheduler.step()
# visualize
plt.figure()
plt.legend()
plt.plot(epochs, lrs, label='StepLR')
plt.show()
2.按需调整学习率(MultiStep):
torch.optim.lr_scheduler.MultiStepLR(optimizer, milestones, gamma=0.1, last_epoch=-1)
n
e
w
−
l
r
=
i
n
i
t
i
a
l
−
l
r
∗
γ
b
i
s
e
c
t
−
r
i
g
h
t
(
m
i
l
e
s
t
o
n
e
s
,
e
p
o
c
h
)
new_-lr=initial_-lr*\gamma ^{bisect_-right(milestones,epoch)}
new−lr=initial−lr∗γbisect−right(milestones,epoch)
更新策略:
每次遇到milestones
中的epoch
,学习率做一次更新。
n
e
w
−
l
r
new_-lr
new−lr:新得到的学习率。
i
n
i
t
i
a
l
−
l
r
initial_-lr
initial−lr:初始学习率。
γ
\gamma
γ:参数gamma
,更新
l
r
lr
lr的乘法因子。
m
i
l
e
s
t
o
n
e
s
milestones
milestones:递增的list,存放需要更新
l
r
lr
lr的epoch。
l
a
s
t
−
e
p
o
c
h
last_-epoch
last−epoch:最后一个epoch的index,如果是训练了很多个epoch后中断了,继续训练,这个值就等于加载的模型的epoch。默认为-1表示从头开始训练,即从epoch=1开始。
b
i
s
e
c
t
−
r
i
g
h
t
(
m
i
l
e
s
t
o
n
e
s
,
e
p
o
c
h
)
bisect_-right(milestones,epoch)
bisect−right(milestones,epoch):bisect
模块中的bisect_right
函数,返回值是把epoch
插入排序好的列表milestones
式的位置。
代码如下:
import matplotlib.pyplot as plt
import torch
import torch.optim as optim
from torch.optim.lr_scheduler import MultiStepLR
from torchvision.models import AlexNet
num_epochs = 100
#定义2分类网络
model = AlexNet(num_classes=2)
optimizer = optim.SGD(params=model.parameters(), lr=0.05)
scheduler = MultiStepLR(
optimizer=optimizer,
milestones=[10, 20, 40], # 设定调整的间隔数
gamma=0.5, # 系数
last_epoch=-1
)
# train-like iteration
lrs, epochs = [], []
for epoch in range(num_epochs):
lrs.append(scheduler.get_lr()) #.get_lr()获取当前学习率
epochs.append(epoch)
pass # 在这里进行迭代训练
#学习率更新
scheduler.step()
# visualize
plt.figure()
plt.legend()
plt.plot(epochs, lrs, label='MultiStepLR')
plt.show()
其中最后一个epoch的index,如果是训练了很多个epoch后中断了,继续训练,这个值就等于加载的模型的epoch。默认为-1表示从头开始训练,即从epoch=1开始。
3.指数衰减调整(Exponential):torch.optim.lr_scheduler.ExponentialLR
n
e
w
−
l
r
=
i
n
i
t
i
a
l
−
l
r
∗
γ
e
p
o
c
h
new_-lr=initial_-lr*\gamma ^{epoch}
new−lr=initial−lr∗γepoch
import matplotlib.pyplot as plt
import torch
import torch.optim as optim
from torch.optim.lr_scheduler import ExponentialLR
from torchvision.models import AlexNet
import seaborn as sns
num_epochs = 100
#定义2分类网络
model = AlexNet(num_classes=2)
optimizer = optim.SGD(params=model.parameters(), lr=0.05)
scheduler = ExponentialLR(
optimizer=optimizer,
gamma=0.5, # 系数
last_epoch=-1
)
# train-like iteration
lrs, epochs = [], []
for epoch in range(num_epochs):
lrs.append(scheduler.get_lr()) #.get_lr()获取当前学习率
epochs.append(epoch)
pass # 在这里进行迭代训练
#学习率更新
scheduler.step()
# visualize
plt.figure()
plt.plot(epochs, lrs, label='ExponentialLR')
plt.legend()
plt.show()
4.余弦退火调整(CosineAnnealing):torch.optim.lr_sheduler.CosineAnnealingLR
n
e
w
−
l
r
=
e
t
a
−
m
i
n
+
(
i
n
i
t
i
a
l
−
l
r
−
e
t
a
−
m
i
n
)
∗
(
1
+
c
o
s
(
e
p
o
c
h
T
−
m
a
x
∗
π
)
)
new_-lr=eta_-min+(initial_-lr-eta_-min)*(1+cos(\frac{epoch}{T_-max}*\pi ))
new−lr=eta−min+(initial−lr−eta−min)∗(1+cos(T−maxepoch∗π))
更新策略:
每次遇到milestones
中的epoch
,学习率做一次更新。
n
e
w
−
l
r
new_-lr
new−lr:新得到的学习率。
i
n
i
t
i
a
l
−
l
r
initial_-lr
initial−lr:初始学习率。
e
t
a
−
m
i
n
eta_-min
eta−min:表示最小学习率。
T
−
m
a
x
T_-max
T−max:代表1/2个cos周期所对应的epoch值,学习率下降到最小值时的epoch数,即当epoch=T_max时,学习率下降到余弦函数最小值,当epoch>T_max时,学习率将增大;
l
a
s
t
−
e
p
o
c
h
last_-epoch
last−epoch:最后一个epoch的index,如果是训练了很多个epoch后中断了,继续训练,这个值就等于加载的模型的epoch。默认为-1表示从头开始训练,即从epoch=1开始。
import matplotlib.pyplot as plt
import torch
import torch.optim as optim
from torch.optim.lr_scheduler import CosineAnnealingLR
from torchvision.models import AlexNet
num_epochs = 100
#定义2分类网络
model = AlexNet(num_classes=2)
optimizer = optim.SGD(params=model.parameters(), lr=0.05)
scheduler = CosineAnnealingLR(
optimizer=optimizer,
T_max=20, # 设定调整的间隔数
eta_min=0.005, # 系数
last_epoch=-1
)
# 迭代训练
lrs, epochs = [], []
for epoch in range(num_epochs):
lrs.append(scheduler.get_lr()) #.get_lr()获取当前学习率
epochs.append(epoch)
pass # 在这里进行迭代训练
#学习率更新
scheduler.step()
# visualize
plt.figure()
plt.legend()
plt.plot(epochs, lrs, label='CosineAnnealingLR')
plt.show()
二、自适应调整:
自适应调整学习率 ReduceLROnPlateau:torch.optim.lr_scheduler.ReduceLROnPlateau
n
e
w
−
l
r
=
λ
∗
o
l
d
−
l
r
new_-lr=\lambda *old_-lr
new−lr=λ∗old−lr
三、自定义调整:
自定义调整学习率 LambdaLR:torch.optim.lr_scheduler.LambdaLr(optimizer, lr_lambda, last_epoch=-1)
更新策略:
遇到模型不同的层参数时,根据该层参数的调整方式来调整学习率。
为不同参数组设定不同学习率调整策略,在fine-tune中特别有用,我们不仅可以为不同层设置不同的学习率,还可以为不同层设置不同的学习率调整策略。
n
e
w
−
l
r
=
λ
∗
i
n
i
t
i
a
l
−
l
r
new_-lr=\lambda *initial_-lr
new−lr=λ∗initial−lr
n
e
w
−
l
r
new_-lr
new−lr:新得到的学习率。
i
n
i
t
i
a
l
−
l
r
initial_-lr
initial−lr:初始学习率。
λ
λ
λ:通过参数lr_lambda
和epoch
得到的。
l
r
−
l
a
m
b
d
a
lr_-lambda
lr−lambda:根据epoch计算λ的函数;或者是一个list的这样的function,分别计算各个parameter groups的学习率更新用到的λ;
import matplotlib.pyplot as plt
import torch
import torch.optim as optim
from torch.optim.lr_scheduler import LambdaLR
from torchvision.models import AlexNet
import seaborn as sns
num_epochs = 100
#定义2分类网络
model = AlexNet(num_classes=2)
# optimizer parameter groups 设置了个优化组:权重,偏置,其他参数
pg0, pg1, pg2 = [], [], []
for k, v in model.named_parameters():
v.requires_grad = True
if '.bias' in k:
pg2.append(v) # biases
elif '.weight' in k and '.bn' not in k:
pg1.append(v) # apply weight decay
else:
pg0.append(v) # all else
optimizer = optim.SGD(pg1, lr=0.05)
#给optimizer管理的参数组中增加新的组参数,
#可为该组参数定制lr,momentum,weight_decay 等在finetune 中常用。
optimizer.add_param_group({'params': pg2}) # add pg2 (biases)
# 对于卷积层权重的学习率,每十轮调整为原来的 0.1 倍
pg1 = lambda epoch: 0.1 ** (epoch // 10)
# 对于偏置权重的学习率,每轮调整为原来的 0.94 倍
pg2 = lambda epoch: 0.94 ** epoch
scheduler = LambdaLR(
optimizer=optimizer,
lr_lambda=[pg1, pg2], #传入一个函数或一个以函数为元素列表,作为学习率调整的策略
last_epoch=-1
)
weight_lrs, bias_lrs, epochs = [], [], []
for epoch in range(num_epochs):
weight_lrs.append(scheduler.get_lr()[0])
bias_lrs.append(scheduler.get_lr()[1])
epochs.append(epoch)
pass # iter and train here
scheduler.step(epoch)
# visualize
plt.figure()
plt.plot(epochs, weight_lrs, label='step')
plt.plot(epochs, bias_lrs, label='exponential')
plt.legend()
plt.show()