仅设 requires_grad=False 不够,还需确保优化器只接收 requires_grad=True 的参数,否则冻结层仍可能被更新。

冻结某几层时 requires_grad 设为 False 就够了吗?
不够。设完 requires_grad=False 后,必须确保这些参数不被优化器更新——否则梯度虽不计算,但优化器仍可能用旧梯度或零梯度覆盖参数值。
- 务必在构造
torch.optim.Adam或其他优化器前,筛选出requires_grad=True的参数传入,例如:optimizer = torch.optim.Adam(filter(lambda p: p.requires_grad, model.parameters()), lr=1e-4) - 如果已建好优化器,改
requires_grad后需重新构建,或手动清空其param_groups并重填(不推荐) - 验证是否生效:训练中打印
sum(p.grad is not None for p in model.parameters() if p.requires_grad),应大于 0;而冻结层的p.grad应始终为None
想只微调最后两层,但模型是 nn.Sequential 怎么操作?
不能直接按索引切片赋值,因为 Sequential 的子模块是只读属性;得逐层遍历并判断位置。
- 用
list(model.children())转成列表,再取后两个元素,对它们调用requires_grad_(True) - 其余层统一设为
False,注意要递归进每个子模块(比如里面还有Conv2d+ReLU组合):for param in module.parameters(): param.requires_grad = False - 常见坑:只设了
model[-1].requires_grad_(True),但忘了model[-2]也是要微调的,结果最后一层有梯度、倒数第二层没梯度,loss 不下降
model.eval() 和冻结参数有什么关系?
完全无关。前者只影响 Dropout、BatchNorm 等层的行为,后者才真正阻止梯度传播和参数更新。
- 冻结层时,仍应调用
model.train()(除非你明确想关掉所有 BN/DP) - 若误用
model.eval(),BN 层会用运行统计量而非 batch 统计量,导致微调阶段特征分布偏移,尤其小 batch 下 loss 波动大 - 典型错误现象:
loss初期骤降后卡住,验证集 acc 上不去——先检查是否阴差阳错用了eval()
用 torch.no_grad() 替代冻结参数行不行?
不行。它只是临时禁用梯度计算,不改变参数本身的 requires_grad 状态,也无法防止优化器更新。
-
torch.no_grad()是上下文管理器,只作用于代码块内前向过程;一旦退出,后续反向仍会计算梯度(如果参数本就可训) - 它适合推理或特征提取,不适合微调场景——因为你需要部分层的梯度来更新对应参数
- 性能上无优势:冻结参数后,自动求导引擎本来就不会为这些张量构建计算图,开销已最小化
实际冻结最稳的方式,是写个函数遍历命名参数,按层名匹配(比如 "layer4"、"classifier"),再统一开关 requires_grad,比靠索引或结构假设更可靠。名字容易写错,建议先 print(list(model.named_parameters())[0]) 看一眼真实键名。











