图像分割任务中的评价指标简析

前言

在图像分割领域中,我们需要使用特定的指标来评估实验效果。
在这里插入图片描述

上图源自《Fully Convolutional Networks for Semantic Segmentation

整个评价体系中,被广泛应用的几个评价指标有:

  • Precision
  • Recall
  • Accuracy
  • IoU
  • F1-Score
  • Mean Accuracy / Frequency Weighted Accuracy / Mean IoU / Frequency Weighted IoU等衍生指标

在此对上述用到的指标做出简要分析。这一切还需要从分类任务中的TP/FP/FN/TN的概念说起。
在这里插入图片描述

评估指标

TP/FP/FN/TN

在二分类场景(记为类别0和类别1)中,有四种分类结果,即:

  • 本该是0被分为0
  • 本该是0被分为1
  • 本该是1被分为0
  • 本该是1被分为1

若以类别1为正例(positive),类别0为负例(negative)。则分类结果可以如下图所示:
在这里插入图片描述
则上述四种分类结果分别可以概括为:

  • 本该是0被分为0 (True negative,TN,真阴)
  • 本该是0被分为1 (False positive,FP,假阳)
  • 本该是1被分为0 (False negative,FN,假阴)
  • 本该是1被分为1 (True positive,TP,真阳)

上图中,selected elements表示在本次分类任务中被分类为positives的元素,包括TPs和NPs。relevant elements表示在本次分类任务中本应是positives的元素,包括TPs和FNs。

注:TPs是所有TP的意思,类同。

Precision和Recall

指标Precision和Recall是基于对正例分类正确率的评估而建立的。

其中Precision是要评估在所有被分类为positives的元素中实际分类正确的概率,即:

P r e c i s i o n = T P s T P s + F P s Precision = \frac {TPs}{TPs+FPs} Precision=TPs+FPsTPs
在这里插入图片描述
而Recall则是要评估正例分类正确占本应是positives的所有元素的概率,即:

R e c a l l = T P s T P s + F N s Recall = \frac {TPs}{TPs+FNs} Recall=TPs+FNsTPs
在这里插入图片描述
简而言之,Precision和Recall的分子都是TPs,都是要评估正例分类正确的水平。

不同的是分母,Precision是以被分类的所有样本为分母,分母可随每次采样元素变化而变化。Precision的意义在于衡量是否有误判,希望误判越少越好。

而Recall则是以原本所有的positives元素为分母,分母不随采样元素变化而变化。Recall的意义在于衡量是否有遗漏,希望遗漏越少越好。

Accuracy

说到Accuracy,很容易和Precision搞混,实际上二者不一样。Accuracy实际上衡量范围更广,相比于Precision只将TP作为考虑范围,Accuracy则是将TP和TN都纳入评估范围。

A c c u r a c y = T P s + T N s T P s + F P s + T N s + F N s = T P s + T N s T o t a l s \begin{aligned}Accuracy &= \frac {TPs+TNs}{TPs+FPs+TNs+FNs}\\ &=\frac {TPs+TNs}{Totals}\end{aligned} Accuracy=TPs+FPs+TNs+FNsTPs+TNs=TotalsTPs+TNs

IoU

IoU和Precision/Recall比较相近,同样是对正例分类正确的水平进行衡量,不同的是IoU的分母是Precision和Recall分母的并集。正如其全名Intersection over Union(交并比),即selected elements和relevant elements的交集比上两者的并集,实际上就是:

I o U = T P s T P s + F P s + F N s IoU = \frac {TPs}{TPs+FPs+FNs} IoU=TPs+FPs+FNsTPs

在这里插入图片描述

F1-Score

F1-Score是Precision和Recall的调和平均数。
在这里插入图片描述
在上面说到Precision和Recall,提到Precision是以被分类的所有样本为分母,Recall则是以原本所有的positives元素为分母。二者之间并没有建立直接联系,如果一个分类器,Precision很高但是Recall很低,或者Recall很高但是Precision很低,这两种分类器都是不好的,都是我们不希望的。所以我们采用F1-Score来建立Precision和Recall的联系。

在数学中,我们知道调和平均数是永远小于等于算术均值平均数的,当用于求两个数的平均数时,如果直接用算术平均作为结果,那么两数之间的差异将被大的值削平,而调和平均数则不会极大削平这种大的差异,得到的结果更倾向于小的值。

例如,1和9的平均,算术平均数为5,而调和平均数约为2。

采用F1-Score能够更好的同时衡量Precision和Recall,也就是希望在遗漏少的前提下误判也少,这样得到的F1-Score才会高。

Mean / Frequency Weighted 衍生

用于多分类任务中,其中Mean IoU表示计算每一类的IoU后求均值,Frequency Weighted IoU表示根据每一类出现的频率对各个类的IoU进行加权求和。Mean Accuracy和Frequency Weighted Accuracy类同。

Python实现

对于上述评估指标,一般由混淆矩阵计算后处理得到。
在这里插入图片描述
对于猫狗二分类问题,分类结果如下:
在这里插入图片描述
若以猫为正类,则混淆矩阵如下:
在这里插入图片描述

计算混淆矩阵

import numpy as np


class Metric(object):
    def __init__(self, n_classes):
        self.n_classes = n_classes
        self.confusion_matrix = np.zeros((n_classes, n_classes))

    def _fast_hist(self, label_true, label_pred, n_class):
        mask = (label_true >= 0) & (label_true < n_class)
        hist = np.bincount(
            n_class * label_true[mask].astype(int) + label_pred[mask], minlength=n_class ** 2
        ).reshape(n_class, n_class)
        return hist

    def update(self, label_trues, label_preds):
        for lt, lp in zip(label_trues, label_preds):
            self.confusion_matrix += self._fast_hist(
                lt.flatten(), lp.flatten(), self.n_classes)

根据混淆矩阵计算评价指标

# For multi-classes
def get_scores(self):
        """
        Returns accuracy score evaluation result.
            - Overall accuracy
            - Mean accuracy
            - Frequency Weighted acc
            - Mean IoU
            - Overall F1
        """
        hist = self.confusion_matrix
        FP = hist.sum(axis=0) - np.diag(hist)
        FN = hist.sum(axis=1) - np.diag(hist)
        TP = np.diag(hist)
        precision = TP / (TP+FP)
        recall = TP / (TP+FN)
        f1 = (2 * (precision*recall) / (precision + recall)).mean()

        acc = np.diag(hist).sum() / hist.sum()
        acc_cls = np.diag(hist) / hist.sum(axis=1)
        acc_cls = np.nanmean(acc_cls)
        iou = np.diag(hist) / (hist.sum(axis=1) +
                              hist.sum(axis=0) - np.diag(hist))
        mean_iou = np.nanmean(iou)
        freq = hist.sum(axis=1) / hist.sum()
        fwavacc = (freq[freq > 0] * iou[freq > 0]).sum()
        cls_iou = dict(zip(range(self.n_classes), iou))

        return (
            {
                "Overall Acc: \t": acc,
                "Mean Acc : \t": acc_cls,
                "FreqW Acc : \t": fwavacc,
                "Mean IoU : \t": mean_iou,
                "Overall F1: \t": f1
            },
            cls_iou,
        )

完整代码

import numpy as np


class Metric(object):
    def __init__(self, n_classes):
        self.n_classes = n_classes
        self.confusion_matrix = np.zeros((n_classes, n_classes))

    def _fast_hist(self, label_true, label_pred, n_class):
        mask = (label_true >= 0) & (label_true < n_class)
        hist = np.bincount(
            n_class * label_true[mask].astype(int) + label_pred[mask], minlength=n_class ** 2
        ).reshape(n_class, n_class)
        return hist

    def update(self, label_trues, label_preds):
        for lt, lp in zip(label_trues, label_preds):
            self.confusion_matrix += self._fast_hist(
                lt.flatten(), lp.flatten(), self.n_classes)
    
    # For multi-classes
    def get_scores(self):
        """
        Returns accuracy score evaluation result.
            - Overall accuracy
            - Mean accuracy
            - Frequency Weighted acc
            - Mean IoU
            - Overall F1
        """
        hist = self.confusion_matrix
        FP = hist.sum(axis=0) - np.diag(hist)
        FN = hist.sum(axis=1) - np.diag(hist)
        TP = np.diag(hist)
        precision = TP / (TP+FP)
        recall = TP / (TP+FN)
        f1 = (2 * (precision*recall) / (precision + recall)).mean()

        acc = np.diag(hist).sum() / hist.sum()
        acc_cls = np.diag(hist) / hist.sum(axis=1)
        acc_cls = np.nanmean(acc_cls)
        iou = np.diag(hist) / (hist.sum(axis=1) +
                              hist.sum(axis=0) - np.diag(hist))
        mean_iou = np.nanmean(iou)
        freq = hist.sum(axis=1) / hist.sum()
        fwavacc = (freq[freq > 0] * iou[freq > 0]).sum()
        cls_iou = dict(zip(range(self.n_classes), iou))

        return (
            {
                "Overall Acc: \t": acc,
                "Mean Acc : \t": acc_cls,
                "FreqW Acc : \t": fwavacc,
                "Mean IoU : \t": mean_iou,
                "Overall F1: \t": f1
            },
            cls_iou,
        )

    def reset(self):
        self.confusion_matrix = np.zeros((self.n_classes, self.n_classes))

在这里插入图片描述

参考资料

[1] Fully Convolutional Networks for Semantic Segmentation
[2] F-score - Wikipedia
[3] 语义分割之评价指标 - 知乎
[4] 机器学习中的F1度量,为什么定义为precision和recall的调和平均,而不是算术平均? - 知乎
[5] 评估指标中IoU/precision/recall/tp/fp/fn/tn的个人理解_TracelessLe的专栏-优快云博客
[6] Confusion matrix - Wikipedia
[7] FaceParsing.PyTorch/metrics.py at master · TracelessLe/FaceParsing.PyTorch
[8] python实现混淆矩阵 - 知乎
[9] 混淆矩阵 - 维基百科,自由的百科全书

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

TracelessLe

❀点个赞加个关注再走吧❀

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值