You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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]

问题分析与解决方案

核心定位

模型本身具备学习能力(统一标签后快速收敛),问题集中在数据预处理、标签与损失函数匹配、迁移学习策略三个核心环节。

具体修复方案

  1. 数据预处理不符合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
  1. 标签格式与损失函数不匹配
    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
  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)
    # 后续训练逻辑不变
  1. 训练循环效率低且无验证环节
    直接遍历__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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 03:02:14