pytorch模型冻结及BN层存在的坑

一、pytorch模型冻结

冻结模型常用的方法有两种,一种是将需要冻结的参数的梯度设置为False,另一种是只将不动结的参数传给优化器。
方法一:将需要冻结的参数的梯度设置为False。

model = ...
# 冻结模型的某些层
for name,param in model.named_parameters():
    if 'conv' in name:  # 假设我们冻结所有卷积层
        param.requires_grad = False

方法二:只将不动结模型的参数传递给优化器

冻结模型中除了fc1层之外的所有层
optimizer = optim.SGD(model.fc1.parameters(), lr=0.01)

下面我们来举一个简单的例子来说明。

import torch
import torch.nn as nn
import torch.optim as optim

class Model(nn.Module):
    def __init__(self):
        super(Model,self).__init__()
        self.fc1 = nn.Linear(5,3)
        self.fc2 = nn.Linear(3,2)

    def forward(self,x):
        return self.fc2(self.fc1(x))

model = Model()

print(model.fc1.weight)

optim = optim.SGD(model.parameters(),lr = 0.01)
loss_fn = nn.CrossEntropyLoss()

x = torch.randn((1,5))
label = torch.randn((1,2))

for epoch in range(10):
    output = model(x)
    loss = loss_fn(output,label)
    optim.zero_grad()
    loss.backward()
    optim.step()

print(model.fc1.weight)

在这里插入图片描述
首先,我们定义一个简单的模型,然后对其进行训练,然后我们分别打印fc1层在训练前和训练后的权重。可以看到,在没有冻结模型的情况下,模型权重在训练过程中会改变。

接着我们使用方法一去冻结fc1的网络参数,不冻结fc2的网络参数,去看一下最终的网络参数变化情况。

import torch
import torch.nn as nn
import torch.optim as optim

class Model(nn.Module):
    def __init__(self):
        super(Model,self).__init__()
        self.fc1 = nn.Linear(5,3)
        self.fc2 = nn.Linear(3,2)

    def forward(self,x):
        return self.fc2(self.fc1(x))

model = Model()


for name,param in model.named_parameters():
    if 'fc1' in name:
        param.requires_grad = False
print(model.fc1.weight,model.fc2.weight)

optim = optim.SGD(model.parameters(),lr = 0.01)
loss_fn = nn.CrossEntropyLoss()

x = torch.randn((1,5))
label = torch.randn((1,2))

for epoch in range(10):
    output = model(x)
    loss = loss_fn(output,label)
    optim.zero_grad()
    loss.backward()
    optim.step()

print(model.fc1.weight,model.fc2.weight)

在这里插入图片描述

可以看到,此时fc1的requires_grad被设置为False后,模型的参数在训练前后没有改变;而fc2层的requires_grad为True,所以其训练前后的网络参数发生了改变。

注意:使用方法一时,一定要注意不要拼写错误,requires_grad拼写错误的话也不会报错,但是如果拼写错误的话就不能正常把梯度设置为False了,所以有的时候明明把requires_grad设置为False了却还是没有冻结住模型,去看看是不是单词拼写错了,我有好几次都是拼写错误导致模型没有冻结住但是没有及时发现。

二、pytorch中BN层存在的坑

在训练过程中,有时候在导入预训练模型,在只进行前向传播的情况下网络每次的输出不同,按照道理说只进行前向传播没有进行参数更新,网络参数应该不变,每次的输出应该相同,为什么会出现这个问题呢?
这是因为BN层搞的鬼,BN层的参数(均值和方差)是在前向传播过程中更新的。在训练模式下,BN层会计算输入数据的批次均值和方差,并使用这些统计量来归一化输入数据。同时,BN层还会更新其运行均值和运行方差的估计值,这些估计值在模型评估时使用。在PyTorch中,这个过程是自动进行的,只要模型处于训练模式。在评估模式下,BN层会使用之前训练过程中更新的运行均值和方差来归一化数据,而不是使用当前批次的统计量。这是BN层在训练和评估模式下的一个关键区别,也是为什么在使用BN层时需要正确地在训练和评估模式之间切换的原因。因此,模型在做推理的过程中,如果模型中有BN层,要把模型设置成评估模式,即model.eval(),否则在推理的过程中BN层的参数会变化。

如果模型在训练过程中使用了BN层,通常建议保持BN层的统计信息更新,以获得最佳的模型性能。只有在某些特殊情况下,比如模型微调或者特定层的性能问题,才可能需要冻结BN层的参数。那么如何冻结住BN层呢?同样的道理,只需要把BN层的requries_grad设置为False就可以了。

  • 12
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
PyTorch中,冻结Batch Normalization(BN)层的常见做法是将其设置为eval模式,并将其track_running_stats属性设置为False。这样做可以防止BN层参与训练过程中的梯度更新。以下是几种常见的冻结BN层的方法: 方法一: 在加载预训练模型时,需要使用以下代码来冻结BN层: ```python def freeze_bn(m): classname = m.__class__.__name__ if classname.find('BatchNorm') != -1: m.eval() model.apply(freeze_bn) ``` 这段代码会将模型中所有的BN层设置为eval模式,从而冻结它们的参数。 方法二: 如果在自己定义的模型中需要冻结特征提取层(pretrain layer)的BN层,可以按如下方式修改train函数: ```python def train(self, mode=True): super(fintuneNet, self).train(mode) if self.args.freeze_bn and mode==True: self.branch_cnn.apply(self.fix_bn) return self def fix_bn(self, m): classname = m.__class__.__name__ if classname.find('BatchNorm') != -1: m.eval() m.track_running_stats = False for name, p in m.named_parameters(): p.requires_grad = False ``` 这段代码会将模型中特征提取层的BN层设置为eval模式,并将其track_running_stats属性设置为False,同时将参数的requires_grad属性设置为False,从而冻结这些层的参数。 另外,可以阅读一篇名为"Pytorch BN(BatchNormal)计算过程与源码分析和train与eval的区别"的文章,该文章对PyTorchBN层的计算过程以及train和eval模式的区别进行了详细分析。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值