pytorch中冻结层需先设requires_grad=false再创建optimizer,否则无效;常见做法是加载预训练模型后冻结backbone、替换fc层并确保其可训练,同时调用model.train()以维持bn和dropout行为。

冻结指定层的参数 requires_grad=False
PyTorch 中冻结某层权重,本质是把该层所有参数的 requires_grad 设为 False。这会让 autograd 忽略这些参数,不计算梯度、不更新——但模型前向仍正常运行。关键点在于:必须在创建 optimizer 之前设置,否则已注册进 optimizer 的参数不会因后续修改 requires_grad 而自动剔除。
- 常见错误:先定义
optimizer = torch.optim.Adam(model.parameters()),再冻结层 → 冻结无效,优化器仍会尝试更新这些参数(导致 RuntimeError 或意外训练) - 正确顺序:加载预训练模型 → 冻结需要的部分(如
model.layer1.requires_grad_(False))→ 再传入model.parameters()构造 optimizer - 注意
requires_grad_()是 in-place 方法,返回的是模块本身,可链式调用;而requires_grad = False需遍历.parameters()手动设,易漏
只对 classifier 层做 finetune 的典型写法
以 torchvision.models.resnet18(pretrained=True) 为例,迁移学习常只替换并训练最后的 fc 层。此时需确保 backbone 全部冻结,且新 fc 层参数默认 requires_grad=True(PyTorch 新建层自动启用梯度)。
- 直接写
model.fc = nn.Linear(512, num_classes)即可,无需手动设requires_grad - 但若保留原
fc并只微调它,得显式冻结其余部分:for param in model.parameters(): param.requires_grad = False,再model.fc.requires_grad_(True) - 别忘了调用
model.train()—— 否则 BatchNorm 层的 running stats 不更新,Dropout 也不生效,即使参数冻结了,行为也和推理时不同
按层名或模块路径选择性冻结(比如只冻住前3个 residual block)
ResNet 类模型有层级嵌套结构,model.layer1、model.layer2 是 nn.Sequential,里面包含多个 Bottleneck 或 BasicBlock。要精准控制,推荐用模块名匹配或递归遍历。
Python 3.14.2是Python编程语言在2025年12月5日发布的稳定版本,属于3.14系列的第二个维护更新。该版本包含了18项修复,重点解决了多进程、数据类及正则表达式等模块的回归问题,并修复了CVE-2025-12084等安全漏洞。此版本标志着自由线程模式(移除GIL)正式获得官方支持,是Python发展的重要里程碑。
- 简单粗暴法:
for name, param in model.named_parameters(): if "layer1" in name or "layer2" in name: param.requires_grad = False - 更稳健的做法:遍历子模块,用
isinstance(m, nn.Conv2d)或isinstance(m, nn.BatchNorm2d)判断类型再冻结,避免字符串匹配出错(比如层名含 "layer" 但不是 backbone) - 注意:BatchNorm 层的
weight和bias默认参与训练,冻结时通常一并处理;但有时想保留 BN 的统计更新(即track_running_stats=True),就得单独设model.layer1[0].bn1.training = True,否则model.eval()会关掉它
验证冻结是否生效:检查梯度和参数更新
光看代码不能确认冻结成功。最直接的办法是在一次 backward 后检查目标参数的 grad 是否为 None,或训练前后对比参数值是否变化。
- 训练前打印:
print(next(model.layer1[0].conv1.weight.grad))→ 应报 AttributeError(因为 grad 为 None) - 训练一轮后,用
torch.allclose(old_param, new_param)检查 backbone 参数是否未变 - 如果发现本该冻结的层参数在变,八成是 optimizer 初始化太早,或者用了
model.parameters()而没过滤 —— 改用filter(lambda p: p.requires_grad, model.parameters())构造 optimizer 更安全
冻结不是一劳永逸的操作,尤其在动态修改模型结构(比如插入 adapter)时,新增参数默认启用梯度,老参数仍冻结,但容易忽略新模块是否被 optimizer 覆盖。动手前务必 print 出 optimizer.param_groups[0]['params'] 看看里面到底包了哪些张量。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










