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

PyTorch ResNet图像分割训练损失为零的问题排查

解决ResNet图像分割中损失为0但准确率虚高的问题

嘿,我仔细看了你的问题和代码,发现几个关键错误直接导致了“损失全程为0但准确率虚高到0.99”的诡异情况,咱们一步步来搞定它:

核心错误分析

1. 标签(y)的错误压缩

你代码里写了y = y[:, 0,0],这直接把原本形状为[Nbatch, H, W]的像素级二值掩码,硬改成了每个batch只取一个单点的标签。CrossEntropyLoss在这种维度不匹配的情况下,要么计算异常,要么如果这个单点刚好是模型总能“蒙对”的类别(比如占比极高的背景类),损失就会一直保持为0。

2. 准确率计算完全脱离模型预测

你计算预测结果时写的是_, preds = torch.max(X, 1)——这里的X是输入的RGB图像啊!不是模型输出的prediction!这就导致你统计的“准确率”完全和模型性能无关,只是基于图像通道的最大值随机生成的数值,所以才会出现离谱的0.99准确率。

3. 模型输出不符合分割任务要求

ResNet默认是为图像分类设计的,输出是[N, num_classes]的全局分类结果,但图像分割需要输出和输入同尺寸的[N, num_classes, H, W]特征图(每个像素对应类别概率)。如果你的模型没做适配(比如没加上采样/转卷积层),那模型输出和标签维度完全不匹配,损失计算必然出问题。

4. 损失计算的细节漏洞

你用了reduce=False得到每个像素的损失,但后续计算running_loss += loss.data[0]只取了第一个元素,再结合前面的标签错误,这个损失值根本不能反映真实训练状态。

具体修复步骤

第一步:修正标签与预测结果的处理

把这几行错误代码彻底替换:

# 删掉这行致命的标签压缩代码:y = y[:, 0,0]
# 从模型输出prediction中取类别,而不是输入图像X
_, preds = torch.max(prediction, 1)  # 得到[N, H, W]的像素级预测标签

第二步:把ResNet改成适配分割的模型

直接用原生ResNet做分割是行不通的,需要把它改成“编码器+解码器”的结构,比如:

import torchvision.models as models
from torch import nn

class ResNetSeg(nn.Module):
    def __init__(self, num_classes=2):
        super().__init__()
        # 加载预训练ResNet,去掉最后两层(全局池化+全连接)
        self.encoder = nn.Sequential(*list(models.resnet50(pretrained=True).children())[:-2])
        # 添加解码器,通过转卷积把特征图上采样到输入尺寸
        self.decoder = nn.Sequential(
            nn.ConvTranspose2d(2048, 1024, kernel_size=2, stride=2),
            nn.ReLU(inplace=True),
            nn.ConvTranspose2d(1024, 512, kernel_size=2, stride=2),
            nn.ReLU(inplace=True),
            nn.ConvTranspose2d(512, 256, kernel_size=2, stride=2),
            nn.ReLU(inplace=True),
            nn.ConvTranspose2d(256, 128, kernel_size=2, stride=2),
            nn.ReLU(inplace=True),
            nn.ConvTranspose2d(128, 64, kernel_size=2, stride=2),
            nn.ReLU(inplace=True),
            # 最后输出2个通道对应两类目标
            nn.Conv2d(64, num_classes, kernel_size=1)
        )
    
    def forward(self, x):
        x = self.encoder(x)
        x = self.decoder(x)
        return x

第三步:修正损失与准确率的计算逻辑

因为是像素级分割,要统计所有像素的正确数,而不是单个batch的单点值:

# 损失计算:CrossEntropyLoss会自动匹配[N, 2, H, W]的预测和[N, H, W]的标签
loss = criterion(prediction, y)
loss_mean = loss.mean()  # 取整个batch的平均损失

# 统计所有像素中预测正确的数量
running_corrects += torch.sum(preds == y.data)
# 总像素数 = 数据集样本数 × 图像高 × 图像宽
total_pixels = len(dataLoader[phase].dataset) * patch_size * patch_size
epoch_acc = running_corrects.double() / total_pixels

第四步:检查类别权重与损失函数设置

你的类别权重计算逻辑是对的(平衡类别不平衡),但要确保dataset["train"].numpixels是正确的两类像素数量统计。如果没有需要忽略的类别,建议去掉ignore_index参数,避免不必要的干扰。

额外调试建议

  • 训练前先打印prediction、y的形状,确保prediction是[N, 2, patch_size, patch_size],和y的[N, patch_size, patch_size]完全匹配。
  • 先跑1-2个小batch,手动检查损失值、预测结果是否合理,比如损失是否有变化,预测结果是否和标签有相关性。

内容的提问来源于stack exchange,提问作者Sarah

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.11 08:04:42