torch finetune冻结层操作
这个做了简单的实验:
文章说了两种冻结层的方法:
一、设置requires_grad为False
第一步:
for param in model.named_parameters():
if param[0] in need_frozen_list:
param[1].requires_grad = False
这种方法需要注意的是层名一定要和model中一致,model经过.cuda后往往所用层会添加module.的前缀,会导致后面的冻结无效。
第二步,还需要注意的是加上filter:
optimizer = torch.optim.SGD(filter(lambda p: p.requires_grad, model.parameters()), lr=args.lr,momentum=args.momentum, weight_decay=args.weight_decay)
第三步,模型需要设置eval(),train(False)。
二、使用 torch.no_grad()
这种方式只需要在网络定义中的forward方法中,将需要冻结的层放在 torch.no_grad()下,强力推这种方式。
class xxnet(nn.Module):
def __init__():
....
self.layer1 = xx
self.layer2 = xx
self.fc = xx
def forward(self.x):
with torch.no_grad():
x = self.layer1(x)
x = self.layer2(x)
x = self.fc(x)
return x
模型也需要设置eval(),train(False)。
这种方式则是将layer1和layer2定义的层冻结,只训练fc层的参数。
我看了一下,层的参数确实冻结了,但是训练过程中,网络预测的结果一直是变的。
经过反复测试,发现是model.train()的原因:
model.train()
是保证BN层能够用到每一批数据的均值和方差。对于Dropout,model.train()
是随机取一部分网络连接来训练更新参数。
3、调整学习率衰减。
方法一:使用torch.optim.lr_scheduler()函数:
model = Mymodel()
if use_gpu:
model = model.cuda()
# loss
criterion = nn.CrossEntropyLoss()
# optimizer
ignored_params = list(map(id, model.ViewModel.viewclassifier.parameters())) + list(map(id, model.Block.parameters()))
base_params = filter(lambda p: id(p) not in ignored_params, model.parameters())
optimizer_ft = optim.SGD([
{'params': base_params, 'lr': 0.01},
{'params': model.ViewModel.viewclassifier.parameters(), 'lr': 0.001},
{'params': model.Block.parameters(), 'lr': 0.01}
], weight_decay=1e-3, momentum=0.9, nesterov=True)
exp_lr_scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=60, gamma=0.1)
scheduler.step() # put it before model.train(True)
model.train(True) # Set model to training mode
....
方法二:使用optimizer.param_groups方法。(好处:能分别设定不同层的衰减率!)
model = Mymodel()
if use_gpu:
model = model.cuda()
criterion = nn.CrossEntropyLoss()
ignored_params = list(map(id, model.ViewModel.viewclassifier.parameters())) + list(map(id, model.Block.parameters()))
base_params = filter(lambda p: id(p) not in ignored_params, model.parameters())
optimizer_ft = optim.SGD([
{'params': base_params, 'lr': 0.01},
{'params': model.ViewModel.viewclassifier.parameters(), 'lr': 0.001},
{'params': model.Block.parameters(), 'lr': 0.03}],
weight_decay=1e-3, momentum=0.9, nesterov=True)
def adjust_lr(epoch):
step_size = 60
lr = args.lr * (0.1 ** (epoch // 30))
for g in optimizer.param_groups:
g['lr'] = lr * g.get('lr')
### optimizer.param_groups 类型与内容
[
{ 'params': base_params, 'lr': 0.01, 'momentum': 0.9, 'dampening': 0,
'weight_decay': 0.001, 'nesterov': True, 'initial_lr': 0.01 },
{ 'params': model.ViewModel.viewclassifier.parameters(), 'lr': 0.001,
'momentum': 0.9, 'dampening': 0, 'weight_decay': 0.001, 'nesterov': True,
'initial_lr': 0.001 },
{ 'params': model.Block.parameters(), 'lr': 0.03, 'momentum': 0.9,
'dampening': 0, 'weight_decay': 0.001, 'nesterov': True, 'initial_lr':
0.03 }
]
### optimizer.param_groups 类型与内容
for epoch in range(start_epoch, args.epochs):
adjust_lr(epoch) # 每epoch更新一次。
model.train(True) # Set model to training mode
....
补充知识:python中的字典方法.get():