MIOU的计算

MIOU,即平均交并比(Mean Intersection over Union),是评价语义分割模型性能的一个重要指标。它计算的是预测结果中每一类结果的交并比的平均值。假设绿色为真实的标签值,粉红色为预测的结果。

所谓交并比,就是这两种数据的相交元素数量/两种数据的并集的元素数量,如下图所示:

下面我们来一下MIOU的计算方法。

1. IOU的计算

要计算平均交并比,就要首先计算每一类的交并比。交并比(Intersection over Union)是衡量预测值与真实值相似度的一个指标。第i类IOU的计算公式如下:

其中,TP代表真正例,即某类别i的像素正确预测为该类别的数量;

FP代表假正例,即非类别i的像素预测为该类别的数量;

FN代表假负例,即类别i的像素预测为非该类别的数量;

获取到这几个值之后就可以计算出交并比IOU了。

2. 混淆矩阵的计算

混淆矩阵是一个n*n的矩阵(n是类别数),用于表示实际类别与预测类别之间的关系。混淆矩阵的对角线元素表示正确预测的数量,而其他元素表示错误预测的数量。根据周志华老师的《机器学习》,混淆矩阵如下:

根据混淆矩阵,可以很方便的计算出TP,FN和FP,进而计算出IOU和MIOU。

混淆矩阵的计算,有好几种算法,我这里选了三种方法介绍一下。

第一种是网上很流行的numpy.bincount方法:

numpy.bincount是一个计算某个数组中,索引出现次数的函数。代码举例会更清楚。

import numpy as np

a = [4,5,4,1,3,5,2] 
np.bincount(a)
# 输出:array([0, 1, 1, 1, 2, 2], dtype=int64)

这里,最大值是5,所以类别是0~6之间的6类,返回6个结果,每个位置代表该位置索引出现的次数。这里索引0出现0次,索引1在a中出现1次,索引2在a中出现1次,索引3在a中出现1次,索引4在a中出现2次,索引5在a中出现2次,所以结果是[0,1,1,1,2,2]。

bincount有一个参数minlength,minlength是为了强行设定类别数,比如数组a的最大值只是到5,但是我认为类别数有8类,那么我就可以设定minlength=8,结果当然是后面几类的索引值都是0了。

np.bincount(a, minlength=8)
# 输出:array([0, 1, 1, 1, 2, 2, 0, 0], dtype=int64)
'''
得到n*n的混淆矩阵
a:真实标签label
b:预测结果pred
n:类别数
'''
def confusion_matrix(a, b, n):
    #k就是为了防止出错的
    k = (a >= 0) & (a < n) 
    return np.bincount(n * a[k].astype(int) + b[k], minlength=n**2).reshape(n, n)

这个函数中,k只是为了防止运算出错,其实如果a,b是标签值和预测的结果的话,k可以不需要。这里首先把结果扩充到n * n大小,最后再reshape到n * n,就是为了生成一个n * n的矩阵。而其中的值用n乘上a的值,再加上b的值,其实仅仅是为了计算方便而已。假设a是0,b也是0,那么a乘上n后还是0,加上b还是0,所以索引0就会存在,如果a和b不同,则会有不同的值出现,也就是混淆矩阵其他位置上的值。假设a是1,n是3,那么n * a还是3,b如果是0,那就是和就是3,b如果是1,那和就是4。

这么说可能不大好理解,我举个实际例子,假设有个a和b,类别是3类,用0,1,2表示。

a = np.array([0,1,0,2,1,0,2,2,1])
b = np.array([0,2,0,2,1,0,1,2,1])

这里有三类,0,1,2,所以n=3,minlength=9,我们可以手动计算一下这9个值,根据函数中的算法: [3 * 0 + 0, 3 * 1 + 2, 3 * 0 + 0, 3 * 2 + 2, 3 * 1 + 1, 3 * 0 + 0, 3 * 2 + 1, 3 * 2 + 2, 3 * 1 + 1] ,结果就是 [0, 5, 0, 8, 4, 0, 7, 8, 4] ,

再做一个bincount就是 [3, 0, 0, 0, 2, 1, 0, 1, 2] ,转成3 * 3的矩阵,就是 [[3,0,0] [0,2,1] [0,1,2]] 这个就是混淆矩阵。用程序验证一下。

confusion_matrix(a,b,3)
#输出:
array([[3, 0, 0],
       [0, 2, 1],
       [0, 1, 2]], dtype=int64)

和我们自己计算的结果一致。

第二种计算混淆矩阵的方法,就比较直观,统计出标签值和预测值的各种结果的总和数,就是混淆矩阵,直接上代码:

import numpy as np  
  
def confusion_matrix_np(y_true, y_pred):  
    """  
    使用NumPy计算混淆矩阵。  
  
    参数:  
    - y_true: 真实标签的数组。  
    - y_pred: 预测标签的数组。  
  
    返回:  
    - 混淆矩阵的NumPy数组。  
    """  
    # 确保y_true和y_pred是NumPy数组  
    y_true = np.asarray(y_true)  
    y_pred = np.asarray(y_pred)  
  
    # 检查y_true和y_pred的长度是否相同  
    if y_true.shape[0] != y_pred.shape[0]:  
        raise ValueError("y_true和y_pred的长度必须相同")  
  
    labels = np.unique(np.concatenate((y_true, y_pred)))  
  
    # 初始化混淆矩阵  
    n_labels = len(labels)  
    conf_mat = np.zeros((n_labels, n_labels), dtype=np.int64)  
  
    # 填充混淆矩阵  
    for i, l1 in enumerate(labels):  
        for j, l2 in enumerate(labels):  
            # 混淆矩阵每个元素的值就是该位置标签值和预测值符合条件的总数
            # 比如:conf_mat[0,0]就是真实值和预测值都是0的总数,
            # conf_mat[0,1]就是真实标签值是1,但预测值是0的元素总数
            conf_mat[i, j] = np.sum((y_true == l1) & (y_pred == l2))  
  
    return conf_mat
cm = confusion_matrix_np(a, b)  
print(cm)
# 输出
[[3 0 0]
 [0 2 1]
 [0 1 2]]

这里可以看到,结果和我们之前用bincount计算的结果一致。

第三种方法最简单,直接安装一个sklearn包,用sklearn提供的计算混淆矩阵的函数计算得到。

from sklearn.metrics import confusion_matrix

cm = confusion_matrix(a, b)  
print(cm)
# 输出:
[[3 0 0]
 [0 2 1]
 [0 1 2]]

可以看到,结果也是一样的。

有个混淆矩阵,我们可以计算IOU了。

IOU的计算,在混淆矩阵中,就是把该类所在行的值列的值求和,然后减去对角线上的值,因为对角线上的值在求和的时候会被计算两次,所以要减去一次,然后用对角线的值去除以这个和就是IOU的值。下面是计算过程:

第一类的IOU:3 / ((3+0+0)+(3+0+0)-3) = 1

第二类的IOU:2 / ((0+2+1)+(0+2+1)-2) = 0.5

第三类的IOU:2 / ((0+1+2)+(0+1+2)-2) = 0.5

如果用程序的话,我们可以一次性计算出来,其实就是做了一个批处理,本质上和我们的计算过程是一样的:

def iou(cm):
    return np.diag(cm) / (cm.sum(0) + cm.sum(1) - np.diag(cm))
    
iou(cm)
#输出:array([1. , 0.5, 0.5])

有了各个类别的IOU,就可以很方便的求得MIOU了。

miou = np.mean(iou(cm))
miou
# 输出:0.6666666666666666

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值