pytorch冻结模型部分层的参数相关问题整理
问题一:固定模型参数的方法
for para in model.parameters():
para.requires_grad = Flase
optimizer = optim.Adam(filter(lambda p: p.requires_grad, net.parameters()), lr=0.1)
问题二:requires_grad==False
的参数是否要加入optimizer
结论:加不加output的结果都一样。使用filter过滤一下的好处是防止后面犯一些意想不到的错误。后面要是想重新加上,可以使optimizer.add_param_group()
把需要加的参数加到param_groups
里面(见下例),这个时候,optimizer
的param_groups
的长度就增加了1,param_groups
是一个列表,里面的每个元素都是字典,每个字典有关键字'params'
、'lr'
等等。
问题三:开始固定,后来又解冻的那些参数,他们的学习率如何变化
结论:这个要分情况讨论
情况一:requires_grad==False
的参数是也加入optimizer
,这种情况下,这些参数一直都在optimizer
中,他的学习率随着他所在的那个para_group变化而变化。这个可以用optimizer.para_groups[索引(比如0)]['lr']
查看,
情况二:requires_grad==False
在optimizer
创建中被过滤掉,后面需要使用的时候,再使用optimizer.add_param_group()
加上去,从这个add就可以看出来,他是在原来的optimizer.para_groups
的后面append
上去的,因此,这个是新加进optimizer上的,使用scheduler.step的时候,新加进去的这些参数的是从optimizer创建时候的初始学习率开始的。
举个例子:
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.fc1 = nn.Linear(5, 3)
self.fc2 = nn.Linear(3, 1)
def forward(self, x):
x = self.fc1(x)
x = self.fc2(x)
return x
net = Net()
random_input = Variable(torch.randn(5, ))
random_target = Variable(torch.randn(1, ))
net.fc2.weight.requires_grad = False
net.fc2.bias.requires_grad = False
optimizer = optim.Adam(filter(lambda p: p.requires_grad, net.parameters()), lr=0.1)
scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=2, gamma=0.1,last_epoch=-1)
for i in range(4):
optimizer.zero_grad()
output = net(random_input)
loss = criterion(output, random_target)
loss.backward()
optimizer.step()
scheduler.step()
net.fc2.weight.requires_grad = True
net.fc2.bias.requires_grad = True
optimizer.add_param_group({'params': net.fc2.parameters()})
for i in range(4):
optimizer.zero_grad()
output = net(random_input)
loss = criterion(output, random_target)
loss.backward()
optimizer.step()
scheduler.step()
- 创建好后:
optimizer
的param_group
s的长度为1,只有索引0,里面的param对应的参数并没有fc2. - 第一次的四个epoch:此时
optimizer
的参数的学习率每隔2个epoch变成原来的0.1,所以依次为0.1 -> 0.1 -> 0.01 -> 0.01
- fc2的
requires_grad
设置为True,并add进optimizer
:optimizer的param_groups的长度为2,索引1对应的字典,里面的param对应的参数是fc2,此时查看optimizer.param_groups,发现,param_groups[0]里面新增了一个键值对'initial_lr': 0.1
,并且'lr':0.001
,param_groups[1]是对应的是刚才新增的fc2,里面的'lr':0.1
,没有键'initial_lr'
。 - 第二次的四个epoch:此时
optimizer
的参数的学习率每隔2个epoch变成原来的0.1(注意此时param_groups
两组的学习率是不相等的)。所以param_groups[0]
的就是0.001 -> 0.001 -> 0.0001 -> 0.0001
,param_groups[1]
的是0.1 -> 0.1 -> 0.01 -> 0.01
。