自学内容网 自学内容网

语义分割性能提升---通过优化损失改进分割效果

本文主要总结最近的调研调试结果,介绍通过改进损失来提升语义分割的分割效果;当然还有其他途径,比如蒸馏(提升分割效果)、剪枝(提升fps),之前博客有总结,此处不做介绍。

一、Dice损失

语义分割一般绕不开Dice损失,公式如下:

DiceLoss=1- 2X∩Y+SmoothX+Y+Smooth

集合相似度度量函数,

        通常用于计算两个样本的相似度,属于metric learning。X为真实目标mask,Y为预测目标mask,我们总是希望X和Y交集尽可能大,占比尽可能大,但是loss需要逐渐变小,所以在比值前面添加负号。
        另外,该损失可以缓解样本中前景背景(面积)不平衡带来的消极影响,前景背景不平衡也就是说图像中大部分区域是不包含目标的,只有一小部分区域包含目标。Dice Loss训练更关注对前景区域的挖掘,即保证有较低的FN,但会存在损失饱和问题,而CE Loss是平等地计算每个像素点的损失。因此单独使用Dice Loss往往并不能取得较好的结果,需要进行组合使用,比如Dice Loss+CELoss或者Dice Loss+Focal Loss等。

如下为Dice_Loss的torch实现:

def Dice_loss(inputs, target, beta=1, smooth = 1e-5):
    n, c, h, w = inputs.size()
    nt, ht, wt, ct = target.size()
    if h != ht and w != wt:
        inputs = F.interpolate(inputs, size=(ht, wt), mode="bilinear", align_corners=True)
        
    temp_inputs = torch.softmax(inputs.transpose(1, 2).transpose(2, 3).contiguous().view(n, -1, c),-1)
    temp_target = target.view(n, -1, ct)

    #--------------------------------------------#
    #   计算dice loss
    #--------------------------------------------#
    tp = torch.sum(temp_target[...,:-1] * temp_inputs, axis=[0,1])
    fp = torch.sum(temp_inputs                       , axis=[0,1]) - tp
    fn = torch.sum(temp_target[...,:-1]              , axis=[0,1]) - tp

    score = ((1 + beta ** 2) * tp + smooth) / ((1 + beta ** 2) * tp + beta ** 2 * fn + fp + smooth)
    dice_loss = 1 - torch.mean(score)
    return dice_loss

二、Tversky Loss

公式为:

T(X,Y)=X∩YX∩YX-Y+β|Y-X|

其中,|X|表示预测的分割图像,|Y|表示标签的分割图像。Tversky系数是Dice系数和 Jaccard 系数(就是IOU系数)的广义系数。

        如果分割任务更加关注召回率(高灵敏度),即真实mask尽可能都被预测出来,不太关注预测mask有没有多预测。Y为真实mask,X为预测mask。 其中α和β可以影响召回率和准确率,若想目标有较高的召回率,那么我们可以选择较高的beta;相反,若想有较高的准确率,则可选择较高的α。

        当设置α=β=0.5,此时Tversky系数就是Dice系数。而当设置α=β=1时,此时Tversky系数就是Jaccard系数。其中|X-Y|表示FP(假阳性),|Y-X|表示FN(假阴性),α,β分别控制假阴性和假阳性。通过调整 α 和 β 这两个超参数可以控制这两者之间的权衡,进而影响召回率等指标。

如下为Tiversky代码:

只用了一个参数alpha,另一个参数通过1-alpha来控制。

class tversky(nn.Module):
    def __init__(self, smooth=1):
        super(tversky, self).__init__()
        self.smooth = smooth


    def forward(self, logits, label,alpha=0.7):
        '''
        args: logits: tensor of shape (1, H, W)
        args: label: tensor of shape (1, H, W)
        '''
        probs = torch.sigmoid(logits)
        # print("logits:",probs.shape)
        # print("label:",label.shape)
        true_pos = torch.sum(label * probs)
        false_neg = torch.sum(label * (1 - probs))
        false_pos = torch.sum((1 - label) * probs)
        loss = (true_pos + self.smooth)/(true_pos + alpha*false_neg + (1-alpha)*false_pos + self.smooth)

        return 1-loss

三、boundary_loss边界损失

作者实验表明,boundary_loss结合Dice_Loss效果非常好,一个利用距离,一个利用边界。作者对这两个loss的用法是给他们一个权重,训练初期dice loss很高,随着训练进行,Boundary loss比例增加,也就是说越到训练后期越关注边界的准确,边界处理得更细一些。

大致原理为将distance map当做权重来作为某类loss的权重系数训练,详细公式解释请看以下链接:

https://zhuanlan.zhihu.com/p/72783363

源码中,核心部分的distance map变换代码如下:

def one_hot2dist(seg):
    res = np.zeros_like(seg)
    for i in range(len(seg)):
        posmask = seg[i].astype(np.bool)
        if posmask.any():
            negmask = ~posmask
            # print('negmask:', negmask)
            # print('distance(negmask):', distance(negmask))
            res[i] = distance(negmask) * negmask - (distance(posmask) - 1) * posmask
            # print('res[c]', res[c])
    return res

假设某一类别的mask如下:

则得到的distance map为:

可以看到,边界处的权重为0,mask内部为负值,背景区域离边界越远权重值越大。

根据上面的distance map的可视化结果看出,如果预测边界小于或者完全符合真实边界并被真实边界包围,这时候loss为负。根据实测,一般训练到最后,Boundary loss会为负值。

如下为我调通的boundary_loss的GitHub链接:

active-boundary-loss/abl.py at main · wangchi95/active-boundary-loss · GitHubOfficial repository for Active Boundary Loss for Semantic Segmentation. - active-boundary-loss/abl.py at main · wangchi95/active-boundary-lossicon-default.png?t=O83Ahttps://github.com/wangchi95/active-boundary-loss/blob/main/abl.py切记:传入的pred和gt形状,需满足如下要求:

        Input:
            - pred: the output from model (before softmax)
                    shape (N, C, H, W)
            - gt: ground truth map
                    shape (N, H, w)

若不满足,需要进行转换。

如下为我自己的调用代码:

            Boundary = True
            if Boundary:
                
                # 我的输入为:outputs.shape=(n,c,h,w);labels.shape=(n,h,w,c)
                n, c, h, w = outputs.size()
                nt, ht, wt, ct = labels.size()

                # 进行维度转换
                temp_labels = torch.flatten(labels[...,:-1].transpose(2, 3).transpose(1, 2), 0, 1)  # labels.shape: n h w 4 --> n*3 h w
                # print('temp_labels.shape:',temp_labels.shape) 
                
                 # 调用
                abl = ABL()
                main_boundaryloss = abl(outputs,temp_labels)
                
                # loss为原损失(dice_loss),在此处也可以加一个系数,来控制dice_loss和边界损失的占比
                loss = loss + main_boundaryloss


原文地址:https://blog.csdn.net/qq_41920323/article/details/142528307

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!