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

Unet医学图像语义分割训练中Dice Loss异常负值问题求助

医学图像语义分割中Dice Loss异常问题排查

我在使用Unet架构进行医学图像语义分割的监督训练(图像/掩码配对训练)时,遇到了Dice Loss数值异常的问题,训练日志如下:

Vanila_UNet
Epoch [0]
Mean loss on train: -140.31943819224836 
Mean DICE on train: 1.7142934219089918 
Mean DICE on validation: 1.8950854703170916
Epoch [1]
Mean loss on train: -154.01165542602538 
Mean DICE on train: 1.8439450739097656 
Mean DICE on validation: 1.923283325048502
Epoch [2]
Mean loss on train: -155.57704811096193 
Mean DICE on train: 1.8617926383475962 
Mean DICE on validation: 1.9318473889899364
Epoch [3]
Mean loss on train: -156.61962712605794 
Mean DICE on train: 1.8733720566917649 
Mean DICE on validation: 1.933697909810023
Epoch [4]
Mean loss on train: -157.22541224161785 
Mean DICE on train: 1.8788127825940564 
Mean DICE on validation: 1.9533974303968433

当前数据处理逻辑:图像通过数据增强完成归一化,掩码保持0-255范围时,模型具备一定预测能力;但如果在数据加载时将掩码除以255.,训练集和验证集的Mean DICE均变为0,Loss仍为负值(约-0.025),最终预测结果为空白图像。怀疑问题出在数据加载环节,相关代码如下:

class DukePeopleDataset(Dataset):

    def __init__(self, df, img_w, img_h):
        self.IMG_SIZE_W = img_w
        self.IMG_SIZE_H = img_h
        self.df = df
        self.in_channels = 3
        self.out_channels = 1
        
        self.transforms = self.define_transorms()
        
    def __len__(self):
        return len(self.df)

    def __getitem__(self, idx):
        image = cv2.resize(cv2.imread(self.df.iloc[idx, 0]), (self.IMG_SIZE_W, self.IMG_SIZE_H))
        mask = cv2.resize(cv2.imread(self.df.iloc[idx, 1],0), (self.IMG_SIZE_W, self.IMG_SIZE_H))
#         mask = mask/255.

        augmented = self.transforms(image = image,
                                      mask = mask)
        
        image = augmented['image']
        mask = mask.unsqueeze(0)
        
        return image, mask

    def get_dataframe(self):
        return self.df

    def define_transorms(self):
        transforms = A.Compose([
            A.HorizontalFlip(p=0.5),
            A.Normalize(p=1.0),
            ToTensorV2(),

        ])
        return transforms

问题原因分析及解决方案

1. 掩码未使用增强后的数据

代码中augmented = self.transforms(image=image, mask=mask)已经对掩码应用了增强变换,但后续直接使用了原始的mask变量,而非augmented['mask']。这会导致:

  • 掩码为0-255时,虽然未用到增强后的掩码,但模型仍能学到部分特征;
  • 掩码除以255后,原始掩码与增强后的掩码未同步,模型完全无法对齐训练信号,出现Dice为0的情况。

修复代码:

def __getitem__(self, idx):
    image = cv2.resize(cv2.imread(self.df.iloc[idx, 0]), (self.IMG_SIZE_W, self.IMG_SIZE_H))
    mask = cv2.resize(cv2.imread(self.df.iloc[idx, 1],0), (self.IMG_SIZE_W, self.IMG_SIZE_H)).astype(np.float32)
    mask = mask / 255.  # 启用这行,将掩码归一化到0-1范围

    augmented = self.transforms(image=image, mask=mask)
    
    image = augmented['image']
    mask = augmented['mask'].unsqueeze(0)  # 使用增强后的掩码
    
    return image, mask

2. Dice Loss计算逻辑错误

正常Dice系数范围是[0,1],Loss通常为1 - Dice系数,因此Loss应在[0,1]之间。当前出现负Loss和大于1的Dice值,说明存在以下可能:

  • 模型输出未经过sigmoid(二分类场景)转换为0-1的概率值;
  • Dice计算时未做数值裁剪,或分子分母顺序错误;
  • 未添加平滑项避免除零错误。

标准Dice Loss实现参考:

def dice_loss(pred, target):
    smooth = 1e-6
    pred = torch.sigmoid(pred)  # 二分类场景需先转概率
    intersection = (pred * target).sum()
    union = pred.sum() + target.sum()
    dice = (2. * intersection + smooth) / (union + smooth)
    return 1 - dice

3. 掩码数据类型问题

掩码除以255时,若未转为float类型,会触发整数除法导致掩码全为0。需确保掩码读取后转为float32类型(如上述修复代码所示)。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 03:01:00