医学图像分割中常用loss函数

18 篇文章 2 订阅
10 篇文章 0 订阅

一、交叉熵损失函数-cross entropy

  • 二分类交叉熵损失函数binary_crossentropy

                                                             -\frac{1}{N}\sum_{i=1}^{N}\left ( y_{i}log\left ( \hat{y}_{i} \right )+\left ( 1-y_{i} \right )log\left (1-\hat{y}_{i} \right )\right )

其中,N为像素点个数,y_{i}为输入实例x_{i} ​的真实类别,\hat{y}_{i}为预测输入实例 x_{i} 属于类别 1 的概率. 对所有样本的对数损失表示对每个样本的对数损失的平均值, 对于完美的分类器, 对数损失为 0。 

  • 多分类交叉熵损失函数categorical_crossentropy

                                                         cross entropy

                                                               -\frac{1}{N}\sum_{i=1}^{N}\left ( y_{i1}log\left ( \hat{y}_{i1} \right )+ y_{i2} log\left (\hat{y}_{i2} \right )+...+y_{im}log\left ( \hat{y}_{im} \right )\right ) =-\frac{1}{N}\sum_{i=1}^{N}\sum_{j=1}^{m}\left( y_{ij}log\left ( \hat{y}_{ij} \right )\right )

图像分割中最常用的损失函数是逐像素交叉熵损失。该损失函数分别检查每个像素,将类预测(深度方向的像素向量)与我们的热编码目标向量进行比较。

该损失函数分别检查每个像素,m为类别数(num_label),将类预测y_{i}=[y_{i1},y_{i2},...,y_{im}](深度方向的像素向量)与我们的热编码目标向量\hat{y}_{i}=[\hat{y}_{i1},\hat{y}_{i2},...,\hat{y}_{im}]进行比较。

由此可见,交叉熵的损失函数单独评估每个像素矢量的类预测,然后对所有像素求平均值,所以我们可以认为图像中的像素被平等的学习了。但是,医学图像中常出现类别不均衡(class imbalance)的问题,由此导致训练会被像素较多的类主导,对于较小的物体很难学习到其特征,从而降低网络的有效性。

pytorch代码实现

pytorch自带的nn.CrossEntropyLoss结合了nn.logSoftmax()和nn.NLLLoss()

class CrossEntropy(nn.Module):
    def __init__(self,ignore_label=-1,weight=None):
        super(CrossEntropy,self).__init__()
        self.ignore_label=ignore_label
        self.criterion=nn.CrossEntropyLoss(weight=weight,
                                           ignore_index=self.ignore_label)
 
    def forward(self, score,target):
        '''
        :param score: Tensor[bs,num_classes,256,256]
        :param target: Tensor[bs,256,256]
        :return:
        '''
        loss=self.criterion(score,target)
        return loss

二.带权重的交叉熵函数-Weighted cross entropy(WCE)

                                              -\frac{1}{N}\sum_{i=1}^{N}\left ( \omega_{1} y_{i1}log\left ( \hat{y}_{i1} \right )+ \omega_{2}y_{i2} log\left (\hat{y}_{i2} \right )+...+ \omega_{m}y_{im}log\left ( \hat{y}_{im} \right )\right ) =-\frac{1}{N}\sum_{i=1}^{N}\sum_{j=1}^{m}\left( \omega_{j}y_{ij}log\left ( \hat{y}_{ij} \right )\right )

其中,\omega=[\omega_{1},\omega_{2},...\omega_{m}]为对预测概率图中每个类别的权重,用于加权在预测图上占比例小的类别对loss函数的贡献

                                                                                               \omega_{j}=\frac{N-\sum_{i}^{N}\hat{y}_{ij}}{\sum_{i}^{N}\hat{y}_{ij}}

                                                                                                  求和符号中i=1到N

三.Dice Coefficient Loss

dice coefficient 源于二分类,本质上是衡量两个样本的重叠部分。该指标范围从0到1,其中“1”表示完整的重叠。 其计算公式为:

                                                                        Dice = \frac{2\left | A\cap B \right |}{\left | A \right |+\left | B \right | }

    其中\left | A\bigcap B \right |表示集合A、B 之间的共同元素的个数,|A|表示 A 中的元素的个数,B也用相似的表示方法。

    为了计算预测的分割图的 dice coefficient,将\left | A\bigcap B \right |近似为预测图每个类别score和target之间的点乘,并将结果函数中的元素相加。

      

      因为我们的目标是二进制的,因而可以有效地将预测中未在 target mask 中“激活”的所有像素清零。对于剩余的像素,主要是在惩罚低置信度预测; 该表达式的较高值(在分子中)会导致更好的Dice系数。

      为了量化计算 |A|和 |B| ,部分研究人员直接使用简单的相加, 也有一些做法是取平方求和。

 

       其中,在式子中 Dice系数的分子中有2,因为分母“重复计算” 了两组之间的共同元素。为了形成可以最小化的损失函数,我们将简单地使用1-Dice。这种损失函数被称为 soft dice loss,因为我们直接使用预测概率而不是使用阈值或将它们转换为二进制mask。      

      关于神经网络输出,分子涉及到我们的预测和 target mask 之间的共同激活,而分母将每个mask中的激活量分开考虑。实际上起到了利用 target mask 的大小来归一化损失的效果,使得 soft dice 损失不会难以从图像中具有较小空间表示的类中学习。

      soft dice loss 将每个类别分开考虑,然后平均得到最后结果。

定义如下:

                                                      L_{dice}=1-\frac{1}{m}\sum_{j=1}^{m}\frac{2\sum_{i=1}^{N}y_{ij}\hat{y}_{ij}}{\sum_{i=1}^{N}y_{ij}+\sum_{i=1}^{N}\hat{y}_{ij}}

                                              soft dice

pytorch代码实现:

class SoftDiceLoss(nn.Module):
    '''
    Soft_Dice = 2*|dot(A, B)| / (|dot(A, A)| + |dot(B, B)| + eps)
    eps is a small constant to avoid zero division,
    '''
 
    def __init__(self, weight=None):
        super(SoftDiceLoss, self).__init__()
        self.activation = nn.Softmax2d()
 
    def forward(self, y_preds, y_truths, eps=1e-8):
        '''
        :param y_preds: [bs,num_classes,768,1024]
        :param y_truths: [bs,num_calsses,768,1024]
        :param eps:
        :return:
        '''
        bs = y_preds.size(0)
        num_classes = y_preds.size(1)
        dices_bs = torch.zeros(bs,num_classes)
        for idx in range(bs):
            y_pred = y_preds[idx] #[num_classes,768,1024]
            y_truth = y_truths[idx] #[num_classes,768,1024]
            intersection = torch.sum(torch.mul(y_pred, y_truth),dim=(1,2)) + eps/2
            union = torch.sum(torch.mul(y_pred, y_pred), dim=(1, 2)) + torch.sum(torch.mul(y_truth, y_truth), dim=(1, 2)) + eps
 
            dices_sub = 2 * intersection / union
            dices_bs[idx] = dices_sub
 
        dices = torch.mean(dices_bs,dim=0)
        dice = torch.mean(dices)
        dice_loss = 1 - dice
        return dice_loss
 

值得注意的是,dice loss比较适用于样本极度不均的情况,一般的情况下,使用 dice loss 会对反向传播造成不利的影响,容易使训练变得不稳定。有时使用dice loss会使训练曲线有时不可信,而且dice loss好的模型并不一定在其他的评价标准上效果更好,例如mean surface distance 或者是Hausdorff surface distance。不可信的原因是梯度,对于softmax或者是log loss其梯度简化而言为p−t,t为目标值,p为预测值。而dice loss为\frac{2t^2}{(p+t)^2},如果p,t过小则会导致梯度变化剧烈,导致训练困难。

四.Generalized Dice loss 

在使用DICE loss时,对小目标是十分不利的,因为在只有前景和背景的情况下,小目标一旦有部分像素预测错误,那么就会导致Dice大幅度的变动,从而导致梯度变化剧烈,训练不稳定。

GDL(the generalized Dice loss)公式如下:

                                                                             L_{gd}=1-\frac{1}{m}\frac{2\sum_{j=1}^{m}\omega_{j}\sum_{i=1}^{N}y_{ij}\hat{y}_{ij}}{\sum_{j=1}^{m}\omega_{j}\sum_{i=1}^{N}\left ( y_{ij}+\hat{y}_{ij} \right )}

在dice loss基础上增加了\omega=[\omega_{1},\omega_{2},...,\omega_{m}]给每个类别加权,计算公式如下:

                                                                         \omega_{j}=\frac{1}{\left ( \sum_{i=1}^{N}y_{ij} \right )^2}

这样起到了平衡各类(包括背景类)目标区域对loss的贡献。

论文中的给出的分割效果:

但是在AnatomyNet中提到GDL面对极度不均衡的情况下,训练的稳定性仍然不能保证。 

代码实现:

def generalized_dice_coeff(y_true, y_pred):
    #y_true,y_pred shape=[num_label,H,W,C]
    num_label=y_pred.shape[0]
    w=K.zeros(shape=(num_label,))
    w=K.sum(y_true,axis=(1,2,3))
    w=1/(w**2+0.000001)
    # Compute gen dice coef:
    intersection_w = w*K.sum(y_true * y_pred, axis=[1,2,3])
    union_w = w*K.sum(y_true+y_pred, axis=[1,2,3])
    return K.mean( (2. * intersection_w + smooth) / (union_w + smooth), axis=0)    
 
def generalized_dice_loss(y_true, y_pred):
    return 1 - generalized_dice_coeff(y_true, y_pred)

五.IOU Loss

IOU类似于Dice,定义如下:

                                                                          IOU= \frac{\left | A \bigcap B \right | }{\left | A \right| +\left | B \right| - \left| A \bigcap B \right | }

pytorch代码实现:

class SoftDiceLoss(nn.Module):
    '''
    Soft_Dice = 2*|dot(A, B)| / (|dot(A, A)| + |dot(B, B)| + eps)
    eps is a small constant to avoid zero division,
    '''
 
    def __init__(self, weight=None):
        super(SoftDiceLoss, self).__init__()
        self.activation = nn.Softmax2d()
 
    def forward(self, y_preds, y_truths, eps=1e-8):
        '''
        :param y_preds: [bs,num_classes,768,1024]
        :param y_truths: [bs,num_calsses,768,1024]
        :param eps:
        :return:
        '''
        bs = y_preds.size(0)
        num_classes = y_preds.size(1)
        dices_bs = torch.zeros(bs,num_classes)
        for idx in range(bs):
            y_pred = y_preds[idx] #[num_classes,768,1024]
            y_truth = y_truths[idx] #[num_classes,768,1024]
            intersection = torch.sum(torch.mul(y_pred, y_truth),dim=(1,2)) + eps/2
            union = torch.sum(torch.mul(y_pred, y_pred), dim=(1, 2)) + torch.sum(torch.mul(y_truth, y_truth), dim=(1, 2)) + eps
 
            ious_sub = intersection / (union-intersection)
            ious_bs[idx] = ious_sub
 
        ious = torch.mean(ious_bs,dim=0)
        iou = torch.mean(ious)
        iou_loss = 1 - iou
        return iou_loss

IOU loss的缺点呢同DICE loss是相类似的,训练曲线可能并不可信,训练的过程也可能并不稳定,有时不如使用softmax loss等的曲线有直观性,通常而言softmax loss得到的loss下降曲线较为平滑。

 

 

 

 

 

  • 7
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值