多类别语义分割中常见 Loss 函数总结

1 综述

学习并整理了一下语义分割的常见Loss,希望能为大家训练语义分割网络的时候提供一些关于Loss方面的知识,之后会不定期更新;【tensorflow实现】

看到一篇2020年论文《A survey of loss functions for semantic segmentation》,文章对目前常见语义分割中Loss functions进行了总结,大家有兴趣可以看看;

论文地址:A survey of loss functions for semantic segmentation
code地址:Semantic-Segmentation-Loss-Functions

1.1 Dice with weight

(1)Dice Loss 对正负样本严重不平衡的场景有着不错的性能,训练过程中更侧重对前景区域的挖掘;
(2)Dice Loss是一种区域相关的loss,像素点的loss以及梯度值不仅和该点的label以及预测值相关,和其他点的label以及预测值也相关;
(3)但训练loss容易不稳定,尤其是小目标的情况下;

Dice with weight代码如下:

def Dice_weight_loss(Y_pred, Y_gt, weight_loss):
    """
    multi label dice loss with weighted
    WDL=1-2*(sum(w*sum(r&p))/sum((w*sum(r+p)))),w=array of shape (C,)
    :param Y_pred: [None, self.image_depth, self.image_height, self.image_width,
                                                       self.numclass],Y_pred is softmax result
    :param Y_gt:[None, self.image_depth, self.image_height, self.image_width,
                                                       self.numclass],Y_gt is one hot result
    :param weight_loss: numpy array of shape (C,) where C is the number of classes
    :return:
    """
    weight_loss = np.array(weight_loss)
    smooth = 1.e-5
    smooth_tf = tf.constant(smooth, tf.float32)
    Y_pred = tf.cast(Y_pred, tf.float32)
    Y_gt = tf.cast(Y_gt, tf.float32)
    # Compute gen dice coef:
    numerator = Y_gt * Y_pred
    numerator = tf.reduce_sum(numerator, axis=(1, 2, 3))
    denominator = Y_gt + Y_pred
    denominator = tf.reduce_sum(denominator, axis=(1, 2, 3))
    gen_dice_coef = tf.reduce_mean(2. * (numerator + smooth_tf) / (denominator + smooth_tf), axis=0)
    loss = -tf.reduce_mean(weight_loss * gen_dice_coef)
    return loss
    

1.2 Tversky loss

论文地址为:Tversky loss function for image segmentation using 3D fully convolutional deep networks

实际上Dice Loss只是Tversky loss的一种特殊形式而已,我们先来看一下Tversky系数的定义,它是Dice系数和Jaccard系数的广义系数。
当 alpha 和 beta 均为0.5的时候,这个公式就是Dice系数,当 alpha 和 beta 均为1的时候,这个公式就是Jaccard系数。

def tversky_loss(Y_pred, Y_gt, beta, weight_loss):
    """
    multi label tversky with weighted
    Tversky loss (TL) is a generalization of Dice loss. TL adds a weight to FP and FN.
    define:TL(p,p')=(p&p')/(p&p'+b*((1-p)&p')+(1-b)*(p&(1-p')))
    :param Y_pred: [None, self.image_depth, self.image_height, self.image_width,
                                                       self.numclass],Y_pred is softmax result
    :param Y_gt:[None, self.image_depth, self.image_height, self.image_width,
                                                       self.numclass],Y_gt is one hot result
    :param beta:beta=1/2,just Dice loss,beta must(0,1)
    :return:
    """
    weight_loss = np.array(weight_loss)
    smooth = 1.e-5
    smooth_tf = tf.constant(smooth, tf.float32)
    Y_pred = tf.cast(Y_pred, tf.float32)
    Y_gt = tf.cast(Y_gt, tf.float32)
    p0 = Y_pred
    p1 = 1 - Y_pred
    g0 = Y_gt
    g1 = 1 - Y_gt
    # Compute gen dice coef:
    numerator = p0 * g0
    numerator = tf.reduce_sum(numerator, axis=(1, 2, 3))
    denominator = tf.reduce_sum(beta * p0 * g1, axis=(1, 2, 3)) + tf.reduce_sum((1 - beta) * p1 * g0, axis=(1, 2, 3)) + numerator
    gen_dice_coef = tf.reduce_mean((numerator + smooth_tf) / (denominator + smooth_tf), axis=0)
    loss = -tf.reduce_mean(weight_loss * gen_dice_coef)
    return loss
    

1.3 Focal loss

论文地址:Focal Loss for Dense Object Detection

何凯明团队在RetinaNet论文中引入了Focal Loss来解决难易样本数量不平衡,gamma用来控制易分样本和难分样本的权重,alpha用来控制正负样本的权重。

Focal loss模块代码如下:

def Focal_loss(Y_pred, Y_gt, gamma, alpha):
    """
     focal_loss between an output and a target
    :param Y_pred: A tensor resulting from a softmax(-1,z,h,w,numclass)
    :param Y_gt: A tensor of the same shape as `y_pred`
    :param alpha: Sample category weight,which is shape (C,) where C is the number of classes
    :param gamma: Difficult sample weight
    :return:
    """
    weight_loss = np.array(alpha)
    epsilon = 1.e-5
    # Scale predictions so that the class probas of each sample sum to 1
    output = Y_pred / tf.reduce_sum(Y_pred, axis=- 1, keepdims=True)
    # Clip the prediction value to prevent NaN's and Inf's
    output = tf.clip_by_value(output, epsilon, 1. - epsilon)
    # Calculate Cross Entropy
    cross_entropy = -Y_gt * tf.log(output)
    # Calculate Focal Loss
    loss = tf.pow(1 - output, gamma) * cross_entropy
    loss = tf.reduce_sum(loss, axis=(1, 2, 3))
    loss = tf.reduce_mean(loss, axis=0)
    loss = tf.reduce_mean(weight_loss * loss)
    return loss

1.4 Generalized dice with weight

论文地址:Generalized Overlap Measures for Evaluation and Validation in Medical Image Analysis

因 Dice Loss 对小目标的预测是十分不利的,因为一旦小目标有部分像素预测错误,就可能会引起Dice系数大幅度波动,导致梯度变化大训练不稳定。另外从上面的代码实现可以发现,Dice Loss针对的是某一个特定类别的分割的损失。当类似于病灶分割有多个场景的时候一般都会使用多个Dice Loss,所以Generalized Dice loss就是将多个类别的Dice Loss进行整合,使用一个指标作为分割结果的量化指标。

Generalized dice with weight代码如下:

def Generalized_dice_loss_w(Y_pred, Y_gt):
    """
    Generalized Dice Loss with class weights
    GDL=1-2*(sum(w*sum(r*p))/sum((w*sum(r+p)))),w=1/sum(r)*sum(r)
    rln为类别l在第n个像素的标准值(GT),而pln为相应的预测概率值。此处最关键的是wl,为每个类别的权重
    :param Y_gt:[None, self.image_depth, self.image_height, self.image_width,
                                                       self.numclass],Y_gt is one hot result
    :param Y_pred:[None, self.image_depth, self.image_height, self.image_width,
                                                       self.numclass],Y_pred is softmax result
    :return:
    """
    smooth = 1.e-5
    smooth_tf = tf.constant(smooth, tf.float32)
    Y_pred = tf.cast(Y_pred, tf.float32)
    Y_gt = tf.cast(Y_gt, tf.float32)
    # Compute weights: "the contribution of each label is corrected by the inverse of its volume"
    weight_loss = tf.reduce_sum(Y_gt, axis=(0, 1, 2, 3))
    weight_loss = 1 / (tf.pow(weight_loss, 2) + smooth_tf)
    # Compute gen dice coef:
    numerator = Y_gt * Y_pred
    numerator = weight_loss * tf.reduce_sum(numerator, axis=(0, 1, 2, 3))
    numerator = tf.reduce_sum(numerator)
    denominator = Y_gt + Y_pred
    denominator = weight_loss * tf.reduce_sum(denominator, axis=(0, 1, 2, 3))
    denominator = tf.reduce_sum(denominator)
    loss = -2 * (numerator + smooth_tf) / (denominator + smooth_tf)
    return loss

1.5 Cross Entropy with weight

Cross entropy with weight代码如下:

def weighted_categorical_crossentropy(Y_pred, Y_gt, weights):
    """
    weighted_categorical_crossentropy between an output and a target
    loss=-weight*y*log(y')
    :param Y_pred:A tensor resulting from a softmax
    :param Y_gt:A tensor of the same shape as `output`
    :param weights:numpy array of shape (C,) where C is the number of classes
    :return:categorical_crossentropy loss
    Usage:
    weights = np.array([0.5,2,10]) # Class one at 0.5, class 2 twice the normal weights, class 3 10x.
    """
    weights = np.array(weights)
    epsilon = 1.e-5
    # scale preds so that the class probas of each sample sum to 1
    output = Y_pred / tf.reduce_sum(Y_pred, axis=- 1, keep_dims=True)
    # manual computation of crossentropy
    output = tf.clip_by_value(output, epsilon, 1. - epsilon)
    loss = - Y_gt * tf.log(output)
    loss = tf.reduce_sum(loss, axis=(1, 2, 3))
    loss = tf.reduce_mean(loss, axis=0)
    loss = tf.reduce_mean(weights * loss)
    return loss

1.6 Dice + crossEntropy

Dice + crossEntropy代码如下:

def dice_and_crossentroy(Y_pred, Y_gt, weightdice, weightfocal, lamda1=100, lamda2=1):
    """
    hybrid loss function from dice loss and crossentroy
    loss=Ldice+lamda*Lfocalloss
    :param Y_pred:A tensor resulting from a softmax(-1,z,h,w,numclass)
    :param Y_gt: A tensor of the same shape as `y_pred`
    :param gamma:Difficult sample weight
    :param alpha:Sample category weight,which is shape (C,) where C is the number of classes
    :param lamda:trade-off between dice loss and focal loss,can set 0.1,0.5,1
    :return:diceplusfocalloss
    """
    weight_loss1 = np.array(weightdice)
    weight_loss2 = np.array(weightfocal)
    smooth = 1.e-5
    smooth_tf = tf.constant(smooth, tf.float32)
    Y_pred = tf.cast(Y_pred, tf.float32)
    Y_gt = tf.cast(Y_gt, tf.float32)
    # Compute gen dice coef:
    numerator = Y_gt * Y_pred
    numerator = tf.reduce_sum(numerator, axis=(1, 2, 3))
    denominator = Y_gt + Y_pred
    denominator = tf.reduce_sum(denominator, axis=(1, 2, 3))
    gen_dice_coef = tf.reduce_sum(2. * (numerator + smooth_tf) / (denominator + smooth_tf), axis=0)
    loss1 = tf.reduce_mean(weight_loss1 * gen_dice_coef)
    epsilon = 1.e-5
    # scale preds so that the class probas of each sample sum to 1
    output = Y_pred / tf.reduce_sum(Y_pred, axis=- 1, keep_dims=True)
    # manual computation of crossentropy
    output = tf.clip_by_value(output, epsilon, 1. - epsilon)
    loss = -Y_gt * tf.log(output)
    loss = tf.reduce_mean(loss, axis=(1, 2, 3))
    loss = tf.reduce_mean(loss, axis=0)
    loss2 = tf.reduce_mean(weight_loss2 * loss)
    total_loss = lamda1 * (1 - loss1) + lamda2 * loss2
    return total_loss

1.7 Dice + Focal loss

Dice + Focal loss代码如下:

def dice_and_Focalloss(Y_pred, Y_gt, weightdice, weightfocal, lamda1=100, lamda2=1, gamma=4.):
    """
    hybrid loss function from dice loss and focalloss
    loss=Ldice+lamda*Lfocalloss
    :param Y_pred:A tensor resulting from a softmax(-1,z,h,w,numclass)
    :param Y_gt: A tensor of the same shape as `y_pred`
    :param gamma:Difficult sample weight
    :param alpha:Sample category weight,which is shape (C,) where C is the number of classes
    :param lamda:trade-off between dice loss and focal loss,can set 0.1,0.5,1
    :return:dicePfocalloss
    """
    weight_loss1 = np.array(weightdice)
    weight_loss2 = np.array(weightfocal)
    smooth = 1.e-5
    smooth_tf = tf.constant(smooth, tf.float32)
    Y_pred = tf.cast(Y_pred, tf.float32)
    Y_gt = tf.cast(Y_gt, tf.float32)
    # Compute gen dice coef:
    numerator = Y_gt * Y_pred
    numerator = tf.reduce_sum(numerator, axis=(1, 2, 3))
    denominator = Y_gt + Y_pred
    denominator = tf.reduce_sum(denominator, axis=(1, 2, 3))
    gen_dice_coef = tf.reduce_sum(2. * (numerator + smooth_tf) / (denominator + smooth_tf), axis=0)
    loss1 = tf.reduce_mean(weight_loss1 * gen_dice_coef)
    epsilon = 1.e-5
    # Scale predictions so that the class probas of each sample sum to 1
    output = Y_pred / tf.reduce_sum(Y_pred, axis=- 1, keepdims=True)
    # Clip the prediction value to prevent NaN's and Inf's
    output = tf.clip_by_value(output, epsilon, 1. - epsilon)
    # Calculate Cross Entropy
    cross_entropy = -Y_gt * tf.log(output)
    # Calculate Focal Loss
    loss = tf.pow(1 - output, gamma) * cross_entropy
    loss = tf.reduce_mean(loss, axis=(1, 2, 3))
    loss = tf.reduce_mean(loss, axis=0)
    loss2 = tf.reduce_mean(weight_loss2 * loss)
    total_loss = lamda1 * (1 - loss1) + lamda2 * loss2
    return total_loss
    

参考文献

1、【损失函数合集】超详细的语义分割中的Loss大盘点
2、Tensorflow入门教程(四十七)——语义分割损失函数总结

  • 8
    点赞
  • 73
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值