0、摘要
在深度学习中我们经常会使用到预训练进行微调,提高模型的泛化能力,加快收敛速度,节约训练时间。
一般来讲,预训练多种方式,常见的有对预训练层设置更小的学习率,或者固定预训练层,不进行权重更新,这里讨论的是后一种。
1、固定层训练
现在假设有模型:
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.layer1=conv_base # conv_base是由conv、BN等组成的卷积基
self.fc1=nn.Linear(512,100) # 分类器
self.fc2=nn.Linear(100, 10) # 分类器
def forward(self,x):
feat=self.layers(x)
out=self.fc1(feat)
out=self.fc2(out)
return out
其中layer1是预训练的特征提取,而fc1和fc2是我们需要训练的,所以我们希望固定layer1的参数,仅训练fc1和fc2的参数,实现的方法有两种(本质是一样的):
- 对固定的参数设定requires_grad=True
# 首先将固定层layer1的requires_grad属性置False
for name, p in model.layer1.named_parameters(): # 固定feat权重
p.requires_grad = False
# 在优化器中过滤到这些requires_grad参数
para=filter(lambda x:x.requires_grad is not False,model.parameters())
optimizer = torch.optim.SGD(para, lr=0.1)
- 直接在优化器中仅指定要更新的参数
para=[{"params":model.fc1.parameters()},
{"params":model.fc2.parameters()}]
optimizer = torch.optim.SGD(para, lr=0.1)
以上两种方式,通过优化器指定仅更新参数的方法,训练特定参数。
2、问题
通过以上方式训练后,layer1的参数是不变的,仅fc1和fc2的参数变化,可是当我用layer1的参数在数据集上验证时,发现指标变化了,如果layer1的参数是不变的,指标也应该和训练之前一样。
实际上问题在于train()和eval()模式上,我们知道BatchNorm是需要根据数据集样本的均值和方差进行运算的,在训练是BatchNorm会计算本次数据集样本的均值和方差进行运算,同时会保存本次均值和方差。验证和测试时会数据量比较小(极端为1张),此时的均值和方差就用训练时保存的均值和方差计算。
所以虽然我们固定了layer1参数训练,但是处于model.train()模式下的layer1,其包含的BatchNorm中的参数是变化的,当我们再次用layer1在数据集上测试时使用的就是更新后的mean和std了,造成指标发生了变化。
综上就是,虽然layer1非BatchNorm层的参数(比如conv等)没有变化,但是BatchNorm的参数变化了。
解决办法也很简单:
# 训练时:先将模型全设为train模型,再将固定曾layer1设为eval模式,这样保证了参数不更新,且BatchNorm层参数也不更新
model.train()
model.layer1.eval()
# 测试时:
model.eval()
1130

被折叠的 条评论
为什么被折叠?



