文章目录
- 1. 为什么引入学习率衰减?
- 2. 针对不同层设置不一样的学习率
- 3. 手动更新学习率
- 4. 使用学习率调度器
- 5. 推荐做法
在前面的文章中,介绍了各种学习率。在此,将进行拓展,学习如何手动更新学习率(即不使用pytorch自带的学习率调度器)。本文详细探讨了学习率衰减在神经网络优化中的作用,包括为何引入、如何调整不同层的学习率、手动设置衰减方法以及利用PyTorch内置的lr_scheduler。通过实例演示了如何在实际项目中应用这些策略以提高模型性能。
1. 为什么引入学习率衰减?
我们都知道几乎所有的神经网络采取的是梯度下降法来对模型进行最优化,其中标准的权重更新公式:
W+=α∗ gradient
学习率 α 控制着梯度更新的步长(step),α越大,意味着下降的越快,到达最优点的速度也越快,如果为0,则网络就会停止更新。学习率过大,在算法优化的前期会加速学习,使得模型更容易接近局部或全局最优解。但是在后期会有较大波动,甚至出现损失函数的值围绕最小值徘徊,波动很大,始终难以达到最优。
总而言之,通过引入学习率衰减,在模型训练初期,会使用较大的学习率进行模型优化,随着迭代次数增加,模型逐渐收敛,学习率会逐渐进行减小,保证模型在训练后期不会有太大的波动,从而更加接近最优解。
2. 针对不同层设置不一样的学习率
当我们在使用预训练的模型时,需要对分类层进行单独修改并进行初始化,其他层的参数采用预训练的模型参数进行初始化,这个时候我们希望在进行训练过程中,除分类层以外的层只进行微调,不需要过多改变参数,因此需要设置较小的学习率。而改正后的分类层则需要以较大的步子去收敛,学习率往往要设置大一点
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()
3. 手动更新学习率
根据衰减参数设置学习率:
for param_group in self.optimizer.param_groups:
param_group['lr'] = self.learning_rate * (self.gamma ** (epoch // self.lr_decay))
根据参数列表设置学习率:
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]]# 执行此学习率的epoch数
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]
optimizer = torch.optim.SGD(net.parameters(),lr = start_lr)
for epoch in range(100):
adjust_learning_rate_list(optimizer,epoch)
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()
优点:
灵活性:你可以完全控制学习率如何随时间变化,包括调整衰减率(self.gamma)、衰减间隔(self.lr_decay)等。
简单性:对于简单的学习率调整策略,代码相对简洁明了。
缺点:
维护成本:需要手动在每个epoch后更新学习率,增加了代码的复杂性,尤其是在训练循环中。
易出错:容易忘记更新学习率或更新错误,特别是在复杂的训练流程中。
不易复用:每次训练都需要手动设置和调整学习率更新逻辑。
4. 使用学习率调度器
PyTorch提供了多种学习率调度器,如StepLR、MultiStepLR、ExponentialLR等。
例如,使用ExponentialLR:
scheduler = torch.optim.lr_scheduler.ExponentialLR(self.optimizer, gamma=self.gamma)
# 在每个epoch后调用
scheduler.step()
优点:
自动化:学习率调度器自动在每个epoch(或指定的steps)后更新学习率,减少了手动干预。
易维护:代码更加整洁,学习率策略的配置和更新逻辑被封装在调度器中。
可复用性:调度器可以很容易地在不同的训练任务中复用。
灵活性:虽然调度器提供了常见的学习率调整策略,但也可以通过继承自定义调度器来实现更复杂的学习率策略。
缺点:
学习曲线:对于初学者来说,可能需要一些时间来熟悉不同调度器的使用方法和适用场景。
关于pytorch各种学习率,可以参考这篇文章:深度学习之pytorch常见的学习率绘制
5. 推荐做法
对于大多数情况,建议使用PyTorch提供的学习率调度器。它们不仅简化了学习率的管理,还提供了多种常见的学习率策略,并且易于扩展和定制。手动更新学习率更适合于非常特定或复杂的学习率调整需求,以及对学习率策略有深入理解和控制需求的场景。