当前位置: 代码迷 >> 综合 >> Pytorch中,动态调整学习率、不同层设置不同学习率和固定某些层训练的方法
  详细解决方案

Pytorch中,动态调整学习率、不同层设置不同学习率和固定某些层训练的方法

热度:81   发布时间:2023-10-20 19:47:54.0

动态调整学习率

三种方法我都写成直接调用的函数了,所以大家复制走拿去用就行了。

第一种 官方例子中按照milestone调整的办法

def adjust_learning_rate(optimizer, epoch, milestones=None):"""Sets the learning rate: milestone is a list/tuple"""def to(epoch):if epoch <= args.warmup:return 1elif args.warmup < epoch <= milestones[0]:return 0for i in range(1, len(milestones)):if milestones[i - 1] < epoch <= milestones[i]:return ireturn len(milestones)n = to(epoch)global lrlr = args.base_lr * (0.2 ** n)for param_group in optimizer.param_groups:param_group['lr'] = lr

args.warmup和args.base_lr分别是1和初始学习率。函数to的目的就是求出当前epoch在milestone的哪个范围内,不同范围代表不同的衰减率,用返回的数字来区别epoch的范围。

之后声明lr是全局的,这样做可能是因为在函数外部有使用lr的地方,函数内容就直接改变的是全局的lr。

第二种

来看一下旷世开源的shuffleNet系列中使用的学习率变化策略。

用的学习率衰减策略是根据当前迭代次数选取的。

    scheduler = torch.optim.lr_scheduler.LambdaLR(optimizer,lambda step : (1.0-step/args.total_iters) if step <= args.total_iters else 0, last_epoch=-1)

每当运行一次 scheduler.step(),参数的学习率就会按照lambda公式衰减。

 

第三种

CCNet官方源码中改变学习率的方法。这个学习率衰减策略是最常用的,被称作多项式衰减法。

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'] = lrreturn lr

NOTE:

看到有些代码用for循环修改optimizer.param_groups,这个group的数目是model.parameters被分为几个字典,就是几个group,每个group有不同的学习率,weight_decay,等等

    optimizer = torch.optim.SGD(model.parameters(),lr=0.1,momentum=0.9,weight_decay=0.005)for o in optimizer.param_groups:# print(type(o)) # dictfor k,v in o.items():print(k)####
params
lr   # optimizer.param_groups[0]['lr']就是这个
momentum
dampening
weight_decay
nesterov

 

如果定义optimizer只用了一组parameters,不是用形如:

optimizer = SGD([{params: params_1, 'lr':0.1},

                             {params:params_2, 'lr': 0.2}]

 

那么不需要for循环了,直接访问optimizer.param_groups[0]['lr']修改。

否则还是for循环吧,可读性高。


 

不同层设置不同的学习率

先搭建一个小网络。

import torch.nn as nn
class Net(nn.Module):def __init__(self):super(Net,self).__init__()self.conv1 = nn.Conv2d(3,32,3)self.conv2 = nn.Conv2d(32,24,3)self.prelu = nn.PReLU()for m in self.modules():if isinstance(m,nn.Conv2d):nn.init.xavier_normal_(m.weight.data)nn.init.constant_(m.bias.data,0)if isinstance(m,nn.Linear):m.weight.data.normal_(0.01,0,1)m.bias.data.zero_()def forward(self, input):out = self.conv1(input)out = self.conv2(out)out = self.prelu(out)return out

我们现在看看这个模型的modules

model = Net()
for m in model.modules():print(m)'''
Net((conv1): Conv2d(3, 32, kernel_size=(3, 3), stride=(1, 1))(conv2): Conv2d(32, 24, kernel_size=(3, 3), stride=(1, 1))(prelu): PReLU(num_parameters=1)
)
Conv2d(3, 32, kernel_size=(3, 3), stride=(1, 1))
Conv2d(32, 24, kernel_size=(3, 3), stride=(1, 1))
PReLU(num_parameters=1)'''

比如说我们想对前两个卷积层的学习率设置为0.2(注意是前两层),对PRelu激活函数中的参数设置为0.02。剩下的参数用0.3的学习率(假象网络比我写的更加大)。

model = Net()
conv_params = list(map(id,model.conv1.parameters()))   #提出前两个卷积层存放参数的地址
conv_params += list(map(id,model.conv2.parameters()))
prelu_params = []
for m in model.modules():    #找到Prelu的参数if isinstance(m, nn.PReLU):prelu_params += m.parameters()#假象网络比我写的很大,还有一部分参数,这部分参数使用另一个学习率
rest_params = filter(lambda x:id(x) not in conv_params+list(map(id,prelu_params)),model.parameters())  #提出剩下的参数
print(list(rest_params))
'''
>> []   #是空的,因为我举的例子没其他参数了
'''
import torch.optim as optimoptimizer = optim.Adam([{'params':model.conv1.parameters(),'lr':0.2},{'params':model.conv2.parameters(),'lr':0.2},{'params':prelu_params,'lr':0.02},{'params':rest_params,'lr':0.3}])

 

固定某些层训练

思路就是利用tensor的requires_grad,每一个tensor都有自己的requires_grad成员,值只能为True和False。

  • 我们对不需要参与训练的参数的requires_grad设置为False。
  • 在optim参数模型参数中过滤掉requires_grad为False的参数。

还是以上面搭建的简单网络为例,我们固定第一个卷积层的参数,训练其他层的所有参数。

  • 需要遍历第一层的参数,然后为其设置requires_grad
model = Net()
for name, p in model.named_parameters():if name.startswith('conv1'):p.requires_grad = Falseimport torch.optim as optim
optimizer = optim.Adam(filter(lambda x: x.requires_grad is not False ,model.parameters()),lr= 0.2)

为了验证一下我们的设置是否正确,我们分别看看model中的参数的requires_grad和optim中的params_group()。

for p in model.parameters():print(p.requires_grad)
'''
False
False
True
True
True
'''
for p in optimizer.param_groups[0]['params']:print(p.requires_grad)print(type(p))'''
True
<class 'torch.nn.parameter.Parameter'>
True
<class 'torch.nn.parameter.Parameter'>
True
<class 'torch.nn.parameter.Parameter'>
'''

能看出优化器仅仅对requires_grad为True的参数进行迭代优化。