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

动态调整学习率

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

第一种 官方例子中按照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 1
        elif args.warmup < epoch <= milestones[0]:
            return 0
        for i in range(1, len(milestones)):
            if milestones[i - 1] < epoch <= milestones[i]:
                return i
        return len(milestones)

    n = to(epoch)

    global lr
    lr = 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'] = lr
    return 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)) # dict
        for 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 optim

optimizer = 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 = False

import 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的参数进行迭代优化。

  • 48
    点赞
  • 156
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值