
怎么改 ResNet 的 fc 层做迁移学习
直接替换 model.fc 是最常用也最安全的做法,PyTorch 官方预训练模型(如 resnet18、resnet50)都把最后的全连接层单独暴露为 fc 属性,方便修改。别碰 model.classifier 或其他名字——ResNet 没这属性,写了会报 AttributeError: 'ResNet' object has no attribute 'classifier'。
实操建议:
- 先用
torchvision.models.resnet18(pretrained=True)加载模型,再立刻设model.fc = nn.Linear(512, num_classes)(ResNet18 的fc输入是 512;ResNet50 是 2048,别写错) - 如果要冻结前面所有层,加一句
for param in model.parameters(): param.requires_grad = False,但记得在替换fc后再执行,否则新fc层参数也会被冻结 - 别用
model.children()遍历改最后一层——ResNet 结构里fc不在 children 列表末尾,容易误删或漏改
为什么不能直接改 model.layer4[1].conv2.out_channels
有人想“从底层改起”,比如调大某卷积层通道数来适配新任务,这是典型误解。ResNet 的特征维度由整个前向流决定:layer4 输出尺寸固定为 7×7×2048(ResNet50),硬改中间卷积的 out_channels 会导致后续层输入/输出不匹配,forward 时直接报 size mismatch 错误。
真正需要调整的只有分类头:它唯一作用就是把固定尺寸的全局平均池化输出(2048 维向量)映射到你的类别数。其他层保持原样,既保证特征提取能力,又避免破坏预训练权重的语义对齐。
替换 fc 后要不要重新初始化权重
要,而且必须显式初始化。PyTorch 新建的 nn.Linear 默认用 Kaiming 初始化,但和原始 ResNet 的初始化风格不一致;更关键的是,如果你用了 pretrained=True,旧 fc 权重还在内存里,不替换就会残留干扰。
实操建议:
- 替换后立即检查:打印
model.fc.weight,确认不是全零或 nan;若发现仍是旧权重,说明赋值没生效(常见于用model = torchvision.models.resnet18(...)后又做了model = torch.nn.DataParallel(model)——此时需改model.module.fc) - 想控制初始化方式?加一句
nn.init.normal_(model.fc.weight, std=0.01)或nn.init.xavier_uniform_(model.fc.weight) - 别依赖“自动重置”——PyTorch 不会因为你替换了属性就帮你清旧参数
验证修改是否生效的最快方法
跑一次 dummy forward,看输出 shape 和梯度是否符合预期。别等训练完才发现 fc 没生效。
示例代码片段:
x = torch.randn(2, 3, 224, 224) out = model(x) print(out.shape) # 应该是 torch.Size([2, num_classes]) print(model.fc.weight.grad) # 应该是 None(还没 backward) out.sum().backward() print(model.fc.weight.grad is not None) # 应该是 True
如果 out.shape[1] 还是 1000,说明 fc 没换成功;如果 grad 始终为 None,大概率是前面层被冻结时误锁了 fc,或者模型被封装在 DataParallel/DistributedDataParallel 里没访问对子模块。
实际项目里,最容易被忽略的是多卡训练下的属性访问路径,以及忘记在替换后手动初始化新层——这两处出问题,模型看起来“能跑”,但根本没学新任务。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











