语义分割的评价指标——PA(像素准确率)、CPA(类别像素准确率)、MPA(类别平均像素准确率)、IoU(交并比)、MIoU(平均交并比)详细总结

本文主要是介绍语义分割的评价指标——PA(像素准确率)、CPA(类别像素准确率)、MPA(类别平均像素准确率)、IoU(交并比)、MIoU(平均交并比)详细总结,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

语义分割是像素级别的分类,其常用评价指标:

像素准确率(Pixel Accuracy,PA)、
类别像素准确率(Class Pixel Accuray,CPA)、
类别平均像素准确率(Mean Pixel Accuracy,MPA)、
交并比(Intersection over Union,IoU)、
平均交并比(Mean Intersection over Union,MIoU),
其计算都是建立在混淆矩阵(Confusion Matrix)的基础上。

1 混淆矩阵

混淆矩阵就是统计分类模型的分类结果,即:统计归对类,归错类的样本的个数,然后把结果放在一个表里展示出来,这个表就是混淆矩阵

对于二分类问题,将类别A称为正例(Positive),类别B称为反例(Negative),分类器预测正确记作真(True),预测错误记作(False),由这4个基本组合,构成混淆矩阵的4个基础元素,为:

TP(True Positive):真正例,模型预测为正例,实际是正例(模型预测为类别1,实际是类别1)
FP(False Positive):假正例,模型预测为正例,实际是反例 (模型预测为类别1,实际是类别2)
FN(False Negative):假反例,模型预测为反例,实际是正例 (模型预测为类别2,实际是类别1)
TN(True Negative):真反例,模型预测为反例,实际是反例 (模型预测为类别2,实际是类别2)

在这里插入图片描述
假设有13种动物的样本-8只猫和5只狗
在这里插入图片描述
混淆矩阵中,系统预测了8只实际的猫,系统预测了3只是狗,而5只狗中,则预测有2只是猫。所有正确的预测都位于表格的对角线(以粗体突出显示)中,因此很容易从视觉上检查表格中的预测错误,因为它们将由对角线之外的值表示。

在这里插入图片描述
在这里我们假设猫是positive,非猫是negative (这里指的是狗)

  1. TP:表示测试样本中猫能够被准确地预测成猫
  2. TN: 表示测试样本中非猫(狗)能够被准确地预测成非猫(狗)
  3. FP:表示测试样本中非猫能够被准确地预测成猫
  4. FN:表示表示测试样本中猫能够被准确地预测成非猫

这里只要记住:T开头就是表示能准确预测, 后面接的P跟N 分别表示准确地预测了猫(P) 和非猫(N)
而 F 开头表示不能准确预测,FN可以理解成猫被错误得预测成了非猫,因为N 代表的是非猫。 相反, FP 可以理解为非猫被错误地预测称了猫。

准确率(Accuracy),对应:语义分割的像素准确率 PA
公式:Accuracy = (TP + TN) / (TP + TN + FP + FN)
意义:对角线计算。预测结果中正确的占总预测值的比例(对角线元素值的和 / 总元素值的和)

精准率(Precision),对应:语义分割的类别像素准确率 CPA
公式:Precision = TP / (TP + FP) 或 TN / (TN + FN)
意义:竖着计算。预测结果中,某类别预测正确的概率

召回率(Recall),不对应语义分割常用指标
公式:Recall = TP / (TP + FN) 或 TN / (TN + FP)
意义:横着计算。真实值中,某类别被预测正确的概率

举个例子:
在这里插入图片描述
以类别1为例,计算公式为:

准确率:Accuracy = (a + e + i) / (a + b + c + d + e +f + g + h + i)
精准率:P1 = a / (a + d + g)
召回率:R1 = a / (a + b + c)

不管进行分类的是动物,还是图片像素点,其混淆矩阵的获取、评价指标公式的计算都是一样的!

PA:像素准确率

对应:准确率(Accuracy)
含义:预测类别正确的像素数占总像素数的比例
混淆矩阵计算:对角线元素之和 / 矩阵所有元素之和PA = (TP + TN) / (TP + TN + FP + FN)

像素精度是图像分割的最简单指标,它是正确分类的总像素除以总像素,可以理解为图像中正确分类的像素的百分比。
下面我们举一个简单的例子


一般在图像分割,都是label图像的像素点
这里我们假设R指的是道路线像素点,S 指的是背景像素点
所以R 是positive, 非R(S)就是negative,指的是像素点不属于道路线
从上面的图我们可以看出

道路线像素点能够地预测为道路线像素点有3 个(中间行前两个+第一行中间) ,所以TP=3
非道路线像素点能够地预测为非道路线像素点有4个(最后一整行+中间行最后一个),所以TN=4
道路线点被错误地预测为非道路线像素点有2个(第一行的左右两个角落上S),所以FN=2

在这里插入图片描述
我们能够计算出像素精度:
在这里插入图片描述

MPA:类别平均像素准确率

含义:分别计算每个类被正确分类像素数的比例,即:CPA,然后累加求平均
混淆矩阵计算:每个类别像素准确率为:Pi(计算:对角线值 / 对应列的像素总数)MPA = sum(Pi) / 类别数

IoU

IoU(intersection over union) 值得是像素的真实值与预测值的交集除以像素的真实值和预测值的并集

在这里插入图片描述
在图像分割中,我们的目标是把人物分割出来。
右边的图片是真实值(黄色部分), 左边(黄色部分)是预测值
所以我们可以计算IoU:
真实值跟预测值交集(红色部分):

在这里插入图片描述真实值与预测值的并集(红色部分):

在这里插入图片描述在这里插入图片描述
在这里插入图片描述

import torch 
import pandas as pd  # For filelist reading
from torch.utils.data import Dataset
import myPytorchDatasetClass  # Custom dataset class, inherited from torch.utils.data.datasetdef iou(pred, target, n_classes = 37):
#n_classes :the number of classes in your datasetious = []pred = pred.view(-1)target = target.view(-1)# Ignore IoU for background class ("0")for cls in xrange(1, n_classes):  # This goes from 1:n_classes-1 -> class "0" is ignoredpred_inds = pred == clstarget_inds = target == clsintersection = (pred_inds[target_inds]).long().sum().data.cpu()[0]  # Cast to long to prevent overflowsunion = pred_inds.long().sum().data.cpu()[0] + target_inds.long().sum().data.cpu()[0] - intersectionif union == 0:ious.append(float('nan'))  # If there is no ground truth, do not include in evaluationelse:ious.append(float(intersection) / float(max(union, 1)))return np.array(ious)def evaluate_performance(net):Dataloader for test databatch_size = 1  filelist_name_test = '/path/to/my/test/filelist.txt'data_root_test = '/path/to/my/data/'dset_test = myPytorchDatasetClass.CustomDataset(filelist_name_test, data_root_test)test_loader = torch.utils.data.DataLoader(dataset=dset_test,  batch_size=batch_size,shuffle=False,pin_memory=True)data_info = pd.read_csv(filelist_name_test, header=None)num_test_files = data_info.shape[0]  #reture data.info's hangshu that means dots in dataset sample_size = num_test_files# Containers for resultspreds = torch.FloatTensor(sample_size, opt.imageSizeH, opt.imageSizeW)preds = Variable(seg,volatile=True)gts = torch.FloatTensor(sample_size, 1, opt.imageSizeH, opt.imageSizeW)gts = Variable(gts,volatile=True)dataiter = iter(test_loader) for i in xrange(sample_size):images, labels, filename = dataiter.next()images = Variable(images).cuda()labels = Variable(labels)gts[i:i+batch_size, :, :, :] = labelsoutputs = net(images)outputs = outputs.permute(0, 2, 3, 4, 1).contiguous()val, pred = torch.max(outputs, 4)preds[i:i+batch_size, :, :, :] = pred.cpu()acc = iou(preds, gts)return acc

MIoU

在语义分割中,MIoU 就是分别对每个类计算(真实标签和预测结果的交并比)IOU,然后再对所有类别的IOU求均值。MIoU是标准的准确率度量方法。
在这里插入图片描述
以求二分类的MIoU为例
MIoU = (IoU正例p + IoU反例n) / 2 = [ TP / (TP + FP + FN) + TN / (TN + FN + FP) ] / 2

F1 score (Dice score):

Dice score 是根据测试样本的准确性(precision)跟敏感度(也叫recall)计算出来的,就是平衡两个标准

precision:
用上面猫狗分类的例子解释
准确性就是 测试样本中准确地预测为猫的数除以测试样本准确预测的总数(包括猫和狗)

在这里插入图片描述

recall: 就是测试样本中准确地预测为猫的数除以测试样本猫总数

在这里插入图片描述

Dice score 的通用公式:
在这里插入图片描述

通常,我们认为recall的重要性跟precision的重要性一样,所以 belta=1
这就是所谓的 F1 score。
在这里插入图片描述

综合实例:
在这里插入图片描述 PA

PA = 对角线元素之和 / 矩阵所有元素之和 = (3 + 2 + 2) / (3 + 2 + 2 + 0 + 0 + 0 + 0 + 1 + 1)= 0.78

CPA

Pi = 对角线值 / 对应列的像素总数
P类别1 = 3 / ( 3 + 0 + 0) = 1
P类别2 = 2 / ( 0 + 2 + 1) = 0.67
P类别3 = 2 / ( 0 + 1 + 2) = 0.67

MPA

MPA = sum(Pi) / 类别数 = ( P类别1 + P类别2 + P类别3 ) / 3 = 0.78

IoU

IoUi = 对角线值 / 与该值有关元素的求和 [画线法]
第一种求法:(混淆矩阵,以对角线值画横、竖线,将相关元素求和)
IoU类别1 = 3 / (3 + 0 + 0 + 0 + 0) = 1
IoU类别2 = 2 / (0 + 2 + 1 + 0 + 1) = 0.5
IoU类别3 = 2 / (0 + 1 + 2 + 0 + 1) = 0.5

第二种求法:(代码中用的求法:SAUB = SA + SB - SA∩B)(参考链接)
IoU类别1 = 3 / [(3 + 0 + 0 ) + ( 3 + 0 + 0) - 3] = 1
IoU类别2 = 2 / [(0 + 2 + 1 ) + (0 + 2 + 1) - 2] = 0.5
IoU类别3 = 2 / [(0 + 1 + 2 ) + (0 + 1 + 2) - 2] = 0.5

MIoU

MIoU = sum(IoUi) / 类别数 = (1 + 0.5 + 0.5) / 3 = 0.67

"""
refer to https://github.com/jfzhang95/pytorch-deeplab-xception/blob/master/utils/metrics.py
"""
import numpy as np
__all__ = ['SegmentationMetric']"""
confusionMetric  # 注意:此处横着代表预测值,竖着代表真实值,与之前介绍的相反
P\L     P    N
P      TP    FP
N      FN    TN
"""
class SegmentationMetric(object):def __init__(self, numClass):self.numClass = numClassself.confusionMatrix = np.zeros((self.numClass,)*2)def pixelAccuracy(self):# return all class overall pixel accuracy#  PA = acc = (TP + TN) / (TP + TN + FP + TN)acc = np.diag(self.confusionMatrix).sum() /  self.confusionMatrix.sum()return accdef classPixelAccuracy(self):# return each category pixel accuracy(A more accurate way to call it precision)# acc = (TP) / TP + FPclassAcc = np.diag(self.confusionMatrix) / self.confusionMatrix.sum(axis=1)return classAcc # 返回的是一个列表值,如:[0.90, 0.80, 0.96],表示类别1 2 3各类别的预测准确率def meanPixelAccuracy(self):classAcc = self.classPixelAccuracy()meanAcc = np.nanmean(classAcc) # np.nanmean 求平均值,nan表示遇到Nan类型,其值取为0return meanAcc # 返回单个值,如:np.nanmean([0.90, 0.80, 0.96, nan, nan]) = (0.90 + 0.80 + 0.96) / 3 =  0.89def meanIntersectionOverUnion(self):# Intersection = TP Union = TP + FP + FN# IoU = TP / (TP + FP + FN)intersection = np.diag(self.confusionMatrix) # 取对角元素的值,返回列表union = np.sum(self.confusionMatrix, axis=1) + np.sum(self.confusionMatrix, axis=0) - np.diag(self.confusionMatrix) # axis = 1表示混淆矩阵行的值,返回列表; axis = 0表示取混淆矩阵列的值,返回列表 IoU = intersection / union  # 返回列表,其值为各个类别的IoUmIoU = np.nanmean(IoU) # 求各类别IoU的平均return mIoUdef genConfusionMatrix(self, imgPredict, imgLabel): # 同FCN中score.py的fast_hist()函数# remove classes from unlabeled pixels in gt image and predictmask = (imgLabel >= 0) & (imgLabel < self.numClass)label = self.numClass * imgLabel[mask] + imgPredict[mask]count = np.bincount(label, minlength=self.numClass**2)confusionMatrix = count.reshape(self.numClass, self.numClass)return confusionMatrixdef Frequency_Weighted_Intersection_over_Union(self):# FWIOU =     [(TP+FN)/(TP+FP+TN+FN)] *[TP / (TP + FP + FN)]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 FWIoUdef addBatch(self, imgPredict, imgLabel):assert imgPredict.shape == imgLabel.shapeself.confusionMatrix += self.genConfusionMatrix(imgPredict, imgLabel)def reset(self):self.confusionMatrix = np.zeros((self.numClass, self.numClass))if __name__ == '__main__':imgPredict = np.array([0, 0, 1, 1, 2, 2]) # 可直接换成预测图片imgLabel = np.array([0, 0, 1, 1, 2, 2]) # 可直接换成标注图片metric = SegmentationMetric(3) # 3表示有3个分类,有几个分类就填几metric.addBatch(imgPredict, imgLabel)pa = metric.pixelAccuracy()cpa = metric.classPixelAccuracy()mpa = metric.meanPixelAccuracy()mIoU = metric.meanIntersectionOverUnion()print('pa is : %f' % pa)print('cpa is :') # 列表print(cpa)print('mpa is : %f' % mpa)print('mIoU is : %f' % mIoU)

测评截图:
步骤一:输入预测和标记图片 (4个类别分类,图片大小:512*512)
imgPredict = pic_predict # 预测图片
imgLabel = pic_label # 标注图片

步骤二:获取混淆矩阵 (特别注意:此代码混淆矩阵,行为预测值,列为真实值,因此:CPA的计算需要翻转一下)

在这里插入图片描述

可计算上述值的和为:262144,即:图片512 * 512的值,表示模型共预测了512 * 512个像素点的类别
步骤三:评估指标计算
在这里插入图片描述

【语义分割】评价指标代码函数:np.sum()、np.nansum()、np.nanmean()、np.diag()、np.bincount()

参考 : https://blog.csdn.net/sinat_29047129/article/details/103656465

综合代码:


"""
refer to https://github.com/jfzhang95/pytorch-deeplab-xception/blob/master/utils/metrics.py
"""
import numpy as np
import cv2__all__ = ['SegmentationMetric']"""
confusionMetric  # 注意:此处横着代表预测值,竖着代表真实值,与之前介绍的相反
P\L     P    N
P      TP    FP
N      FN    TN
"""class SegmentationMetric(object):def __init__(self, numClass):self.numClass = numClassself.confusionMatrix = np.zeros((self.numClass,) * 2)  # 混淆矩阵(空)def pixelAccuracy(self):# return all class overall pixel accuracy 正确的像素占总像素的比例#  PA = acc = (TP + TN) / (TP + TN + FP + TN)acc = np.diag(self.confusionMatrix).sum() / self.confusionMatrix.sum()return accdef classPixelAccuracy(self):# return each category pixel accuracy(A more accurate way to call it precision)# acc = (TP) / TP + FPclassAcc = np.diag(self.confusionMatrix) / self.confusionMatrix.sum(axis=1)return classAcc  # 返回的是一个列表值,如:[0.90, 0.80, 0.96],表示类别1 2 3各类别的预测准确率def meanPixelAccuracy(self):"""Mean Pixel Accuracy(MPA,均像素精度):是PA的一种简单提升,计算每个类内被正确分类像素数的比例,之后求所有类的平均。:return:"""classAcc = self.classPixelAccuracy()meanAcc = np.nanmean(classAcc)  # np.nanmean 求平均值,nan表示遇到Nan类型,其值取为0return meanAcc  # 返回单个值,如:np.nanmean([0.90, 0.80, 0.96, nan, nan]) = (0.90 + 0.80 + 0.96) / 3 =  0.89def IntersectionOverUnion(self):# Intersection = TP Union = TP + FP + FN# IoU = TP / (TP + FP + FN)intersection = np.diag(self.confusionMatrix)  # 取对角元素的值,返回列表union = np.sum(self.confusionMatrix, axis=1) + np.sum(self.confusionMatrix, axis=0) - np.diag(self.confusionMatrix)  # axis = 1表示混淆矩阵行的值,返回列表; axis = 0表示取混淆矩阵列的值,返回列表IoU = intersection / union  # 返回列表,其值为各个类别的IoUreturn IoUdef meanIntersectionOverUnion(self):mIoU = np.nanmean(self.IntersectionOverUnion())  # 求各类别IoU的平均return mIoUdef genConfusionMatrix(self, imgPredict, imgLabel):  #"""同FCN中score.py的fast_hist()函数,计算混淆矩阵:param imgPredict::param imgLabel::return: 混淆矩阵"""# remove classes from unlabeled pixels in gt image and predictmask = (imgLabel >= 0) & (imgLabel < self.numClass)label = self.numClass * imgLabel[mask] + imgPredict[mask]count = np.bincount(label, minlength=self.numClass ** 2)confusionMatrix = count.reshape(self.numClass, self.numClass)# print(confusionMatrix)return confusionMatrixdef Frequency_Weighted_Intersection_over_Union(self):"""FWIoU,频权交并比:为MIoU的一种提升,这种方法根据每个类出现的频率为其设置权重。FWIOU =     [(TP+FN)/(TP+FP+TN+FN)] *[TP / (TP + FP + FN)]"""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 FWIoUdef addBatch(self, imgPredict, imgLabel):assert imgPredict.shape == imgLabel.shapeself.confusionMatrix += self.genConfusionMatrix(imgPredict, imgLabel)  # 得到混淆矩阵return self.confusionMatrixdef reset(self):self.confusionMatrix = np.zeros((self.numClass, self.numClass))# 测试内容
if __name__ == '__main__':imgPredict = cv2.imread('1.png')imgLabel = cv2.imread('2.png')imgPredict = np.array(cv2.cvtColor(imgPredict, cv2.COLOR_BGR2GRAY) / 255., dtype=np.uint8)imgLabel = np.array(cv2.cvtColor(imgLabel, cv2.COLOR_BGR2GRAY) / 255., dtype=np.uint8)# imgPredict = np.array([0, 0, 1, 1, 2, 2])  # 可直接换成预测图片# imgLabel = np.array([0, 0, 1, 1, 2, 2])  # 可直接换成标注图片metric = SegmentationMetric(2)  # 2表示有2个分类,有几个分类就填几hist = metric.addBatch(imgPredict, imgLabel)pa = metric.pixelAccuracy()cpa = metric.classPixelAccuracy()mpa = metric.meanPixelAccuracy()IoU = metric.IntersectionOverUnion()mIoU = metric.meanIntersectionOverUnion()print('hist is :\n', hist)print('PA is : %f' % pa)print('cPA is :', cpa)  # 列表print('mPA is : %f' % mpa)print('IoU is : ', IoU)print('mIoU is : ', mIoU)

输出:

hist is :[[  43466.   11238.][  11238. 2582058.]]
PA is : 0.991512
cPA is : [0.79456712 0.99566652]
mPA is : 0.895117
IoU is :  [0.65915502 0.99137043]
mIoU is :  0.8252627241326803

注:文中引用较多,忘记大多引用文档,如有被引用,可评论联系标明出处;共同进步!

这篇关于语义分割的评价指标——PA(像素准确率)、CPA(类别像素准确率)、MPA(类别平均像素准确率)、IoU(交并比)、MIoU(平均交并比)详细总结的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/168994

相关文章

Rust格式化输出方式总结

《Rust格式化输出方式总结》Rust提供了强大的格式化输出功能,通过std::fmt模块和相关的宏来实现,主要的输出宏包括println!和format!,它们支持多种格式化占位符,如{}、{:?}... 目录Rust格式化输出方式基本的格式化输出格式化占位符Format 特性总结Rust格式化输出方式

Java集合中的List超详细讲解

《Java集合中的List超详细讲解》本文详细介绍了Java集合框架中的List接口,包括其在集合中的位置、继承体系、常用操作和代码示例,以及不同实现类(如ArrayList、LinkedList和V... 目录一,List的继承体系二,List的常用操作及代码示例1,创建List实例2,增加元素3,访问元

SpringBoot整合easy-es的详细过程

《SpringBoot整合easy-es的详细过程》本文介绍了EasyES,一个基于Elasticsearch的ORM框架,旨在简化开发流程并提高效率,EasyES支持SpringBoot框架,并提供... 目录一、easy-es简介二、实现基于Spring Boot框架的应用程序代码1.添加相关依赖2.添

Java调用DeepSeek API的最佳实践及详细代码示例

《Java调用DeepSeekAPI的最佳实践及详细代码示例》:本文主要介绍如何使用Java调用DeepSeekAPI,包括获取API密钥、添加HTTP客户端依赖、创建HTTP请求、处理响应、... 目录1. 获取API密钥2. 添加HTTP客户端依赖3. 创建HTTP请求4. 处理响应5. 错误处理6.

Spring AI集成DeepSeek的详细步骤

《SpringAI集成DeepSeek的详细步骤》DeepSeek作为一款卓越的国产AI模型,越来越多的公司考虑在自己的应用中集成,对于Java应用来说,我们可以借助SpringAI集成DeepSe... 目录DeepSeek 介绍Spring AI 是什么?1、环境准备2、构建项目2.1、pom依赖2.2

Goland debug失效详细解决步骤(合集)

《Golanddebug失效详细解决步骤(合集)》今天用Goland开发时,打断点,以debug方式运行,发现程序并没有断住,程序跳过了断点,直接运行结束,网上搜寻了大量文章,最后得以解决,特此在这... 目录Bug:Goland debug失效详细解决步骤【合集】情况一:Go或Goland架构不对情况二:

Python itertools中accumulate函数用法及使用运用详细讲解

《Pythonitertools中accumulate函数用法及使用运用详细讲解》:本文主要介绍Python的itertools库中的accumulate函数,该函数可以计算累积和或通过指定函数... 目录1.1前言:1.2定义:1.3衍生用法:1.3Leetcode的实际运用:总结 1.1前言:本文将详

Deepseek R1模型本地化部署+API接口调用详细教程(释放AI生产力)

《DeepseekR1模型本地化部署+API接口调用详细教程(释放AI生产力)》本文介绍了本地部署DeepSeekR1模型和通过API调用将其集成到VSCode中的过程,作者详细步骤展示了如何下载和... 目录前言一、deepseek R1模型与chatGPT o1系列模型对比二、本地部署步骤1.安装oll

Spring Boot整合log4j2日志配置的详细教程

《SpringBoot整合log4j2日志配置的详细教程》:本文主要介绍SpringBoot项目中整合Log4j2日志框架的步骤和配置,包括常用日志框架的比较、配置参数介绍、Log4j2配置详解... 目录前言一、常用日志框架二、配置参数介绍1. 日志级别2. 输出形式3. 日志格式3.1 PatternL

Springboot 中使用Sentinel的详细步骤

《Springboot中使用Sentinel的详细步骤》文章介绍了如何在SpringBoot中使用Sentinel进行限流和熔断降级,首先添加依赖,配置Sentinel控制台地址,定义受保护的资源,... 目录步骤 1: 添加 Sentinel 依赖步骤 2: 配置 Sentinel步骤 3: 定义受保护的