图像分割中的混淆矩阵和利用混淆矩阵计算指标

目录

1. 介绍

2. 创建混淆矩阵

2.1 update 方法

2.2 compute 方法

2.3 str 方法

3. 测试

4. 完整代码


1. 介绍

语义分割中,性能指标可以利用混淆矩阵进行计算

这里实现的方法和图像分类中不一样,需要的可以参考:混淆矩阵Confusion Matrix

扫描二维码关注公众号,回复: 14868287 查看本文章

这里采用的测试数据如下:

2. 创建混淆矩阵

混淆矩阵的实现如下

init 是初始化混淆矩阵

update 更新混淆矩阵的数值

reset 将矩阵的值清零

compute 根据update 算出来的混淆矩阵计算相应的性能指标

str 是返回的字符串,就是实例化混淆矩阵后,print的值

这里讲解混淆矩阵类的测试数据都是上面的:

2.1 update 方法

如下:

a传入的是真实的label,b传入的是网络预测的值,注:这里的预测值也是和label一样的整型数组

首先通过init初始化方法将混淆矩阵的大小赋值给n(分类类别的个数+1 背景),然后创建混淆矩阵mat,先初始化为0

接下来,k在真正标签a中找到对应的索引,

这里目的是为了将不感兴趣的区域设为False,其余的具体分割标签应该为1,2,3这样的排序方式。因为通常0为背景,255为不感兴趣的区域。

例如,当分割类别为2(1,2),那么加上背景传入的n就是3(0,1,2),同时不感兴趣的区域设置为255(0,1,2,255)。那么在k在真实标签a(0,1,2,255)中的索引,就会将a>=0 & a<n ,也就是0,1,2 的区域设置为True,从而满足了分割要求且拍出来255不感兴趣的

所以,dataset加载数据中,要将前景从1,2,3这样排序

然后,通过下面的操作就能将横坐标为true,纵坐标为pred的混淆矩阵update

中间的inds大概是将a和b变为一维向量,那么n*a就会将一维向量变为n个为一组的样子,然后在里面进行计算,最后reshape成n*n的矩阵就行了。具体的可以自己调试一下

 例如,true = 1,pred = 0 的个数是一个,同样在混淆矩阵的值也是1(第0行,第1列)

2.2 compute 方法

compute 是利用update产生的混淆矩阵,计算分割任务中的性能指标,关于分割任务的性能指标,可以查看:关于语义分割常见的评价指标

混淆矩阵:横坐标为true,纵坐标为pred

像素准确率 = 混淆矩阵对角线 / 混淆矩阵的sum

acc 这里是指各个类别的召回率 = 各个对角线的值 / 真实值(矩阵的行为ture,所以对行求和)

recall 召回率就是在...召回的个数,...就是label,召回的个数就是预测正确的个数。所以召回率就是在label中,预测正确个数的占比

iou 就是各个对角线的值 / (对应行 + 对应列 - 重复的对角线的值)

2.3 str 方法

python类中str方法,是返回实例化类的print的值

因此混淆矩阵类中的str方法返回的是compute计算出的性能指标。

因为这里的str方法自动调用了compute,而compute是根据update计算的。所以调用str之前,一定要先调用update方法,更新混淆矩阵的值

这里的recall和iou都是针对不同类别的,所以返回是个列表

3. 测试

测试的代码如下:

 测试的样本为:

这里手动计算分割的参数,验证混淆矩阵

首先是像素准确率:4 / 9 = 0.4444

然后是各个类别的召回率:这里是三个类别0 1 2 

然后是iou:

对于0:1 / 3 =0.3333

对于1:1 / 6 =0.1667

对于2:2 / 5 =0.4

最后mean iou就是iou的均值:(0.3333+0.1667+0.4) / 3 = 0.9 / 3 = 0.3

4. 完整代码

混淆矩阵的代码:

import torch


# 混淆矩阵
class ConfusionMatrix(object):
    def __init__(self, num_classes):
        self.num_classes = num_classes      # 分类个数(加了背景之后的)
        self.mat = None         # 混淆矩阵

    def update(self, a, b):      # 计算混淆矩阵,a = Ture,b = Predict
        n = self.num_classes
        if self.mat is None:         # 创建混淆矩阵
            self.mat = torch.zeros((n, n), dtype=torch.int64, device=a.device)
        with torch.no_grad():
            k = (a >= 0) & (a < n)
            inds = n * a[k].to(torch.int64) + b[k]      # 统计像素真实类别a[k]被预测成类别b[k]的个数(这里的做法很巧妙)
            self.mat += torch.bincount(inds, minlength=n**2).reshape(n, n)

    def reset(self):
        if self.mat is not None:
            self.mat.zero_()

    def compute(self):      # 计算分割任务的性能指标
        h = self.mat.float()

        acc_global = torch.diag(h).sum() / h.sum()     # 计算全局预测准确率(混淆矩阵的对角线为预测正确的个数)
        acc = torch.diag(h) / h.sum(1)                 # 计算每个类别的 recall
        iou = torch.diag(h) / (h.sum(1) + h.sum(0) - torch.diag(h))     # 计算iou
        return acc_global, acc, iou

    def __str__(self):
        acc_global, acc, iou = self.compute()
        return (
            'global correct: {:.4f}\n'
            'recall: {}\n'
            'IoU: {}\n'
            'mean IoU: {:.4f}').format(
            acc_global.item() ,
            ['{:.4f}'.format(i) for i in acc.tolist()],
            ['{:.4f}'.format(i) for i in iou.tolist()],
            iou.mean().item())

测试的代码:

confmat = ConfusionMatrix(num_classes=3)    # 实例化混淆矩阵

ture = torch.LongTensor([[1,2,1],[0,2,2],[0,1,1]])
pred = torch.LongTensor([[1,2,0],[1,2,1],[0,2,2]])

confmat.update(ture, pred)  # update 混淆矩阵的值
print(confmat)
'''
global correct: 0.4444
recall: ['0.5000', '0.2500', '0.6667']
IoU: ['0.3333', '0.1667', '0.4000']
mean IoU: 0.3000
'''

猜你喜欢

转载自blog.csdn.net/qq_44886601/article/details/130127502
今日推荐