参考来源:
CSDN:pytorch 冻结模型中某几层的参数
CSDN:pytorch 之 冻结某层参数,即训练时不更新

首先,我们知道,深度学习网络中的参数是通过计算梯度,在反向传播进行更新的,从而能得到一个优秀的参数,但是有的时候,我们想固定其中的某些层的参数不参与反向传播。比如说,进行微调时,我们想固定已经加载预训练模型的参数部分,指向更新最后一层的分类器,这时应该怎么做呢。

首先定义如下的模型:

  1. class Char3SeqModel(nn.Module):
  2. def __init__(self, char_sz, n_fac, n_h):
  3. super().__init__()
  4. self.em = nn.Embedding(char_sz, n_fac)
  5. self.fc1 = nn.Linear(n_fac, n_h)
  6. self.fc2 = nn.Linear(n_h, n_h)
  7. self.fc3 = nn.Linear(n_h, char_sz)
  8. def forward(self, ch1, ch2, ch3):
  9. # do something
  10. out = #....
  11. return out
  12. model = Char3SeqModel(10000, 50, 25)

我们通过设置参数 paramrequires_grad 属性为 False ,来冻结该层参数。当然这样还不够,我们要在定义优化器的时候,告诉优化器,哪些需要更新,那些不需要,这一步至关重要。

假如我们想要冻结 fc1 层,需要做如下操作:

  1. model = Char3SeqModel()
  2. # 这里是一般情况,共享层往往不止一层,所以做一个for循环
  3. for para in model.fc1.parameters():
  4. para.requires_grad = False
  5. # 假如真的只有一层也可以这样操作:
  6. # model.fc1.weight.requires_grad = False

最后我们需要将需要优化的参数传入优化器,不需要传入的参数过滤掉,所以要用到 filter() 函数。

  1. optimizer = optim.Adam(filter(lambda p: p.requires_grad, model.parameters()), lr=0.1)

终极方法代码实现

终极方法代码实现:

  1. from collections.abc import Iterable
  2. def set_freeze_by_names(model, layer_names, freeze=True):
  3. if not isinstance(layer_names, Iterable):
  4. layer_names = [layer_names]
  5. for name, child in model.named_children():
  6. if name not in layer_names:
  7. continue
  8. for param in child.parameters():
  9. param.requires_grad = not freeze
  10. def freeze_by_names(model, layer_names):
  11. set_freeze_by_names(model, layer_names, True)
  12. def unfreeze_by_names(model, layer_names):
  13. set_freeze_by_names(model, layer_names, False)
  14. def set_freeze_by_idxs(model, idxs, freeze=True):
  15. if not isinstance(idxs, Iterable):
  16. idxs = [idxs]
  17. num_child = len(list(model.children()))
  18. idxs = tuple(map(lambda idx: num_child + idx if idx < 0 else idx, idxs))
  19. for idx, child in enumerate(model.children()):
  20. if idx not in idxs:
  21. continue
  22. for param in child.parameters():
  23. param.requires_grad = not freeze
  24. def freeze_by_idxs(model, idxs):
  25. set_freeze_by_idxs(model, idxs, True)
  26. def unfreeze_by_idxs(model, idxs):
  27. set_freeze_by_idxs(model, idxs, False)
  1. # 冻结第一层
  2. freeze_by_idxs(model, 0)
  3. # 冻结第一、二层
  4. freeze_by_idxs(model, [0, 1])
  5. #冻结倒数第一层
  6. freeze_by_idxs(model, -1)
  7. # 解冻第一层
  8. unfreeze_by_idxs(model, 0)
  9. # 解冻倒数第一层
  10. unfreeze_by_idxs(model, -1)
  11. # 冻结 em层
  12. freeze_by_names(model, 'em')
  13. # 冻结 fc1, fc3层
  14. freeze_by_names(model, ('fc1', 'fc3'))
  15. # 解冻em, fc1, fc3层
  16. unfreeze_by_names(model, ('em', 'fc1', 'fc3'))

只训练 classifier 层

有的同学表示,我大部分层需要冻结,只有少部分层要训练,那这样一层一层的稍显麻烦,我们应该怎么做呢,还拿上面的模型举例,假设我的模型是按序定义的,则我只想训练 classifier 层,其余的统统冻结。
那么:

  1. class Net(nn.Module):
  2. def __init__(self):
  3. super(Net, self).__init__()
  4. self.conv1 = nn.Conv2d()
  5. self.conv2 = nn.Conv2d()
  6. self.fc1 = nn.Squential(
  7. nn.Linear(),
  8. nn.Linear(),
  9. ReLU(inplace=True),
  10. )
  11. for param in self.parameters():
  12. param.requires_grad = False
  13. #这样for循环之前的参数都被冻结,其后的正常更新。
  14. self.classifier = nn.Linear()

当然同样不要忘了在定义优化器时过滤