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

news2024/10/10 6:13:36

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.coloradmin.cn/o/993822.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【VS Code插件开发】常见自定义命令(七)

🐱 个人主页:不叫猫先生,公众号:前端舵手 🙋‍♂️ 作者简介:前端领域优质作者、阿里云专家博主,共同学习共同进步,一起加油呀! 📢 资料领取:前端…

java包装类简单认识泛型

1 包装类 在 Java 中,由于基本类型不是继承自 Object ,为了在泛型代码中可以支持基本类型, Java 给每个基本类型都对应了一个包装 类型。类中比如由属性/方法 使用比较方便 1.1 基本数据类型和对应的包装类 1.2 装箱和拆箱 装包/装箱 : …

Linux使用docker安装elasticsearch-head

一、elasticsearch-head的安装启动 #下载镜像 docker pull alivv/elasticsearch-head #启动 docker run -d --name eshead -p 9100:9100 alivv/elasticsearch-head 查看日志 docker logs -f eshead 出现如下证明启动成功 浏览器访问9100端口,出现以下页面也说明…

java 线程安全问题 三种线程同步方案 线程通信(了解)

线程安全问题 线程安全问题指的是,多个线程同时操作同一个共享资源的时候,可能会出现业务安全问题。 下面代码演示上述问题,先定义一个共享的账户类: public class Account {private String cardId; // 卡号private double mone…

分布式版本控制工具——git

✅<1>主页&#xff1a;&#xff1a;我的代码爱吃辣 &#x1f4c3;<2>知识讲解&#xff1a;Linux——git ☂️<3>开发环境&#xff1a;Centos7 &#x1f4ac;<4>前言&#xff1a;git是一个开源的分布式版本控制系统&#xff0c;可以有效、高速地处理从很…

路由缓存问题 | vue-router的导航守卫

路由缓存问题 带参路由&#xff0c;当参数发生变化时&#xff0c;相同的组件实例将被复用&#xff0c;组件的生命周期钩子不会被调用&#xff0c;导致数据无法更新。 两种解决方法&#xff1a; 1. 给 RouterView绑定key值&#xff0c;即 <RouterView :key"$route.ful…

SpringBoot原理-自动配置-原理分析-源码跟踪

自动配置原理 SpringBootApplication 该注解标识在SpringBoot项目的启动类上&#xff0c;是SpringBoot中最为重要的注解&#xff0c;该注解由三个部分组成。 SpringBootConfiguration&#xff1a;该注解与Configuration注解作用一样&#xff0c;用来声明当前类为一个配置类Comp…

解决SVN文件不显示绿色小钩图标问题

问题描述&#xff1a; 今天重新安装了SVN&#xff0c;发现从中央服务器拉取文件到本地仓库后&#xff0c;对应的文件没有绿色的小钩图标&#xff0c;于是查了一下解决方案&#xff0c;在这里总结一下。 解决方案一&#xff1a; 原因&#xff1a;状态缓存设置问题造成的。 在…

2024年java面试--mysql(1)

系列文章目录 2024年java面试&#xff08;一&#xff09;–spring篇2024年java面试&#xff08;二&#xff09;–spring篇2024年java面试&#xff08;三&#xff09;–spring篇2024年java面试&#xff08;四&#xff09;–spring篇2024年java面试–集合篇2024年java面试–redi…

Java设计模式-结构性设计模式(代理设计模式)

简介 为其他对象提供⼀种代理以控制对这个对象的访问&#xff0c;属于结构型模式。客户端并不直接调⽤实际的对象&#xff0c;⽽是通过调⽤代理&#xff0c;来间接的调⽤实际的对象应用场景 各⼤数码专营店&#xff0c;代理⼚商进⾏销售对应的产品&#xff0c;代理商持有真正的…

算法训练营day46|动态规划 part08:完全背包 (LeetCode 139. 单词拆分、多重背包理论基础)

文章目录 139. 单词拆分 (求排列方法)回溯思路分析背包思路分析代码实现思考总结 多重背包理论基础 139. 单词拆分 (求排列方法) 题目链接&#x1f525;&#x1f525; 给定一个非空字符串 s 和一个包含非空单词的列表 wordDict&#xff0c;判定 s 是否可以被空格拆分为一个或多…

一文读懂LSTM及手写LSTM结构

torch.nn.LSTM是PyTorch中用于创建长短时记忆网络&#xff08;Long Short-Term Memory&#xff09;的类。LSTM是一种用于处理序列数据的循环神经网络&#xff08;Recurrent Neural Network&#xff0c;RNN&#xff09;变体。 官方给出的LSTM API 文档 以下是 torch.nn.LSTM 的…

LORA项目源码解读

大模型fineturn技术中类似于核武器的LORA&#xff0c;简单而又高效。其理论基础为&#xff1a;在将通用大模型迁移到具体专业领域时&#xff0c;仅需要对其高维参数的低秩子空间进行更新。基于该朴素的逻辑&#xff0c;LORA降低大模型的fineturn门槛&#xff0c;模型训练时不需…

Redis-带你深入学习数据类型list

目录 1、list列表 2、list相关命令 2.1、添加相关命令&#xff1a;rpush、lpush、linsert 2.2、查找相关命令&#xff1a;lrange、lindex、llen 2.3、删除相关命令&#xff1a;lpop、rpop、lrem、ltrim 2.4、修改相关命令&#xff1a;lset 2.5、阻塞相关命令&#xff1a…

deepin V23通过flathub安装steam畅玩游戏

deepin V23缺少32位库&#xff0c;在星火商店安装的steam,打开报错&#xff0c;无法使用&#xff01; 通过flathub网站安装steam,可以正常使用&#xff0c;详细教程如下&#xff1a; flathub网址&#xff1a;主页 | Flathub 注意&#xff1a;flathub下载速度慢&#xff0c;只…

【笔试强训选择题】Day38.习题(错题)解析

作者简介&#xff1a;大家好&#xff0c;我是未央&#xff1b; 博客首页&#xff1a;未央.303 系列专栏&#xff1a;笔试强训选择题 每日一句&#xff1a;人的一生&#xff0c;可以有所作为的时机只有一次&#xff0c;那就是现在&#xff01;&#xff01; 文章目录 前言一、Day…

ChatGPT实战与私有化大模型落地

文章目录 大模型现状baseline底座选择数据构造迁移方法评价思考 领域大模型训练技巧Tokenizer分布式深度学习数据并行管道并行向量并行分布式框架——Megatron-LM分布式深度学习框架——Colossal-AI分布式深度学习框架——DeepSpeedP-tuning 微调 资源消耗模型推理加速模型推理…

基于SSM的学院实验中心管理系统

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用JSP技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

从数据页的角度看 B+Tree

InnoDB 是如何存储数据的&#xff1f; MySQL支持多种存储引擎&#xff0c;不同的存储引擎&#xff0c;存储数据的方式也不相同&#xff0c;我们最常使用的是 InnoDB 存储引擎。 在数据库中的记录是按照行来存储的&#xff0c;但是数据库的读取并不是按照 [ 行] 为单位&#x…

MySQL进阶 —— 超详细操作演示!!!(上)

MySQL进阶 —— 超详细操作演示&#xff01;&#xff01;&#xff01;&#xff08;上&#xff09; 一、存储引擎1.1 MySQL 体系结构1.2 存储引擎介绍1.3 存储引擎特点1.4 存储引擎选择 二、索引2.1 索引概述2.2 索引结构2.3 索引分类2.4 索引语法2.5 SQL 性能分析2.6 索引使用2…