ResNet18迁移学习无进展排查:二分类任务精度仅40-65%
问题:ResNet18迁移学习二分类任务准确率偏低(40-65%)
我用PyTorch搭建二分类网络,两个文件夹ai_images和artist_images分别存放200-250张500像素左右的图片,计划用ResNet18做迁移学习,但多轮训练后准确率仅维持在40-65%。
做了两组测试验证模型学习能力:
- 将所有标签统一改为
[0,1](原AI图片标签为[1,0],艺术家图片标签为[0,1]),ResNet训练20个数据点就达到100%准确率; - 恢复原标签后,把所有AI图片的RGB值除以10000,准确率又回落至40-65%。
目前不确定是ResNet18不适用该任务、代码实现有误,还是数据加载环节出了问题。
MAIN.py
model = models.resnet18(pretrained=True) # add a fc network to the end of resnet so it only has 2 outputs model_ = Network() model.fc = model_ print(model) #loads training data train_data = training_data() optimizer = optim.Adam(model.parameters(), lr=0.001) loss_fn = torch.nn.CrossEntropyLoss() test_size = 25 for epoch in range(20): print(f'starting epoch {epoch+1}') correct = 0 for i in range(train_data.__len__()): image,label = train_data.__getitem__(i) image = image.unsqueeze(0) label = label.unsqueeze(0) output = model(image) loss = loss_fn(output,label) if torch.argmax(output) == torch.argmax(label): correct += 1 if i%test_size == test_size-1: print(f'{str(correct/test_size*100)[:5]}%', str(loss.item())[:5]) correct = 0 optimizer.zero_grad() loss.backward() optimizer.step() print('Finished Training') torch.save(model.state_dict(), 'model.pt')
detector_network.py
class Network(nn.Module): def __init__(self): super(Network, self).__init__() self.fc1 = nn.Linear(512, 120) self.fc2 = nn.Linear(120, 2) def forward(self, x): out = F.relu(self.fc1(x)) out = self.fc2(out) return out
data_loader.py
class training_data(torch.utils.data.Dataset): ##Characterizes a dataset for PyTorch def __init__(self): ##Initialization print('Getting Data') self.data = [] ai_images_filenames = glob.glob('ai_images/*.png') artist_images_filenames = glob.glob('artist_images/*.png') for img in ai_images_filenames: img = self.formatImg(img) label = torch.tensor([1,0]).type(torch.float) self.data.append([img, label]) for img in artist_images_filenames: img = self.formatImg(img) label = torch.tensor([0,1]).type(torch.float) self.data.append([img, label]) random.shuffle(self.data) print(f'{len(self.data)} samples') def formatImg(self,img_path): img = Image.open(img_path) # converts img to np and removes transparency values img = asarray(img, dtype=np.float16) img = img[:,:,:3] # converts np to torch and makes the channel value the first one img = torch.from_numpy(img).type(torch.float) img = np.swapaxes(img,0,2) # resizes and crops for resnet img = fn.resize(img,size=[224]) img = fn.center_crop(img,output_size=[224,224]) return img def __len__(self): ##Denotes the total number of samples return len(self.data) def __getitem__(self, index): ##Selects one sample of data return self.data[index]
问题分析与解决方案
核心定位
模型本身具备学习能力(统一标签后快速收敛),问题集中在数据预处理、标签与损失函数匹配、迁移学习策略三个核心环节。
具体修复方案
- 数据预处理不符合ResNet输入规范
ResNet预训练模型要求输入是经过ImageNet均值标准差归一化的张量,你的代码直接使用原始像素值(甚至做了除以10000的错误缩放),导致输入分布与预训练模型的训练分布完全偏离,这是准确率低的核心原因。
修改data_loader.py的formatImg方法:
def formatImg(self,img_path): img = Image.open(img_path).convert('RGB') # 强制转为RGB,避免透明通道干扰 # 像素值缩放到[0,1] img = torch.tensor(np.array(img), dtype=torch.float32) / 255.0 # 调整通道顺序为(C, H, W) img = img.permute(2, 0, 1) # 用ImageNet均值标准差归一化 mean = torch.tensor([0.485, 0.456, 0.406]).view(3, 1, 1) std = torch.tensor([0.229, 0.224, 0.225]).view(3, 1, 1) img = (img - mean) / std # 尺寸调整与裁剪 img = fn.resize(img, size=[224]) img = fn.center_crop(img, output_size=[224, 224]) return img
- 标签格式与损失函数不匹配
CrossEntropyLoss要求标签为类别索引(0或1),而非one-hot向量。你传入的[1,0]/[0,1]格式是错误的,统一标签为[0,1]时,torch.argmax(label)得到的都是1,相当于所有样本属于同一类别,模型自然能快速收敛,这是无效训练。
- 修改
data_loader.py的标签生成逻辑:
# AI图片标签设为0 label = torch.tensor(0, dtype=torch.long) # 艺术家图片标签设为1 label = torch.tensor(1, dtype=torch.long)
- 训练时移除标签的
unsqueeze(0)操作,准确率判断改为:
if torch.argmax(output) == label: correct += 1
- 迁移学习策略不合理
直接让预训练ResNet的所有参数参与训练,且学习率设为0.001,容易破坏预训练的特征。正确做法是先冻结主干网络,只训练新增的全连接层,再解冻部分主干层微调。
- 初始化模型时冻结主干:
model = models.resnet18(pretrained=True) # 冻结所有主干参数 for param in model.parameters(): param.requires_grad = False # 替换全连接层并确保其参数可训练 model.fc = Network() for param in model.fc.parameters(): param.requires_grad = True
- 训练5轮后解冻部分主干层并降低学习率:
for epoch in range(20): if epoch == 5: # 解冻最后一个残差块 for param in model.layer4.parameters(): param.requires_grad = True # 更新优化器,降低学习率避免破坏预训练特征 optimizer = optim.Adam(filter(lambda p: p.requires_grad, model.parameters()), lr=0.0001) # 后续训练逻辑不变
- 训练循环效率低且无验证环节
直接遍历__getitem__未使用DataLoader批量处理,训练效率极低且无法利用批量归一化优化;同时缺少验证集,无法判断模型是否过拟合。
- 使用
DataLoader加载数据:
train_loader = torch.utils.data.DataLoader(train_data, batch_size=32, shuffle=True)
- 修改为批量训练循环:
for epoch in range(20): print(f'starting epoch {epoch+1}') correct = 0 total = 0 for images, labels in train_loader: outputs = model(images) loss = loss_fn(outputs, labels) optimizer.zero_grad() loss.backward() optimizer.step() # 批量计算准确率 _, predicted = torch.max(outputs.data, 1) total += labels.size(0) correct += (predicted == labels).sum().item() print(f'Epoch {epoch+1}, Accuracy: {100 * correct / total:.2f}%')
额外建议
- 检查数据均衡性:若两类图片数量差距大,可做过采样或欠采样;
- 增加数据增强:在
formatImg中加入随机翻转、裁剪等操作,提升模型泛化能力; - 动态调整学习率:使用
ReduceLROnPlateau调度器,根据验证集损失自动调整学习率。
内容的提问来源于stack exchange,提问作者Wung8
相关产品推荐
相关产品推荐

