冻结PyTorch预训练模型层需设param.requires_grad = False,而非仅删梯度;冻结后须调用model.train()确保BatchNorm更新;微调建议解冻最后1–2个block及分类头,并验证可训练参数数量非零。

怎么冻结 PyTorch 预训练模型的层
冻结权重就是让某些参数在训练时不更新,只更新你指定的层。关键不是“删掉梯度”,而是把 requires_grad 设为 False,否则即使没用到也会参与反向传播计算,浪费显存和时间。
常见错误是只改了模型结构(比如加了新分类头),但忘了关掉 backbone 的梯度——结果训了半天,resnet50 的权重纹丝不动,而你还在怀疑学习率设太小。
- 冻结整个 backbone:
for param in model.backbone.parameters(): param.requires_grad = False - 只冻结前几层(比如前 4 个
layer):for name, param in model.named_parameters(): if "layer1" in name or "layer2" in name or "layer3" in name: param.requires_grad = False - 冻结后务必调用
model.train(),否则BatchNorm层会卡在 eval 模式,导致微调失效
微调时该解冻哪些层
全解冻容易过拟合,尤其数据少;全冻结又学不到任务特异性特征。通常解冻最后 1–2 个 block + 分类头是最稳妥的起点。
比如用 torchvision.models.vgg16 做医学图像二分类:原始 classifier 是 1000 类输出,你替换成 2 类,这时至少要解冻整个 classifier;如果效果差,再逐步放开 features[28:](即最后几个卷积层)。
安全 Python 代码沙箱:在隔离的沙箱中执行任意 Python 代码,预装 requests、NumPy、pandas 库。返回 stdout、stderr。
立即学习“Python免费学习笔记(深入)”;
-
ResNet系列建议解冻layer4和fc层 -
ViT模型建议解冻最后 2–3 个encoder.layer+head - 解冻后记得检查
param.requires_grad是否真为True,别信名字——有些模型内部用了nn.Sequential包裹,层名不直观
为什么验证集准确率卡住不动
大概率是冻结/解冻逻辑没生效,或者优化器没看到可更新参数。PyTorch 的 torch.optim.SGD 或 Adam 只会优化传入的参数列表,如果你传的是 model.parameters(),而所有参数都 requires_grad=False,那优化器根本不会做任何事——连 loss 都不会下降。
- 训练前打印可训练参数数量:
sum(p.numel() for p in model.parameters() if p.requires_grad)
,确认不是 0 - 检查是否误用了
model.eval()—— 微调阶段必须保持model.train() - 小数据集上,
BatchNorm层的 running stats 更新不稳定,可考虑用model.apply(lambda m: setattr(m, 'track_running_stats', False) if isinstance(m, nn.BatchNorm2d) else None)临时关闭统计更新(慎用,仅调试)
加载预训练权重后怎么替换分类头
直接改 model.classifier 或 model.fc 就行,但要注意新层的初始化。PyTorch 默认对新 Linear 层用 kaiming_uniform_,一般够用;但如果原模型用了特殊初始化(比如 ViT 的 xavier_normal_),最好手动对齐。
- 替换
resnet50的 fc:model.fc = nn.Linear(model.fc.in_features, num_classes)
- 替换
vit_b_16的 head:model.heads.head = nn.Linear(model.heads.head.in_features, num_classes)
- 替换后不用重新加载预训练权重——只有未匹配的层会被随机初始化,其余照常加载
最容易被忽略的是:不同预训练模型对输入归一化要求不同。resnet50 期望 mean=[0.485,0.456,0.406],而 vit_b_16 用的是 mean=[0.5,0.5,0.5]。归一化错,特征提取就全偏了,再怎么调层也没用。

















