• 【机器学习】图像语义分割常用指标Dice系数 敏感性 特异性 IOU及python代码实现


    知识铺垫

    二分类和多分类的评价指标的选择
    首先,对于像素点,我们要知道,当预测的像素点类别和其真实类别不同或者相同时,我们可以用混淆矩阵来表示,如下图:
    在这里插入图片描述

    1. Dice系数和IOU

    首先,dice和IOU都是衡量两个集合之间相似性的度量,在图像分割领域用来衡量网络分割结果与金标准mask(标签)之间的相似性。
    (分割网络评价指标)dice系数和IOU之间的区别和联系

    IOU

    IOU指标可以使用几何办法给出直观的解释,我们假设红色代表图片中的标签,黄色是分割的结果,那么他们重叠的蓝色部分就是TP(true Positive)真正例,也就是正样本被正确分类。
    那么对应的红色就是预测为负但实际为正,因此是FN。
    下面黄色部分,就是预测为正,但实际为负,因此是FP(false Positive)。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    结合上述图片,我们可以给出IOU的公式(结合几何意义)
    I O U = T P F N + T P + F P IOU = \frac{TP}{FN+TP+FP} IOU=FN+TP+FPTP
    I O U = 预测正确:蓝色 ( 红色 + 蓝色 + 黄色 ) IOU=\frac{预测正确:蓝色}{(红色+蓝色+黄色)} IOU=(红色+蓝色+黄色)预测正确:蓝色
    这位是对于一个类别来说的IOU公式,而mIOU公式是对所有类别求交并比的平均值。
    m I O U = 1 k ∑ i = 1 k T P F N + T P + F P mIOU = \frac{1}{k}\sum_{i=1}^{k}\frac{TP}{FN+TP+FP} mIOU=k1i=1kFN+TP+FPTP
    mIOU的计算过程中首先需要计算混淆矩阵。

    Dice系数

    Dice系数、mIOU与Dice LOss
    Dice系数是一种集合的相似度度量函数,通常用于计算两个样本的相似度,取值范围在[0,1]之间。
    在这里插入图片描述
    我们同样根据上图,可以得出Dice系数的公式为:

    Dice系数的计算公式如下:

    D i c e = 2 × T P ( T P + F N ) + ( T P + F P ) = 2 × 预测正确:蓝色 ( 真实结果:红色 + 蓝色 ) + ( 预测结果:黄色 + 蓝色 ) Dice=\frac{2\times TP}{(TP+FN)+(TP+FP)} =\frac{2\times 预测正确:蓝色}{(真实结果:红色+蓝色)+(预测结果:黄色+蓝色)} Dice=(TP+FN)+(TP+FP)2×TP=(真实结果:红色+蓝色)+(预测结果:黄色+蓝色)2×预测正确:蓝色

    Dice和IOU的关系分析

    Dice和IOU关系分析
    集合交并比: m I O U = T P F N + T P + F P 集合交并比:mIOU = \frac{TP}{FN+TP+FP} 集合交并比:mIOU=FN+TP+FPTP
    集合相似度: D i c e = 2 × T P ( T P + F N ) + ( T P + F P ) 集合相似度:Dice=\frac{2\times TP}{(TP+FN)+(TP+FP)} 集合相似度:Dice=(TP+FN)+(TP+FP)2×TP
    在这里插入图片描述
    集合交并比: I O U = T P F N + T P + F P = T P F N + T P + F P + T P − T P = 2 T P 2 ( F N + T P + F P + T P ) − 2 T P = D i c e 2 − D i c e 集合交并比:IOU = \frac{TP}{FN+TP+FP}\\ = \frac{TP}{FN+TP+FP+TP-TP}\\ =\frac{2TP}{2(FN+TP+FP+TP)-2TP}\\ =\frac{Dice}{2-Dice} 集合交并比:IOU=FN+TP+FPTP=FN+TP+FP+TPTPTP=2(FN+TP+FP+TP)2TP2TP=2DiceDice
    由公式可知,Dice一般情况下>IOU。
    曲线如下:
    在这里插入图片描述

    代码

    由于网上给的Dice系数的求解代码基本上都是batch_size=1的,当batch_size>1的时候,就没法用了,因此在这里对网上流行的代码和自己改的代码分别进行总结。

    def dice_coef(output, target): # batch_size=1
        smooth = 1e-5
        #output = torch.sigmoid(output).view(-1).data.cpu().numpy()
        output=torch.sigmoid(output)
        output[output > 0.5] = 1  #将概率输出变为于标签相匹配的矩阵
        output[output <= 0.5] = 0
        #target = target.view(-1).data.cpu().numpy()
    
        intersection = (output * target).sum() = TP
    	# \符号有换行的作用
        return (2. * intersection + smooth) / \
            (output.sum() + target.sum() + smooth)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    # dice=2*TP/(TP+FN)+(TP+FP)
    def dice_coef(output, target):  # Batch_size>1时
        smooth = 1e-5
        #output = torch.sigmoid(output).view(-1).data.cpu().numpy()
        output=torch.sigmoid(output).data.cpu().numpy()
        output[output > 0.5] = 1  #将概率输出变为于标签相匹配的矩阵
        output[output <= 0.5] = 0
        # target = target.view(-1).data.cpu().numpy()
        target = target.data.cpu().numpy()
        dice=0.
        # ipdb.set_trace() # 用于断掉调试
        if len(output)>1:# 如果样本量>1,则逐样本累加
            for i in range(len(output)):
                intersection = (output[i] * target[i]).sum()
                dice += (2. * intersection + smooth)/(output[i].sum() + target[i].sum() + smooth)
        else:
            intersection = (output * target).sum() # 一个数字,=TP
            dice = (2. * intersection + smooth) /(output.sum() + target.sum() + smooth)
        return dice
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    IOU的计算公式如下:
    D i c e = T P T P + F N + F P Dice=\frac{TP}{TP+FN+FP} Dice=TP+FN+FPTP

    # batch_size = 1
    def iou_score(output, target):
        smooth = 1e-5
    
        if torch.is_tensor(output):
            output = torch.sigmoid(output).data.cpu().numpy()
        if torch.is_tensor(target):
            target = target.data.cpu().numpy()
        output_ = output > 0.5
        target_ = target > 0.5
        intersection = (output_ & target_).sum()
        union = (output_ | target_).sum()
    
        return (intersection + smooth) / (union + smooth)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    #batch_size > 1
    def iou_score(output, target):
        smooth = 1e-5
    
        if torch.is_tensor(output):
            output = torch.sigmoid(output).data.cpu().numpy()
        if torch.is_tensor(target):
            target = target.data.cpu().numpy()
        output_ = output > 0.5
        target_ = target > 0.5
        # intersection = (output_ & target_).sum()
        # union = (output_ | target_).sum()
        iou = 0.
        if len(output)>1:
            for i in range(len(output)):
                union = (output_[i] | target_[i]).sum()
                intersection = (output_[i] & target_[i]).sum()
                iou += (intersection + smooth) / (union + smooth)
        else:
            intersection = (output_ & target_).sum()
            union = (output_ | target_).sum()
            iou = (intersection + smooth) / (union + smooth)
        return iou
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    2.敏感性(=Recall)、特异性和精确度(=precision=PPV)

    2.1 敏感性(召回率)和特异性

    Recall公式

    def get_sensitivity(output, gt): # 求敏感度 se=TP/(TP+FN)
        SE = 0.
        output = output > 0.5
        gt = gt > 0.5
        TP = ((output==1).byte() + (gt==1).byte()) == 2
        FN = ((output==0).byte() + (gt==1).byte()) == 2
        #wfy:batch_num>1时,改进
        if len(output)>1:
            for i in range(len(output)):
                SE += float(torch.sum(TP[i])) / (float(torch.sum(TP[i]+FN[i])) + 1e-6)
        else:
            SE = float(torch.sum(TP)) / (float(torch.sum(TP+FN)) + 1e-6) #原本只用这一句
        #SE = float(torch.sum(TP)) / (float(torch.sum(TP + FN)) + 1e-6)  # 原本只用这一句
        return SE  #返回batch中所有样本的SE和
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    特异性:

    def get_specificity(SR, GT, threshold=0.5):#求特异性 sp=TN/(FP+TN)
        SR = SR > threshold #得到true和false
        GT = GT > threshold
        SP=0.# wfy
        # TN : True Negative
        # FP : False Positive
        TN = ((SR == 0).byte() + (GT == 0).byte()) == 2
        FP = ((SR == 1).byte() + (GT == 0).byte()) == 2
        #wfy:batch_num>1时,改进
        if len(SR)>1:
            for i in range(len(SR)):
                SP += float(torch.sum(TN[i])) / (float(torch.sum(TN[i] + FP[i])) + 1e-6)
        else:
            SP = float(torch.sum(TN)) / (float(torch.sum(TN + FP)) + 1e-6) # 原本只用这一句
        #
        # SP = float(torch.sum(TN)) / (float(torch.sum(TN + FP)) + 1e-6)
        return SP
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    这两个指标在医疗领域很常用,而在机器学习领域常用的是Recall和Precision。

    2.2 敏感性和特异性之间的关系

    暂略

    2.3 Recall和Precision之间的关系

    ppv=precision

    def ppv(output, target): #阳性预测值,准确率(precision)pr = TP/(TP+FP)
        smooth = 1e-5
    
        if torch.is_tensor(output):
            output = torch.sigmoid(output).data.cpu().numpy()
        if torch.is_tensor(target):
            target = target.data.cpu().numpy()
        ppv=0.
        if len(output)>1:
            for i in range(len(output)):
                intersection = (output[i] * target[i]).sum()
                ppv += (intersection + smooth)/(output[i].sum() + smooth)
        else:
            intersection = (output * target).sum() # 一个数字,=TP
            ppv = (intersection + smooth)/(output.sum() + smooth)
    
        # intersection = (output * target).sum() # TP
        return ppv
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    3. F1

    def get_F1(output, gt):
        se = get_sensitivity(output, gt)
        pc = get_precision(output, gt)
        f1 = 2*se*pc / (se+pc+1e-6)
        return f1
    
    • 1
    • 2
    • 3
    • 4
    • 5
  • 相关阅读:
    LLM - LLaMA-2 获取文本向量并计算 Cos 相似度
    好物周刊#11:远程桌面软件
    企业如何确保电子邮件安全?
    uniapp开发的多端APP源码
    【b站韩顺平 快速学Java课】Java的JDK8(包括公共JRE8)安装教程 总结
    今天运气不错
    SQL获取IP电脑名
    基于GPT搭建私有知识库聊天机器人(四)问答实现
    让我们写一个 Win32 文本编辑器吧 - 1. 简介
    内网综合扫描工具-fscan的安装和使用
  • 原文地址:https://blog.csdn.net/adreammaker/article/details/126837006