换一个分类头不等于完成迁移学习。一次实验至少有三份需要分别核对的状态:哪些参数可以训练、哪些模块处于训练模式、图像怎样进入网络。
骨干网络解决什么问题
骨干把图像变成可供后续任务使用的特征。分类头、检测头和分割解码器的输出目标不同,不能只替换输出维数就默认其他设置完全通用。ResNet 的残差连接可写成 y = F(x) + x;尺寸不同还需要投影或其他对齐方式。它改善深层网络的优化,不保证任意任务中越深越好。ResNet 原论文
选型时先固定数据划分和训练预算,再比较精度、显存、延迟及失败样本。不把“某个骨干永远最强”或“某个样本数以下必须冻结”当成通用规则。
三种状态不要混为一谈
| 检查对象 | 它控制什么 | 容易遗漏什么 |
|---|---|---|
参数的 requires_grad |
是否为该参数记录梯度 | 不会自动切换 BatchNorm、Dropout 的行为 |
| 优化器参数组 | 哪些参数真正参与更新 | 可训练参数可能没被加入优化器,也可能被重复加入 |
train() / eval() |
模块的训练与评估行为 | 后续调用总模型的 train() 可能又切回骨干模式 |
训练分类头且固定骨干统计量时,应显式维护“骨干评估、分类头训练”的策略;如果希望域适应时更新统计量,也应记录这个选择,而不是偶然更新。不能用一次 eval() 代替整个训练循环的模式管理。PyTorch 迁移学习教程
一个参数清单检查
下面是独立的参数登记示例,不加载模型、不下载权重,也不是图像分类训练。它说明为什么只登记 layer1 到 layer4 可能漏掉输入端的卷积与归一化层。
from collections import Counter
trainable = {
"conv1.weight", "bn1.weight", "bn1.bias",
"layer1.weight", "layer4.weight", "fc.weight", "fc.bias",
}
groups = {
"backbone": sorted(name for name in trainable if not name.startswith("fc.")),
"head": sorted(name for name in trainable if name.startswith("fc.")),
}
counts = Counter(name for group in groups.values() for name in group)
assert set(counts) == trainable
assert all(count == 1 for count in counts.values())
incomplete = {name for name in trainable if name.startswith(("layer", "fc."))}
assert trainable - incomplete == {"conv1.weight", "bn1.weight", "bn1.bias"}
print("parameter coverage and uniqueness checks passed")
实际 PyTorch 项目中,应对 model.named_parameters() 与优化器里的参数对象做同样的覆盖、重复检查。逐层解冻后还要更新优化器参数组;仅把 requires_grad 改回 True,不保证原优化器包含这些参数。
预处理跟着具体权重走
不要给所有预训练模型统一套一组固定的尺寸和归一化常数。TorchVision 的具体权重对象提供对应推理变换;训练增强可以另行设计,但要记录与权重预处理约定的关系。TorchVision 权重与变换说明
我的实验记录至少应包含:数据划分标识、权重具体版本、输入尺寸、冻结范围、统计量更新策略、各参数组学习率和选模指标。验证集负责选方案,测试集留到方案确定后再评估。
来源与核验边界
本篇结合疏锦行计算机视觉专题《经典骨干网络》讲义第 29—34 页的迁移学习部分,重新组织为工程检查清单,并补充官方文档核对。上面的清单示例已运行;没有复现讲义中的 Flowers-102 训练成绩。视频口头内容尚未逐节核验,原课件不随笔记公开。
迁移学习笔记:冻结骨干之前,先检查参数、模式和预处理
本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。
评论交流
欢迎留下你的想法