为何基于ResNet18的迁移训练耗时与从头训练一致?
问题:冻结ResNet18特征层后训练耗时未降低
我基于ResNet18构建自定义模型,移除原fc层,新增两个全连接层self.l1 = nn.Linear(512, 512)和self.l2 = nn.Linear(512, 256),并将ResNet18的特征层设置为requires_grad=False冻结,仅训练新增的两个层,但训练耗时和从头训练完整ResNet18完全相同。
核心原因
虽然你冻结了特征层的参数,但反向传播时仍然会计算特征层的梯度(只是不会更新参数),而且前向传播时ResNet18的特征提取部分依然会执行所有计算——这部分的计算量占了模型的绝大多数,所以整体耗时和训练完整模型几乎一致。
另外如果使用DataParallel,若在冻结参数之后才包装模型,可能会导致参数的梯度状态没有正确同步,但这不是耗时的主要原因。
优化方案
1. 前向传播时禁用特征层的梯度计算
在模型的forward方法中,用torch.no_grad()包裹特征层的计算,彻底跳过梯度计算环节:
def forward(self, x): with torch.no_grad(): h = self.features(x) h = h.squeeze() x = self.l1(h) x = F.relu(x) x = self.l2(x) return h, x
这样反向传播时不会为特征层的计算构建计算图,能节省内存和计算时间。
2. 提前预计算特征并保存
如果数据集固定,可以提前用预训练的ResNet18特征层把所有图片的特征提取出来保存,训练时直接加载这些特征训练新增的全连接层,彻底跳过ResNet18的特征提取计算:
- 编写脚本遍历数据集,用冻结的ResNet18提取特征,保存为numpy文件或torch张量
- 修改数据加载类,直接加载预计算的特征而非原始图片
- 训练时模型仅保留新增的两个全连接层即可
3. 修正参数冻结的时机(多GPU场景)
如果使用DataParallel,要先冻结参数再包装模型,避免多GPU环境下梯度状态不一致:
model = ResNetSimCLR(self.config["model"]['out_dim']) # 先冻结特征层参数 for param in model.features.parameters(): param.requires_grad = False # 再包装DataParallel if self.config['n_gpu'] > 1: device_n = len(eval(self.config['gpu_ids'])) model = torch.nn.DataParallel(model, device_ids=range(device_n)) model = model.to(self.device)
4. 验证参数冻结状态
训练前打印参数的requires_grad状态,确认特征层参数已被正确冻结:
for name, param in model.named_parameters(): print(f"{name}: requires_grad={param.requires_grad}")
内容的提问来源于stack exchange,提问作者ad150
相关产品推荐
相关产品推荐

