目标检测评估指标mAP:从Precision,Recall,到AP50-95

1. TP, FP, FN, TN

True Positive

满足以下三个条件被看做是TP

        1. 置信度大于阈值(类别有阈值,IoU判断这个bouding box是否合适也有阈值)

        2. 预测类型与标签类型相匹配(类别预测对了)

        3. 预测的Bouding Box和Ground Truth的IoU大于阈值(框 打对了)。 当存在多个满足条件的预选框,则选择置信度最大的作为TP,其余的作为FP

False Positive

       1. 预测的 类别  和 真实的标签类型不匹配(分类错误)

        2. 预测的Bounding box和Ground Truth的IoU小于阈值(框  打的不是那么好, 定位错误)

False Negative

        分类争取,定位正确,但是被检测成了负样本

True Negative

        负样本被检测出的数量,太多了,绝大多数的框都是这个类型。后面计算precision和recall的时候用不到,所以这个东西也就不统计数量了。

2. Precision和Recall

Precision = TP/(TP+FP) = TP/(所有被我判定为正例的个数)

Recall = TP/(TP+FN) = TP/(所有世界是正例的个数)

3. PR曲线

Precision-Recall曲线是根据阈值 从0到1这个区间的变动,每个阈值下模型的precision和recall的值分别作为纵坐标和横坐标来连线绘制而成的。(每个阈值θ对应于一个(Precision,Recall)点,把这些点连起来就是PR曲线)

比如假设我们收集了20个sample的数据,他们的真实标签和置信度如下

 此时我们为了绘制PR曲线,计算出了PR曲线上下面这些点的坐标

阈值=0.9——TP=len([ #1, ]) = 1; FP=0; FN=len([#2, #4, #5, #6, #9, #11, #13, # 17, #19])=9——Precision=TP/(TP+FP)=1/(1+0)=1——Recall=TP/(TP+FN)=1/(1+9)= 0.1

阈值0.8——TP=len([#1,#2])=2; FP=0; FN=len([#4, #5, #6, #9, #11, #13, # 17, #19])=8——Precision=2/(2+0)=1; Recall=2/(2+8)=0.2

阈值0.7——TP=len([#1,#2])=2; FP=len([#3])=1, #_of_True=10 ——Precision=2/(2+1)=0.67;Recall=2/10=0.2

阈值0.6——TP=len([#1, #2,#4])=3FP=len([#3])=1, #_of_True=10——Precision=3/(3+1)=0.75; Recall = 3/10=0.3

阈值0.5——TP=len([#1, #2, #4, #5, #6, #9])=6FP=len([#3, #7, #8, #10])=4,#_of_True=10——Precision=6/(6+4)=0.6; Recall = 6/10=0.6

阈值0.4——TP=len([#1, #2,#4, #5, #6, #9,  #11])=7; FP=len([#3, #7, #8, #10])=4, #_of_True=10——Precision=7/(7+4)=0.64; Recall = 7/10=0.7

阈值0.3——TP=len([#1, #2,#4, #5, #6, #9,  #11, #13, #17, #19])=10;FP=len([#3, #7, #8, #10, #12, #14, #15, #16, #18])=9; #_of_True=10; ——Precision=10/(10+9)=0.53; Recall = 10/10=1

用sklearn的下面这段代码就可以计算出答案

import numpy as np
from sklearn.metrics import precision_recall_curve

# 导入数据
y_true = np.array([1,1,0,1,1,1,0,0,1,0,1,0,1,0,0,0,1,0,1,0])
y_scores = np.array([0.9,0.8,0.7,0.6,0.55,0.54, 0.53,0.52,0.51,0.505, 0.4, 0.39,0.38, 0.37, 0.36, 0.35, 0.34, 0.33, 0.30, 0.1])

# 计算出每个阈值下,precision和recall的值
precision, recall, thresholds = precision_recall_curve(y_true, y_scores)

# 写上这两行,中间的列就不会被用省略号省略,都显示出来
pd.options.display.max_rows = None
pd.options.display.max_columns = None


#整理成横向的dataframe,方便大家查看
precision = pd.DataFrame(precision).T
recall = pd.DataFrame(recall).T
thresholds= pd.DataFrame(thresholds).T
#纵向拼接
results = pd.concat([thresholds, recall,precision], axis=0)
# 仅仅保留2位小数
results = round(results, 2)
#行名改一下
results.index = ["thresholds", "recall", "precision"]
print(results)

绘制出来的precision-recall曲线是下面这样

import matplotlib.pyplot as plt
def plot_pr_curve(recall, precision):
    plt.plot(recall, precision, label='Precision-Recall curve')
    plt.xlabel('Recall')
    plt.ylabel('Precision')
    plt.title('PR Curve')
    plt.legend()
plot_pr_curve(recall, precision)

对于PR图中这种类似锯齿状的图形,我们一般采用平滑锯齿的操作。所谓平滑锯齿操作就是在Recall轴上,对于每个阈值θ计算出的Recall点,看看它的右侧(包含它自己)谁的Precision最大,然后这个区间都使用这个Precision值,在下图这个例子中,Recall=(0,0.4] 都使用 Precision=1, Recall=(0.4,0.8] 都使用 Precision=0.57, Recall=(0.8,1) 都使用 Precision=0.5

因此我们这个例子中的图形经过锯齿平滑化以后应该是下面这个样子

Precision和Recall之间的此消彼长的矛盾关系

        上图的右下角,recall高,说明所有所有的杀人犯中99%的都被抓住了,那就会造成一个结果,抓的人特别多。和这桩杀人案有一丁点关系的人都被逮捕了。这必然导致,所有被抓的人中,实际是罪犯的比例变得很低,也就是precision低。

        OK,如果你希望Police抓的人中,实际是罪犯的比例高一些,(也就是precision高一些)。那Police就不敢乱抓人了,没有十足的证据就不敢去逮人。那结果是啥?那就是大量隐藏额的很好的罪犯都被漏掉了,也就是说所有的罪犯中实际被抓住的比例降低了。也就是recall低了。这种情形对应的就是上图左上角的那个位置。

如何评估一个不同模型的好坏关系

        既然一个模型的precision和recall是此消彼长的关系,不可能两个同时大,那怎么判断哪个模型更优呢?答案是,P-R曲线越往右上角凸起的曲线对应的模型越优秀。正如上图中红线A和黑线B都是比模型C要优秀的

        但是问题来了,模型A和模型B之间,孰优孰劣呢?那就引出了平衡点("Balanced Error Point" (BEP))这个概念。平衡点就是曲线上的Precision值=Recall值的那个点,也就是上图中那三个点,平衡点的坐标越大,模型越好。

        除了平衡点,也可以用F1 score来评估。F1 = 2 * P * R /( P + R )。F1-score综合考虑了P值和R值,是精准率和召回率的调和平均值, 同样,F1值越大,我们可以认为该学习器的性能较好。

4. 从PR曲线到AP

AP的公式如下

\text{AP} = \sum_n (R_n - R_{n-1}) P_n

AP这个指标的实际意义:

        AP是,在“”阈值不同“”引发的“Recall值不同”的情况下,各种Precision值的均值。AP summarizes a precision-recall curve as the weighted mean of precisions achieved at each threshold, with the increase in recall from the previous threshold used as the weight:

        AP值可以理解为PR曲线向下、向左到X轴和Y轴这片面积之和

        这个阈值到前一个阈值之间的差是长方形的宽,precision值是长方形的高。宽乘以高就是长方形柱子的面积。

        AP这就是P-R曲线的积分,也就是面积

用sklearn的公式这样算

from sklearn.metrics import average_precision_score
AP = average_precision_score(y_true, y_scores, average='macro', pos_label=1, sample_weight=None)
print(AP)

# 0.7357475805927818

AP=(0.2-0)× 1 + (0.5-0.2)× 0.83 + (0.6-0.5) × 0.67 + (0.7-0.6)×0.64 +(0.8-0.7)×0.62 +(1-0.8)×0.53=0.7480

和上面那个程序算出来来的有0.1的误差,但是大体区别不大就不去追究是哪个小地方算错了

5. 从AP到mAP

把各个类别(比如汽车、行人、巴士、自行车)的AP值取平均

6. 从mAP到 mAP50, mAP75, mAP95

mAP中也有个阈值, mAP50这个也有IoU阈值,这两个阈值之间是什么关系?有哪些区别

        mAP里面那个阈值,是指的分类问题上的阈值(即判断这个目标是人还是汽车还是摩托车 这个分类),如拿着预测某个类别(如:汽车)的置信度  和这个IoU阈值比较,如果置信度大于阈值,则predicted label是True;反之如果predicted confidence低于这个IoU阈值,则predicted label是False

        mAP50里面这个IoU指的是定位(打bouding box识别框) 里面 这个定位问题的阈值。 判断predicted bbox和ground truth bbox之间的IoU大于   这个IoU阈值   才能被算作  定位上的True。反之,如果predicted bbox和ground truth bbox之间的IoU 小于 这个IoU阈值  定位上 这两个框之间的匹配关系 就是False

        在mAP50里面,这个阈值是针对 定位问题的    交并比(IoU)     的。mAP中的阈值是针对分类问题的。

所以这里的mAP50, mAP75, mAP95里面这些50、75、95是针对 定位的bouding box而言的,阈值分别为0.50、0.75、0.95。

我们在目标检测中常说的mAP实际指的是mAP50-95。这个mAP50-95是mAP阈值为50%到mAP阈值为95,间隔5%,取得10个mAP值,然后对这十个值取平均。后面是这10个IoU阈值[0.5 0.55 0.6 0.65 0.7 0.75 0.8 0.85 0.9 0.95]

        mAP50的阈值最低,要求最宽松,因此一般用于人脸识别的性能评估

        mAP80到mAP95,要求比较严格,因此多用于一些对准确性和安全性要求比较高的陈景中如自动驾驶

7. 目标检测中mAP50-95是如何计算得出的

Step1 拿到原始数据

定位数据(bouding box)

        真实标签 truth label

                下面这个矩阵是一张图片的数据

                第一列数据,t代表 truth,表示有真实标签中有N个,一行就是一条真实的标注

                第二列表示是的是这一个方框里面的object  的 真实类别 

                第三列和第四列是, 真实的bouding box 左下角点的横坐标 和 纵坐标

                第五列和第六列是,真实bouding box右上角点的横坐标和纵坐标

                

                通过这这些数据,图片中的N个object的位置和类别就都表示出来了

        你的模型预测的标签 predicted label

                这里仍然是一张照片上所有的bouding box的数据和对object类别的分类结果

                与true label不同的是,这里的第三列 score是true label所没有的。这就是对于一条数据所框处的这个object属于这个类别的置信度

                ground truth对这张图片打了N个方框,predicted 对这张图片打了M个方框。

Step2 定位  构建IoU_mask

这一步专注在定位(打方框)这里

Step2.1 计算真实框和预测框之间的IoU交并比

计算N个真实框和这M个预测边界框之间的IoU值,共有N×M个匹配的IoU值

 Step2.2 根据mAP设定的 IoU 阈值来每个预测框和每个真实框 之间的True或False

假定你的mAP是mAP50,那么IoU阈值就是0.5,上面所有的IoU值 只要大于0.5这个阈值,就会变成True,反之就是False。

iou_mask = (iou_between_target_and_pred > 0.5)

也就是说根据你设定的IoU不同,比如说mAP75, mAP95,同样的IoU值就可能从True变成False或者反过来。因此,不同IoU阈值,下面这掩码矩阵mask matrix都是有些许不同的。

需要注意:

  • 一个预测框可能与多个真实框的 IOU 都大于等于 0.5,比如第一列
  • 一个真实框可能与多个预测框的 IOU 都大于等于 0.5,比如第一行

Step3 分类 构建cls_mask

正如前文讲的mAP是一个分类的评估指标,是Precision-Recall曲线下和X轴、Y轴组成的这篇区域的面积。而Precision-Recall曲线是你设定不同的阈值的情况下,Recall作为横坐标、Precision作为纵坐标形成的点,这些点所连成的曲线。

我们都知道Recall和Precision的计算需要三个东西的个数(1)True Positive, (2)False Negative, (3)False Positive

要拿到这三个数字,你首先得有一些 True和False吧?怎么拿到True和False呢?

        看下面这个矩阵第三列这个分类的置信度,如果这个置信度大于所设定的阈值就用True 来代替这个置信度, 小于阈值就用False来代替这个置信度

          然后你拿着“predicted ”随着阈值改变的这个由True或False组成的,每个框对应类别的True或False, 和“ground truth”的 类别这一列,都是同一个标签,就赋值为True,反之有一点不同就赋值为False

                不管这个predicted的方框是不是距离这个ground truth近,都一一匹配,标注True和False

        这样就拿到了下面这个矩阵cls_mask

Step4 综合定位和分类

Step 5 按照Step4中匹配为True的位置,得到 真实值-预测值 匹配的pairs

目前还不懂的知识

        ROC曲线是什么?表示什么含义?怎么用?

        AUC曲线同样的问题

Reference

PR曲线与ROC曲线_roc曲线和pr曲线_THE@JOKER的博客-CSDN博客

为什么平均精准度(Average Precision,AP)就是PR曲线的线下面积? - Mark Lue的回答 - 知乎 https://www.zhihu.com/question/422868156/answer/1523130474

sklearn.metrics.average_precision_score — scikit-learn 1.3.0 documentation

[CV] 目标检测中的map计算 - 知乎

COCO - Common Objects in Context

https://www.cnblogs.com/ywheunji/p/13376090.html

准确率、召回率和mAP、AP50/75_map和ap50_dagongji10的博客-CSDN博客


http://www.niftyadmin.cn/n/5014216.html

相关文章

Python/C API - 模組,型別,Tuple,例外和引用計數

Python/C API - 模組,型別,Tuple,例外和引用計數 前言Python/C API - Common Object StructuresPyObjectPyMethodDefPyGetSetDef Python/C API - Module ObjectsPyModuleDefPyModule_CreatePyModule_AddObjectPyModule_AddObjectRef Initiali…

深度学习基础之梯度下降

1. 引言 梯度下降是一种用于最小化(或最大化)损失函数的优化算法。它是机器学习和深度学习中的一个关键概念,通常用于调整学习算法中的参数。 梯度下降背后的核心思想是迭代调整参数以最小化损失函数。它的工作原理是计算损失函数相对于每个…

python小题库(三)

大家好呀,今天继续更新python小题库。 题11:实现学生成绩排序 # 问题:实现学生成绩排序 # 如学生的成绩是字典形式 students [{"sno": 101, "sname": "小张", "sgrade": 88},{"sno": 10…

Hydra工具的使用

目录 Hydra初识 Hydra使用 hydra破解mysql 前言 不固定用户名密码爆破 hydra破解ssh 以用户名为密码登录 hydra破解rdp 将爆破密码的结果输出到文件中 Hydra初识 前言: hydra是一款开源的暴力破解工具,支持多种服务破解原理:使用户…

Java(四)数组与类和对象

Java(四)数组与类和对象 六、数组(非常重要)1.定义2.遍历2.1遍历方法2.2Arrays方法 3.二维数组数组小总结 七、类和对象1. 定义(重要)1.1 类1.2 对象 2. this关键字(重要)2.1 特点 3…

vue el-popover hover延时触发,el-popover 鼠标放上三秒以后触发

背景&#xff1a;el-popover hover只要鼠标刮过就显示 多个el-popover出现加载卡顿 解决方案 给el-popover加一个延时显示 <template><div><el-popovertrigger"hover":open-delay"3000"content"这是一个Popover"><button…

80 # 图片防盗链

referer 来源&#xff0c;表示这个资源被谁引用过&#xff0c;可以用来做防盗链。 我们新建文件 no-referer.js const fs require("fs"); const path require("path"); const url require("url"); const http require("http");h…

【TypeScript学习】—编译选项(三)

【TypeScript学习】—编译选项&#xff08;三&#xff09; 一、自动编译文件 tsc xxx.ts -w二、自动编译整个项目 三、编译器选项