pytorch冻结参数训练的坑

由于项目需要训练一个主干网络接多个分支的模型,所以先训练一个主干网络加第一个分支,再用另外的数据训练第二个分支,训练的过程中需要冻结主干网络部分,后面的分支训练过程也一样需要冻结主干网络部分。

冻结模型的方式

python 复制代码
for name, para in model.named_parameters():
      # 冻结backbone的权重
	if name.split(".")[0] == "backbone":
          para.requires_grad = False        # 或者用para.requires_grad_(False),一个是通过属性直接赋值,一个是通过函数赋值
    else:
          para.requires_grad = True
python 复制代码
# 可以打印需要更新梯度的参数
for name, value in model.named_parameters():
    print(name, "\t更新梯度:",value.requires_grad)

坑1:这样做并不能冻结batchnorm层的参数,所以需要在训练中手动冻结。如:

python 复制代码
def fix_bn(m):
    classname = m.__class__.__name__
    if classname.find('SyncBatchNorm') != -1 or classname.find('InstanceNorm2d') != -1 or classname.find('BatchNorm2d') != -1:          #SyncBatchNorm, InstanceNorm2d
        if m.num_features in [32, 64, 96, 128, 256, 384, 768, 192, 1152, 224]:      # 需要冻结的BN层的通道数
            m.eval()

def train():
	for epoch in range(max_epoch):
		model.train()
		if args.freeze:
			model.apply(fix_bn)
			model.backbone[5][0].block[0][1].eval()   # 假如需要冻结的BN层通道数和不需要冻结的BN层通道数一样,则需要单独写
		for batch_idx, (data, target) in enumerate(train_loader):
			...
			

坑2:用了冻结训练(freeze)就不要用EMA方式更新模型了,不然收敛缓慢不说,还会造成前面冻结的参数产生变化,可以从EMA的代码看出端倪:

python 复制代码
class EMA:
    def __init__(self, model, decay=0.9999):
        super().__init__()
        import copy
        self.decay = decay
        self.model = copy.deepcopy(model)

        self.model.eval()

    def update_fn(self, model, fn):
        with torch.no_grad():
            e_std = self.model.state_dict().values()
            #m_std = model.module.state_dict().values()   # multi-gpu
            m_std = model.state_dict().values()          # single-gpu
            for e, m in zip(e_std, m_std):
                e.copy_(fn(e, m))

    def update(self, model):
        self.update_fn(model, fn=lambda e, m: self.decay * e + (1. - self.decay) * m)

可以看出EMA的方式更新模型方式,大部分是结合上一个模型的参数的,即:

复制代码
model_update = decay*model(t-1) + (1-decay)*model(t)       # model(t-1) 代表上一次迭代模型的参数,model(t)代表当前迭代得到的模型参数

虽然冻结了backbone的参数,阻止了梯度在backbone中反向传播,但参数由于经过如上乘法及加法运算,由于精度原因,还是会发生微小变化,虽然训练次数增加,这个变化会扩大,从而达不到冻结训练的效果。而且从计算公式可以看出来,采用EMA的方式更新模型参数,参数会更新得很慢,会造成网络难以学习的"错觉"。我在这里困住了3天,有怀疑过是否是网络设计问题,是否是多GPU同步的问题,是否是参数设置,如学习率过小,权重衰减过大,或者dropout设置过大等等,最终一步一步排除定位到EMA的问题。

以这次的经验来看,EMA只适合在上一次训练得到模型的基础上,这一次加了额外的数据,需要在上一次的基础上做微调的情况。

相关推荐
拆房老料1 小时前
【第四部分:Agentic 智能体设计模式】16. 一个 Prompt 搞不定复杂任务?试试 Prompt Chaining
java·前端·人工智能·设计模式·prompt
运维行者_2 小时前
应用性能监控怎么选?从调用链到用户体验的五个能力维度
运维·服务器·数据库·人工智能·mysql·性能优化·应用性能监控
张张123y2 小时前
后端第十四课:Agent 记忆系统(让 Agent 记住上下文和用户,跨会话不健忘)
java·大数据·人工智能·python·agent·记忆
国科安芯6 小时前
四重证据,一条逻辑链:商业航天级微控制器MCU抗辐射验证方法学综述——以国科安芯AS32S601为例
人工智能·单片机·嵌入式硬件·mcu·安全·抗辐射
dk_tjy6 小时前
AI驱动的自动化攻击背景下:企业端点安全体系的技术架构与建设路径
网络·人工智能·安全·自动化·终端安全管理·数据防泄漏
happylifetree6 小时前
Python67:核心语法-函数进阶-参数类型(函数作为参数)
python
布吉岛的石头7 小时前
Java 程序员第 50 阶段2:预训练的目标函数:自回归语言建模本质
java·开发语言·人工智能·深度学习·回归·transformer
2601_965454237 小时前
用AI把开源项目功能清单做成MG动画介绍视频
人工智能·开源·音视频
风雅GW7 小时前
局域网访问为何少一半配置页:DSH 的安全边界设计复盘
人工智能
小羊没烦恼!7 小时前
铁道部新客票系统设计(二)
开发语言·前端·数据库·python·oracle·c#·php