分享

深度学习100问-17:图语义分割有哪些常用的评价指标?

 LibraryPKU 2020-02-15

深度学习100问

Author:louwill

Machine Learning Lab

     语义分割作为经典的图像分割问题,其本质上还是一种图像像素分类。既然是分类,我们就可以使用常见的分类评价指标来评估模型好坏。语义分割常见的评价指标包括像素准确率(Pixel Accuracy)、平均像素准确率(Mean Pixel Accuracy)、平均交并比(Mean IoU)、频权交并比(FWIoU)和Dice系数(Dice Coeffcient)等。

     这些指标看似跟常见的分类指标不太一样,但本质上都是基于分类混淆矩阵进行计算的。像素准确率也就是分类评价指标中的准确率(Accuracy)、平均准确率就是分类中的平均精确率(Mean Precision)、Dice系数则是F1-Score,只有平均交并比(MIoU)和频权交并比(FWIoU)在分类指标中找不对对应,但也可以基于混淆矩阵进行计算。

     假设图像中有n个目标类,那么加上背景类则是n+1,其中p_ii表示原本为i类同时也预测为i类,对应到混淆矩阵中则是TP(真阳)+TN(真阴),p_ij表示为原本为i类预测为j类,可以表示为FP(假阳),p_ji则为FN(假阴)。根据像素分类混淆矩阵,我们就可以计算如下指标。

像素准确率(PA)

     像素准确率跟分类中的准确率含义一样,即所有分类正确的像素数占全部像素的比例。PA的计算公式如下:


     对应到混淆矩阵中,则可以计算为:


平均像素准确率(MPA)

     平均像素准确率其实更应该叫平均像素精确率,是指分别计算每个类别分类正确的像素数占所有预测为该类别像素数比例的平均值。所以,从定义上看,这是精确率(Precision)的定义,MPA的计算公式如下:


     对应到混淆矩阵中,Precison可计算为:


平均交并比(MIoU)

     交并比(Intersection over Union)的定义很简单,将标签图像和预测图像看成是两个集合,计算两个集合的交集和并集的比值。而平均交并比则是将所有类的IoU取平均。MIoU的计算公式如下:

     这里注意公式中的分母,p_ii在前两项中都被计算了一次,所以还需要单独减掉一次。
     对应到混淆矩阵中,IoU可以计算为:

     具体如下图所示。交集为TP,并集为TP+FP+FN。

     以一张具体图像为例感受下IoU,下图是Ground Truth和预测图像,可以看到预测和真值还是有些差距的。

     两张图像的交集和并集对应到图像中,计算两张图像的比值即可。

频权交并比(FWIoU)

     频权交并比顾名思义,就是以每一类别的频率为权重和其IoU加权计算出来的结果。FWIoU的设计思想很明确,语义分割很多时候会面临图像中各目标类别不平衡的情况,对各类别IoU直接求平均不是很合理,所以考虑各类别的权重就非常重要了。FWIoU的计算公式如下:


     对应到混淆矩阵中可以计算为:


     其中每个类别的真实像素量为TP+FN,除以总像素量即可得到每个类别像素权重。

Dice系数

     Dice系数是一种度量两个集合相似性的函数,是语义分割中最常用的评价指标之一。Dice系数定义为两倍的交集除以像素和,跟IoU有点类似,其计算公式如下:


     Dice系数对应到分类指标中则是F1得分,其计算公式为精确率和召回率的调和平均数。对应到混淆矩阵中,用precision和recall的公式可以推导Dice的计算公式为:


     可以看到Dice系数对应于IoU,分子分母中的TP都取了两倍。通常1-Dice可作为语义分割的损失函数。Dice作为笔者常用的语义分割评价指标,这里给出Dice在Keras和PyTorch中的实现方式。

     Keras实现方式:
from keras import backend as K
def dice_coef(y_true, y_pred, smooth=1):    ''' Dice = (2*|X & Y|)/ (|X|+ |Y|)
         =  2*sum(|A*B|)/(sum(A^2)+sum(B^2))    '''    y_true = K.flatten(y_true)    y_pred = K.flatten(y_pred)    intersection = K.sum(K.abs(y_true * y_pred))    dice = (2. * intersection + smooth) / (K.sum(K.square(y_true)) + K.sum(K.square(y_pred)) + smooth) return dice
     PyTorch实现方式:
import torch
def dice_coef(pred, target):    '''    Dice = (2*|X & Y|)/ (|X|+ |Y|)         =  2*sum(|A*B|)/(sum(A^2)+sum(B^2))    '''    smooth = 1.    m1 = pred.view(-1).float()    m2 = target.view(-1).float()    intersection = (m1 * m2).sum().float()    dice = (2. * intersection + smooth) / (torch.pow(m1, 2).sum() + torch.pow(m2,2).sum() + smooth) return dice
     最后基于numpy实现一个包含全部分割指标的类:
import numpy as np
class SegMetrics(object):    def __init__(self, num_class):        self.num_class = num_class self.confusion_matrix = np.zeros((self.num_class,)*2)
    # 像素准确率    def Pixel_Accuracy(self):        Acc = np.diag(self.confusion_matrix).sum() / self.confusion_matrix.sum() return Acc
    # 平均像素准确率    def Mean_Pixel_Accuracy(self):        # Precision=TP/(TP+FP)        Acc = np.diag(self.confusion_matrix) / self.confusion_matrix.sum(axis=1)        Acc = np.nanmean(Acc) return Acc
    # 平均IoU    def Mean_IoU(self):        MIoU = np.diag(self.confusion_matrix) / (                    np.sum(self.confusion_matrix, axis=1) + np.sum(self.confusion_matrix, axis=0) -                    np.diag(self.confusion_matrix))        MIoU = np.nanmean(MIoU) return MIoU
    # 频权IoU    def Freq_Weighted_IoU(self):        freq = np.sum(self.confusion_matrix, axis=1) / np.sum(self.confusion_matrix)        iu = np.diag(self.confusion_matrix) / (np.sum(self.confusion_matrix, axis=1) + np.sum(self.confusion_matrix, axis=0) -                    np.diag(self.confusion_matrix))        FWIoU = (freq[freq > 0] * iu[freq > 0]).sum()        return FWIoU  
    # Dice系数    def dice(self):        dice_coef = 2*np.diag(self.confusion_matrix) / (            np.sum(self.confusion_matrix, axis=1) + np.sum(self.confusion_matrix, axis=0)) return dice_coef
    # 生成混淆矩阵    def _generate_matrix(self, gt_image, pre_image):        mask = (gt_image >= 0) & (gt_image < self.num_class)        label = self.num_class * gt_image[mask] + pre_image[mask]        count = np.bincount(label, minlength=self.num_class**2)        confusion_matrix = count.reshape(self.num_class, self.num_class) return confusion_matrix
    # 为真值和预测值生成混淆矩阵    def add_batch(self, gt_image, pre_image):        assert gt_image.shape == pre_image.shape self.confusion_matrix += self._generate_matrix(gt_image, pre_image)
# 重置混淆矩阵    def reset(self): self.confusion_matrix = np.zeros((self.num_class,) * 2)
     以上就是本节内容。

参考资料:

https://www./evaluating-image-segmentation-models/

https://github.com/xtudbxk/semantic-segmentation-metrics

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多