0 为什么引入学习率衰减?
我们都知道几乎所有的神经网络采取的是梯度下降法来对模型进行最优化,其中标准的权重更新公式:
W
+
=
α
?
?gradient?
W+=\alpha * \text { gradient }
W+=α??gradient?
- 学习率
α
\alpha
α 控制着梯度更新的步长(step),
α
\alpha
α 越大,意味着下降的越快,到达最优点的速度也越快,如果为
0
0
0,则网络就会停止更新
- 学习率过大,在算法优化的前期会加速学习,使得模型更容易接近局部或全局最优解。但是在后期会有较大波动,甚至出现损失函数的值围绕最小值徘徊,波动很大,始终难以达到最优。
所以引入学习率衰减的概念,直白点说,就是在模型训练初期,会使用较大的学习率进行模型优化,随着迭代次数增加,学习率会逐渐进行减小,保证模型在训练后期不会有太大的波动,从而更加接近最优解。
本文所有的实验代码可在如下链接下载并测试 lr_scheduler_test.py-深度学习文档类资源-CSDN下载
1 查看学习率
print("Lr:{}".format(optimizer.state_dict()['param_groups'][0]['lr']))
之后我会用类似于如下的代码进行学习率的测试输出
def train():
traindataset = TrainDataset()
traindataloader = DataLoader(dataset = traindataset,batch_size=100,shuffle=False)
net = Net().cuda()
myloss = nn.MSELoss().cuda()
optimizer = optim.SGD(net.parameters(), lr=0.001 )
for epoch in range(100):
print("Epoch:{} Lr:{:.2E}".format(epoch,optimizer.state_dict()['param_groups'][0]['lr']))
for data,label in traindataloader :
data = data.cuda()
label = label.cuda()
output = testnet(data)
loss = myloss(output,label)
optimizer.zero_grad()
loss.backward()
optimizer.step()
scheduler.step()
2 最常用的针对全局的学习率设置
需要根据你选择的优化器的种类把具体你想要的lr 作为可选参数的一部分传入到新建的优化器类初始化中
optimizer = optim.SGD(net.parameters(), lr=0.001 )
3 针对不同层设置不一样的学习率
当我们在使用预训练的模型时,需要对分类层进行单独修改并进行初始化,其他层的参数采用预训练的模型参数进行初始化,这个时候我们希望在进行训练过程中,除分类层以外的层只进行微调,不需要过多改变参数,因此需要设置较小的学习率。而改正后的分类层则需要以较大的步子去收敛,学习率往往要设置大一点
以一个简单的网络为例
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.net1 = nn.Linear(2,10)
self.net2 = nn.Linear(10,1)
def forward(self, x):
x = self.net1(x)
x = self.net2(x)
return x
net = Net()
optimizer = optim.SGD([
{"params":model.net1.parameters()},
{"params":model.net2.parameters(),"lr":1e-5},],
lr=1e-2,
)
for epoch in range(100):
print("Epoch:{} Lr:{:.2E}".format(epoch,optimizer.state_dict()['param_groups'][0]['lr']))
print("Epoch:{} Lr:{:.2E}".format(epoch,optimizer.state_dict()['param_groups'][1]['lr']))
optimizer.step()
以resnet101 为例,分层设置学习率。
model = torchvision.models.resnet101(pretrained=True)
large_lr_layers = list(map(id,model.fc.parameters()))
small_lr_layers = filter(lambda p:id(p) not in large_lr_layers,model.parameters())
optimizer = torch.optim.SGD([
{"params":large_lr_layers},
{"params":small_lr_layers,"lr":1e-4}
],lr = 1e-2,momenum=0.9)
注:large_lr_layers 学习率为 1e-2 ,small_lr_layers 学习率为 1e-4,两部分参数共用一个momenum
4 手动设置自动衰减的学习率
def adjust_learning_rate(optimizer, epoch, start_lr):
"""Sets the learning rate to the initial LR decayed by 10 every 30 epochs"""
lr = start_lr * (0.1 ** (epoch // 3))
for param_group in optimizer.param_groups:
param_group['lr'] = lr
注释:在调用此函数时需要输入所用已经初始化完毕的optimizer 以及对应的epoch ,并且start_lr 作为初始化的学习率也需要给出。
optimizer = torch.optim.SGD(net.parameters(),lr = start_lr)
for epoch in range(100):
adjust_learning_rate(optimizer,epoch,start_lr)
print("Epoch:{} Lr:{:.2E}".format(epoch,optimizer.state_dict()['param_groups'][0]['lr']))
for data,label in traindataloader :
data = data.cuda()
label = label.cuda()
output = net(data)
loss = myloss(output,label)
optimizer.zero_grad()
loss.backward()
optimizer.step()
5 手动根据自定义列表进行学习率指定
def adjust_learning_rate_list(optimizer, epoch):
lr_set_list = [[1,1e-1],[2,1e-2],[3,1e-3],[4,1e-4],[5,1e-5]]
lr_list = []
for i in lr_set_list:
for j in range(i[0]):
lr_list.append(i[1])
for param_group in optimizer.param_groups:
if epoch < len(lr_list)-1:
param_group['lr'] = lr_list[epoch]
else:
param_group['lr'] = lr_list[-1]
6 使用pytorch提供的学习率
在torch.optim.lr_scheduler 内部,基于当前epoch 的数值,封装了几种相应的动态学习率调整方法,该部分的官方手册传送门——optim.lr_scheduler官方文档。需要注意的是学习率的调整需要应用在优化器参数更新之后,也就是说:
optimizer = torch.optim.XXXXXXX()
scheduler = torch.optim.lr_scheduler.XXXXXXXXXX()
for i in range(epoch):
for data,label in dataloader:
out = net(data)
output_loss = loss(out,label)
optimizer.zero_grad()
loss.backward()
optimizer.step()
scheduler.step()
其具体的学习率策略应用的简要代码示例如下:
6.1 lr_scheduler.LambdaLR
更新策略:
将每一个参数组的学习率调整为初始化学习率lr 的给定函数倍(lr_lambda ),在fine-tune 中十分有用,我们不仅可以为不同的层设定不同的学习率,还可以为其设定不同的学习率调整策略。
初始化方法:
torch.optim.lr_scheduler.LambdaLR(optimizer, lr_lambda, last_epoch=-1, verbose=False)
几个最常用的函数:
lr_lambda = lambda step : (1.0-step/args.total_iters) if step <= args.total_iters else 0
def lr_poly(base_lr, iter, max_iter, power):
return base_lr*((1-float(iter)/max_iter)**(power))
def adjust_learning_rate(optimizer, learning_rate, i_iter, max_iter, power):
"""Sets the learning rate to the initial LR divided by 5 at 60th, 120th and 160th epochs"""
lr = lr_poly(learning_rate, i_iter, max_iter, power)
optimizer.param_groups[0]['lr'] = lr
return lr
参数
-
optimizer(Optimizer) :是之前定义好的需要优化的优化器的实例名 -
lr_lambda(function or list) :可以是function或是function list,给定整数参数epoch计算乘数的函数,或者是list形式的函数,分别计算各个parameter groups的学习率更新用到的学习率。一般是一个关于epoch数目的函数,从而计算出一个乘数因子,并根据该乘数因子调整初始学习率。 -
last_epoch(int) :默认为-1,它一般不用设置,为-1时的作用是将人为设置的学习率设定为调整学习率的基础值lr。这里需要注意的是,last_epoch默认为-1只能保证第一次调整学习率时,原始待调整的值为人工设定的初始学习率,而第二次调整学习率时,调整的基值就变成了第一次调整后的学习率。如果是训练了很多个epoch后中断了,继续训练,这个值就等于加载的模型的epoch。默认为-1表示从头开始训练,即从epoch=1开始 -
verbose(bool) :True的话为每次更新打印一个stdout,默认为False
注意: 在将optimizer 传给scheduler 后,在shcduler 类的__init__ 方法中会给optimizer.param_groups 列表中的那个元素(字典)增加一个key = "initial_lr" 的元素表示初始学习率,等于optimizer.defaults['lr'] 。
举例:
lambda1 = lambda epoch: 0.95 ** epoch
optimizer = torch.optim.SGD(net.parameters(), lr=0.001 )
scheduler = torch.optim.lr_scheduler.LambdaLR(optimizer, lr_lambda=lambda1)
6.2 torch.optim.lr_scheduler.StepLR
更新策略:
这是比较常用的等间隔动态调整方法,每经过step_size个epoch,做一次学习率decay,以gamma值为缩小倍数。
初始化方法:
torch.optim.lr_scheduler.StepLR(optimizer, step_size, gamma=0.1, last_epoch=-1)
参数:
optimizer(Optimizer) :是之前定义好的需要优化的优化器的实例名step_size(int) :是学习率衰减的周期,每经过step_size 个epoch,做一次学习率decaygamma(float) :学习率衰减的乘法因子。Default:0.1last_epoch(int) :默认为-1,它一般不用设置,为-1时的作用是将人为设置的学习率设定为调整学习率的基础值lr。这里需要注意的是,last_epoch默认为-1只能保证第一次调整学习率时,原始待调整的值为人工设定的初始学习率,而第二次调整学习率时,调整的基值就变成了第一次调整后的学习率。如果是训练了很多个epoch后中断了,继续训练,这个值就等于加载的模型的epoch。默认为-1表示从头开始训练,即从epoch=1开始verbose(bool) :如果为True,每一次更新都会打印一个标准的输出信息 ,Default:False
注意:
此函数产生的decay 效果,可能与函数外部的对于学习率的更改同时发生,当last_epoch = -1 时,将初始lr 设置为Ir 。
举例:
optimizer = torch.optim.SGD(net.parameters(), lr=0.001 )
scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=5, gamma=0.1)
6.3 lr_scheduler.MultiStepLR
更新策略:
一旦达到某一阶段(milestones)时,就可以通过gamma系数降低每个参数组的学习率。
可以按照milestones列表中给定的学习率,进行分阶段式调整学习率。
初始化方法:
torch.optim.lr_scheduler.MultiStepLR(optimizer, milestones, gamma=0.1, last_epoch=-1, verbose=False)
参数:
optimizer(Optimizer) :是之前定义好的需要优化的优化器的实例名milestones(list) :是一个关于epoch数值的list ,表示在达到哪个epoch范围内开始变化,必须是升序排列gamma(float) :学习率衰减的乘法因子。Default:0.1last_epoch(int) :默认为-1,它一般不用设置,为-1时的作用是将人为设置的学习率设定为调整学习率的基础值lr。这里需要注意的是,last_epoch默认为-1只能保证第一次调整学习率时,原始待调整的值为人工设定的初始学习率,而第二次调整学习率时,调整的基值就变成了第一次调整后的学习率。如果是训练了很多个epoch后中断了,继续训练,这个值就等于加载的模型的epoch。默认为-1表示从头开始训练,即从epoch=1开始verbose(bool) :如果为True,每一次更新都会打印一个标准的输出信息 ,Default:False
注意:
此函数产生的decay 效果,可能与函数外部的对于学习率的更改同时发生,当last_epoch = -1 时,将初始lr 设置为lr 。
举例:
optimizer = torch.optim.SGD(net.parameters(), lr=0.001 )
scheduler = torch.optim.lr_scheduler.MultiStepLR(optimizer, milestones=[2,6,15], gamma=0.1)
6.4 lr_scheduler.ExponentialLR
更新策略:
每一次epoch,lr都乘gamma
初始化方法:
torch.optim.lr_scheduler.ExponentialLR(optimizer, gamma, last_epoch=-1, verbose=False)
参数:
optimizer(Optimizer) :是之前定义好的需要优化的优化器的实例名gamma(float) :学习率衰减的乘法因子。Default:0.1last_epoch(int) :默认为-1,它一般不用设置,为-1时的作用是将人为设置的学习率设定为调整学习率的基础值lr。这里需要注意的是,last_epoch默认为-1只能保证第一次调整学习率时,原始待调整的值为人工设定的初始学习率,而第二次调整学习率时,调整的基值就变成了第一次调整后的学习率。如果是训练了很多个epoch后中断了,继续训练,这个值就等于加载的模型的epoch。默认为-1表示从头开始训练,即从epoch=1开始verbose(bool) :如果为True,每一次更新都会打印一个标准的输出信息 ,Default:False
举例:
optimizer = torch.optim.SGD(net.parameters(), lr=0.001 )
scheduler = torch.optim.lr_scheduler.ExponentialLR(optimizer, gamma=0.1)
6.5 lr_scheduler.CosineAnnealingLR
更新策略:
按照余弦波形的衰减周期来更新学习率,前半个周期从最大值降到最小值,后半个周期从最小值升到最大值
初始化方法:
torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max, eta_min=0, last_epoch=-1, verbose=False)
参数:
optimizer(Optimizer) :是之前定义好的需要优化的优化器的实例名T_max (int) : 余弦波形周期的一半,比如T_max=10,则学习率衰减周期为20,其中前半段即前10个周期学习率从最大值降到最小值,后10个周期从最小值升到最大值eta_min(float) :学习率衰减的最小值,Default:0last_epoch(int) :默认为-1,它一般不用设置,为-1时的作用是将人为设置的学习率设定为调整学习率的基础值lr。这里需要注意的是,last_epoch默认为-1只能保证第一次调整学习率时,原始待调整的值为人工设定的初始学习率,而第二次调整学习率时,调整的基值就变成了第一次调整后的学习率。如果是训练了很多个epoch后中断了,继续训练,这个值就等于加载的模型的epoch。默认为-1表示从头开始训练,即从epoch=1开始verbose(bool) :如果为True,每一次更新都会打印一个标准的输出信息 ,Default:False
举例:
optimizer = torch.optim.SGD(net.parameters(), lr=0.001 )
scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max = 10)
6.6 lr_scheduler.ReduceLROnPlateau
更新策略:
与上述基于epoch数目调整学习率的方法不同,该方法是PyTorch提供的一种基于验证指标的调整方法。它的原理是:当指标停止改善时,降低学习率。当模型的学习停滞时,训练过程通常会受益于将学习率降低2~10倍。该种调整方法读取一个度量指标,如果在“耐心”期间内没有发现它有所改善,那么就会降低学习率。
初始化方法:
torch.optim.lr_scheduler.ReduceLROnPlateau(optimizer, mode='min', factor=0.1, patience=10, verbose=False, threshold=0.0001, threshold_mode= 'rel', cooldown=0, min_1r=0, eps=1e-08)
step()方法:
scheduler.step(loss)
参数:
optimizer(Optimizer) :是之前定义好的需要优化的优化器的实例名mode :可选str字符串数据,为min或max。当选择min时,代表当度量指标停止下降时,开始减小学习率;当选择max时,代表当度量指标停止上升时,开始减小学习率。factor :float类型数据,学习率调整的乘法因子,默认值为0.1。patience :int类型数据,可容忍的度量指标没有提升的epoch数目,默认为10。举例说明,当其设置为10时,我们可以容忍10个epoch内没有提升,如果在第11个epoch依然没有提升,那么就开始降低学习率。verbose :bool数据,如果设置为True,输出每一次更新的信息,默认为False。threshold :float类型数据,衡量新的最佳阈值,仅关注重大变化,默认为0.0001。threshold_mode :可选str字符串数据,为rel或abs,默认为rel。在rel模式下,如果mode参数为max,则动态阈值(dynamic_threshold)为best*(1+threshold),如果mode参数为min,则动态阈值为best+threshold,如果mode参数为min,则动态阈值为best-threshold。cooldown :int类型数据,减少lr后恢复正常操作之前要等待的epoch数,默认为0。min_lr :float或list类型数据,学习率的下界,默认为0。eps :float类型数据,学习率的最小变化值。如果调整后的学习率和调整前的差距小于eps的话,那么就不做任何调整,默认为1e-8。
举例:
optimizer = torch.optim.SGD(net.parameters(), lr=0.001 )
scheduler = torch.optim.lr_scheduler.ReduceLROnPlateau(optimizer, 'min',patience=5)
scheduler.step(loss)
LAST 参考文献
pytorch 模型不同层设置不同的学习率 - 华为云
PyTorch中设置学习率衰减的方法/torch.optim.lr_scheduler/learning_rate_decay - 知乎
Pytorch动态调整学习率的方法详解及示例_风雪夜归人o的博客-CSDN博客
torch.optim — PyTorch 1.9.0 documentation
pytorch 动态调整学习率 - 超杰
Pytorch中,动态调整学习率、不同层设置不同学习率和固定某些层训练的方法_我的博客有点东西-CSDN博客
|