直接冻结指定层即可,无需修改模型结构;通过将目标层参数的requires_grad设为False实现,且必须在构建optimizer前完成,否则optimizer仍会包含已冻结参数。

直接冻结指定层即可,不需要改模型结构或重新定义网络。PyTorch 的 requires_grad 属性控制参数是否参与梯度计算,设为 False 就能冻结,且冻结后 optimizer 自动跳过这些参数。
如何用 requires_grad = False 冻结某一层
最常用也最稳妥的方式是遍历目标模块的 parameters() 并批量设置:
# 冻结 backbone.conv1 和 backbone.layer1(以 ResNet 为例)
for param in model.backbone.conv1.parameters():
param.requires_grad = False
for param in model.backbone.layer1.parameters():
param.requires_grad = False
注意:必须对 parameters() 迭代,不能只写 model.backbone.conv1.requires_grad = False——模块对象本身没有 requires_grad 属性,只有其内部的 Parameter 才有。
- 冻结后调用
model.train()或model.eval()不影响冻结状态 - 冻结操作应在构建
optimizer之前 完成,否则 optimizer 会把已冻结参数也纳入param_groups - 如果之后想解冻,只需重新设回
True,再调用optimizer.param_groups = optimizer.param_groups(或重建 optimizer)
冻结整个子模块(如全部 backbone)的快捷写法
用 named_parameters() 配合字符串匹配,适合批量冻结:
for name, param in model.named_parameters():
if "backbone" in name:
param.requires_grad = False
这种写法灵活但需小心命名冲突,比如 "backbone" 可能误伤 "head.backbone_proj"。更安全的做法是用 isinstance 判断模块类型:
- 冻结所有
nn.Conv2d层:if isinstance(module, nn.Conv2d): - 冻结所有 BatchNorm 层:
if isinstance(module, (nn.BatchNorm2d, nn.SyncBatchNorm)): - 冻结除最后一层外的所有线性层:
if isinstance(module, nn.Linear) and module is not model.classifier:
冻结后验证是否生效
别只信代码逻辑,运行时检查最可靠:
# 查看 backbone.conv1 是否真被冻结 print([p.requires_grad for p in model.backbone.conv1.parameters()]) # 应全为 False <h1>检查 optimizer 是否真的没包含这些参数</h1><p>print(len(optimizer.param_groups[0]["params"])) # 对比冻结前后数量变化 </p>
常见失效场景:
- 在创建
optimizer之后 才冻结参数 → optimizer 仍持有旧引用,梯度更新会静默失败 - 用了
model.module(DDP 包装后)但冻结的是原始model→ 实际要冻结model.module.backbone - 冻结了参数但忘了关掉对应层的
training状态 → BN 层仍会更新 running_mean/var,需手动model.backbone.eval()
真正容易被忽略的是:冻结参数不等于关闭该层的前向行为,BN 和 Dropout 在 train() 模式下依然活跃,该关的 eval() 得手动关,不然微调效果可能反常。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











