目录

参数部分:

tp(correct): shape=[25268, 10] bool

conf: shape=[25268]

pred_cls: shape=[25268]

target_cls: shape=[929]

unique_classes: shape=[71]

n_l && n_p: shape=254 && shape=4488

fpc: shape=[4488,10]

tpc: shape=[4488,10]

recall: shape=[4488,10]

precision: shape=[4488,10]

mpre: shape=[4490]

mrec: shape=[4490]

ap: shape=[71,10]

p: shape=[71,1000]

R: shape=[71,1000]

代码部分:

reference:


整整花了一天的时间在看,原本准备2小时搞懂(太天真了)

参数部分:

 tp(correct): shape=[25268, 10] bool       

含义:整个数据集所有图片中所有预测框在每一个iou条件下(0.5~0.95)10个是否是TP

old_view:

这里的tp中的预测框(每行)是按图片为单位进行存储的(比如说前300行属于第一张图片,300行到500行属于第二张图片)。

new_view:

这里利用np.argsort对conf进行排序(从大到小) 得到新的tp。

 conf: shape=[25268]        

含义:整个数据集所有图片的所有预测框的conf

old_view:

同理这里也是按图片为单位进行存储的。

new_view:

这是经过排序后的conf。

pred_cls: shape=[25268]

含义:整个数据集所有图片的所有预测框的类别

old_view:

同理这里也是按图片为单位进行存储的。

new_view:    

同理,这是经过conf排序后的类别。

注意:这里的tp、conf、pred_cls是一一对应的

target_cls: shape=[929]

含义:整个数据集所有图片的所有gt框的类别

view:

unique_classes: shape=[71]

含义:整个数据集中一共包含了71个类别(CoCo128中)

view:

n_l && n_p: shape=254 && shape=4488

含义:整个数据集gt框中c类别的框数目;所有预测框中c类别的框数目

view:

fpc: shape=[4488,10]

含义:匹配tp中类别为c 顺序按置信度从大到小排列 截至到每一个预测框的各个iou阈值下FP个数 最后一行表示c类在该iou阈值下所有FP个数。

view:

由于使用np.cumsum(),所以对每一行进行累加操作,又由于conf从大到小排序,导致经过最后置信度低的预测框fp的个数基本都会增加。使得view呈现从左到右依次增加,从上到下依次增加的趋势。

tpc: shape=[4488,10]

含义:匹配tp中类别为c 顺序按置信度排列,截至到每一个预测框的各个iou阈值下TP个数 最后一行表示c类在该iou阈值下所有TP数。

view:

随着iou阈值的变大(同一行),对于同一个预测框检测的tp是逐步减少,又由于随着检测框的增多(同一列),检测到的tp会不断增多,使得view呈现从左到右依次减小,从上到下依次增加的趋势。

recall: shape=[4488,10]

含义:类别为c,顺序按置信度排列,截至每一个预测框的各个iou阈值下的召回率。

view:

就是将前面的tpc除以n_l(number of labels)。

precision: shape=[4488,10]

含义:类别为c,顺序按置信度排列,截至每一个预测框的各个iou阈值下的精确率

view:

precision = tpc / (tpc + fpc)

mpre: shape=[4490]

含义:该参数通过np.concatenate 返回 [开头 + 输入precision(排序后) + 末尾]

view:

mpre添加了开头和末尾从4488变为了4490,它取自于precision的c类别下的某一iou列,所以shape为[4490],这里原本经过np.concatenate会变为行向量,但后面经过排序又变为了列向量,这就是计算ap是的y坐标。

mrec: shape=[4490]

含义:该参数通过np.concatenate返回 开头 + 输入recall + 末尾

view:

同理mrec通过添加开头和末尾从4488变为了4490,它取自recall的c类别下的某一iou列,所以shape为[4490],经过np.concatenate变为了行向量,用于计算ap的x坐标。

ap: shape=[71,10]

含义:表示某类别在某个iou下的AP(指的是以mrec为X轴,mpre为Y轴,围成曲线的面积,也是PR图)

view:

shape[71,10]表示71个类别以及各iou(0.5-0.95总共10个),ap越大越好。

p: shape=[71,1000]

含义:指的是所有类别, 横坐标为conf(值为px=[0, 1, 1000] 0~1 1000个点)对应的precision值

view:

每一行代表每一个类别,利用np.interp取得在(0-1)之间1000的插值,用于绘制P-Confidence(注意,这里的Map取值可以手动设置,默认是Map=0.5)

R: shape=[71,1000]

含义:指的是所有类别, 横坐标为conf(值为px=[0, 1, 1000] 0~1 1000个点)对应的recall值

view:

每一行代表每一个类别,利用np.interp取得在(0-1)之间1000的插值,用于绘制P-Confidence(注意,这里的Map取值可以手动设置,默认是Map=0.5)

代码部分:

def ap_per_class(tp, conf, pred_cls, target_cls, plot=False, save_dir='.', names=()):"""用于val.py中计算每个类的mAP计算每一个类的AP指标(average precision)还可以 绘制P-R曲线mAP基本概念: https://www.bilibili.com/video/BV1ez4y1X7g2Source: https://github.com/rafaelpadilla/Object-Detection-Metrics.:params tp(correct): [pred_sum, 10]=[1905, 10] bool 整个数据集所有图片中所有预测框在每一个iou条件下(0.5~0.95)10个是否是TP:params conf: [img_sum]=[1905] 整个数据集所有图片的所有预测框的conf:params pred_cls: [img_sum]=[1905] 整个数据集所有图片的所有预测框的类别这里的tp、conf、pred_cls是一一对应的:params target_cls: [gt_sum]=[929] 整个数据集所有图片的所有gt框的class:params plot: bool:params save_dir: runs\train\exp30:params names: dict{key(class_index):value(class_name)} 获取数据集所有类别的index和对应类名:return p[:, i]: [nc] 最大平均f1时每个类别的precision:return r[:, i]: [nc] 最大平均f1时每个类别的recall:return ap: [71, 10] 数据集每个类别在10个iou阈值下的mAP:return f1[:, i]: [nc] 最大平均f1时每个类别的f1:return unique_classes.astype('int32'): [nc] 返回数据集中所有的类别index"""# Sort by objectness#  np.argsort(将x中的元素从小到大排列,提取其对应的index(索引),这里添加负号其实是从大到小)# 计算mAP 需要将tp按照conf降序排列i = np.argsort(-conf)# 得到重新排序后对应的 tp, conf, pre_clstp, conf, pred_cls = tp[i], conf[i], pred_cls[i]# Find unique classes  对类别去重, 因为计算ap是对每类进行unique_classes = np.unique(target_cls)nc = unique_classes.shape[0]  # number of classes, number of detections# Create Precision-Recall curve and compute AP for each class# px: [0, 1] 中间间隔1000个点 x坐标(用于绘制P-Conf、R-Conf、F1-Conf)# py: y坐标[] 用于绘制IOU=0.5时的PR曲线px, py = np.linspace(0, 1, 1000), []  # for plotting# 初始化 对每一个类别在每一个IOU阈值下 计算AP P R   ap=[nc, 10]  p=[nc, 1000] r=[nc, 1000]ap, p, r = np.zeros((nc, tp.shape[1])), np.zeros((nc, 1000)), np.zeros((nc, 1000))for ci, c in enumerate(unique_classes):# i: 记录着所有预测框是否是c类别框   是c类对应位置为True, 否则为Falsei = pred_cls == c# n_l: gt框中的c类别框数量n_l = (target_cls == c).sum()# n_p: 预测框中c类别的框数量# number of labelsn_p = i.sum()  # number of predictions# 如果没有预测到 或者 ground truth没有标注 则略过类别cif n_p == 0 or n_l == 0:continueelse:# Accumulate FPs(False Positive) and TPs(Ture Positive)   FP + TP = all_detections# tp[i] 可以根据i中的的True/False觉定是否删除这个数  所有tp中属于类c的预测框#       如: tp=[0,1,0,1] i=[True,False,False,True] b=tp[i]  => b=[0,1]# a.cumsum(0)  会按照对象进行列方向累加操作# 一维按行累加如: a=[0,1,0,1]  b = a.cumsum(0) => b=[0,1,1,2]   而二维则按列累加# fpc: 类别为c 顺序按置信度排列 截至到每一个预测框的各个iou阈值下FP个数 最后一行表示c类在该iou阈值下所有FP数# tpc: 类别为c 顺序按置信度排列 截至到每一个预测框的各个iou阈值下TP个数 最后一行表示c类在该iou阈值下所有TP数fpc = (1 - tp[i]).cumsum(0)tpc = tp[i].cumsum(0)# Recall=TP/(TP+FN)  加一个1e-16的目的是防止分母为0# n_l=TP+FN=num_gt: c类的gt个数=预测是c类而且预测正确+预测不是c类但是其实属于c类# recall: 类别为c 顺序按置信度排列 截至每一个预测框的各个iou阈值下的召回率recall = tpc / (n_l + 1e-16)  # recall curve# 返回所有类别, 横坐标为conf(值为px=[0, 1, 1000] 0~1 1000个点)对应的recall值  r=[nc, 1000]  每一行从大到小# 这里 recall[:, 0] 指的是iou=0.5时的R-Confidencer[ci] = np.interp(-px, -conf[i], recall[:, 0], left=0)  # negative x, xp because xp decreases# print(-conf[i])# print(-px)# Precision=TP/(TP+FP)# precision: 类别为c 顺序按置信度排列 截至每一个预测框的各个iou阈值下的精确率precision = tpc / (tpc + fpc)  # precision curve# 返回所有类别, 横坐标为conf(值为px=[0, 1, 1000] 0~1 1000个点)对应的precision值  p=[nc, 1000]# 总体上是从小到大 但是细节上有点起伏 如: 0.91503 0.91558 0.90968 0.91026 0.90446 0.90506# 这里 precision[:, 0] 指的是iou=0.5时的P-Confidencep[ci] = np.interp(-px, -conf[i], precision[:, 0], left=1)  # p at pr_score# AP from recall-precision curve# 对c类别, 分别计算每一个iou阈值(0.5~0.95 10个)下的mAPfor j in range(tp.shape[1]):# 这里执行10次计算ci这个类别在所有mAP阈值下的平均mAP  ap[nc, 10]ap[ci, j], mpre, mrec = compute_ap(recall[:, j], precision[:, j])if plot and j == 0:# py: 用于绘制每一个类别IOU=0.5时的PR曲线py.append(np.interp(px, mrec, mpre))  # precision at mAP@0.5# 计算F1分数 P和R的调和平均值  综合评价指标# 我们希望的是P和R两个越大越好, 但是P和R常常是两个冲突的变量, 经常是P越大R越小, 或者R越大P越小 所以我们引入F1综合指标# 不同任务的重点不一样, 有些任务希望P越大越好, 有些任务希望R越大越好, 有些任务希望两者都大, 这时候就看F1这个综合指标了# 返回所有类别, 横坐标为conf(值为px=[0, 1, 1000] 0~1 1000个点)对应的f1值  f1=[nc, 1000]f1 = 2 * p * r / (p + r + 1e-16)# 画各种曲线图if plot:plot_pr_curve(px, py, ap, Path(save_dir) / 'PR_curve.png', names)plot_mc_curve(px, f1, Path(save_dir) / 'F1_curve.png', names, ylabel='F1')plot_mc_curve(px, p, Path(save_dir) / 'P_curve.png', names, ylabel='Precision')plot_mc_curve(px, r, Path(save_dir) / 'R_curve.png', names, ylabel='Recall')# f1=[nc, 1000]   f1.mean(0)=[1000]求出所有类别在x轴每个conf点上的平均f1# .argmax(): 求出每个点平均f1中最大的f1对应conf点的indexi = f1.mean(0).argmax()  # max F1 indexreturn p[:, i], r[:, i], ap, f1[:, i], unique_classes.astype('int32')def compute_ap(recall, precision):"""用于ap_per_class函数中计算某个类别在某个iou阈值下的mAPCompute the average precision, given the recall and precision curves:params recall: (list) [1635] 在某个iou阈值下某个类别所有的预测框的recall  一直从小到大(每个预测框的recall都是截至到这个预测框为止的总recall):params precision: (list) [1635] 在某个iou阈值下某个类别所有的预测框的precision总体上是从大到小 但是细节上有点起伏 如: 0.91503 0.91558 0.90968 0.91026 0.90446 0.90506(每个预测框的precision都是截至到这个预测框为止的总precision):return ap: Average precision 返回某类别在某个iou下的mAP(均值) [1]:return mpre: precision curve [1637] 返回 开头 + 输入precision(排序后) + 末尾:return mrec: recall curve [1637] 返回 开头 + 输入recall + 末尾"""# 在开头和末尾添加保护值 防止全零的情况出现 value Append sentinel values to beginning and end# np.concatenate 默认是按行进行拼接(0表示行,1表示列) 这里由列向量变成了行向量# https://blog.csdn.net/qq_38150441/article/details/80488800mrec = np.concatenate(([0.], recall, [recall[-1] + 0.01]))mpre = np.concatenate(([1.], precision, [0.]))# Compute the precision envelope  np.flip翻转顺序# np.flip(mpre): 把一维数组每个元素的顺序进行翻转 第一个翻转成为最后一个# np.maximum.accumulate(np.flip(mpre)): 计算数组(或数组的特定轴)的累积最大值 令mpre是单调的 从小到大# np.flip(np.maximum.accumulate(np.flip(mpre))): 从大到小# 到这大概看明白了这步的目的: 要保证mpre是从大到小单调的(左右可以相同)# 我觉得这样可能是为了更好计算mAP 因为如果一直起起伏伏太难算了(x间隔很小就是一个矩形)# 而且这样做误差也不会很大 两个之间的数都是间隔很小的# 这里又从行向量变为列向量mpre = np.flip(np.maximum.accumulate(np.flip(mpre)))# Integrate area under curvemethod = 'interp'  # methods: 'continuous', 'interp'if method == 'interp':# 用一些典型的间断点来计算APx = np.linspace(0, 1, 101)  # 101-point interp (COCO)#  np.trapz(list,list) 计算两个list对应点与点之间四边形的面积 以定积分形式估算AP 第一个参数是y 第二个参数是x# 这里画个图就很清楚了(定积分思想)ap = np.trapz(np.interp(x, mrec, mpre), x)  # integrateelse:  # 'continuous'# 采用连续的方法计算AP# 通过错位的方式 判断哪个点当前位置到下一个位置值发生改变 并通过!=判断 返回一个布尔数组i = np.where(mrec[1:] != mrec[:-1])[0]  # points where x axis (recall) changes# 值改变了就求出当前矩阵的面积  值没变就说明当前矩阵和下一个矩阵的高相等所有可以合并计算ap = np.sum((mrec[i + 1] - mrec[i]) * mpre[i + 1])  # area under curvereturn ap, mpre, mrec

代码部分注释的很详细了,还有不懂得可以自行百度。

reference:

【YOLOV5-5.x 源码解读】metrics.py_满船清梦压星河HK的博客-CSDN博客_metrics.py

np.trapz(): np.trapz()的生动解释_鲁凡 Fan Lu的博客-CSDN博客_numpy trapz

np.interp(): numpy.interp()用法_MrLittleDog的博客-CSDN博客_np.interp

np.cumsum(): numpy.cumsum — NumPy v1.22 Manual

np.maximum.accumulate(): 【python numpy】a.cumsum()、np.interp()、np.maximum.accumulate()、np.trapz()_满船清梦压星河HK的博客-CSDN博客_np.maximum.accumulate用法

YOLOv5 中 metrics.py 之 ap_per_class compute_ap 学习记录相关推荐

  1. 12月29日--Java中有关类与对象的学习记录

    1.12月29日第一课记录 Java中有关类与对象的学习记录 一.基本概念部分 1.类:具有相同.相似的属性.特征.行为方式以及功能的一类事物的总称 (举例:一类用户,如淘宝用户) 类是对象的模板 是 ...

  2. YOLOv5中autoanchor.py的def metric(k)的r = wh[:, None] / k[None]的理解

    check_anchors()内metric()函数 def check_anchors(dataset, model, thr=4.0, imgsz=640):"""在 ...

  3. Python中ASCII转十六进制、C中BCD转十进制、十六进制学习记录

    ASCII.BCD转十六进制 ASCII转十六进制 转换规则 Python实现 BCD转十进制.十六进制 BCD码的优点 BCD码分类 各种BCD码的特点 转换规则 ASCII转十六进制 转换规则 A ...

  4. 生存战争-中阶模拟量电路板视频学习记录

    原视频地址 侵删 一. 生存战争电路教学:SR锁存器 SR锁存器(SR板)又称"置位/复位触发器"(Set/Reset).它具有两个稳定状态,分别为0和1. 如果没用外加触发信号, ...

  5. python 0x0101_Python中ASCII转十六进制、C中BCD转十进制、十六进制学习记录

    ASCII.BCD转十六进制 ASCII转十六进制转换规则Python实现 BCD转十进制.十六进制BCD码的优点BCD码分类各种BCD码的特点转换规则 ASCII转十六进制 转换规则 ASCII(A ...

  6. 解决 YOLOV5中SyntaxError: Non-ASCII character ‘\xf0‘ in file detect.py

    SyntaxError: Non-ASCII character '\xf0' in file detect.py 出现这种原因并不是编码的问题.(YOLOV5中) 首先说为什么不是编码的问题,由于我 ...

  7. YOLOv5的Tricks | 【Trick14】YOLOv5的val.py脚本的解析

    如有问题,恳请指出. 这篇可能是这个系列最后的一篇了,最后把yolov5的验证过程大致的再介绍介绍,基本上把yolov5的全部内容就稍微过了一遍了,也是我自己对这个项目学习的结束.(补充一下,这里我介 ...

  8. 【YOLOV5-5.x 源码解读】metrics.py

    目录 前言 0.导入需要的包 1.fitness 2.ap_per_class.compute_ap 2.1.ap_per_class 2.2.compute_ap 3.ConfusionMatrix ...

  9. python导入py文件-Python导入其他文件中的.py文件 即模块

    python中__init__.py文件的作用 问题 在执行models.py时,报ImportError:No module named transwarp.db的错误,但明明transwarp下就 ...

最新文章

  1. mysql 8.14 rpm安装_centos8 安装 mysql8
  2. MSSQL2000 数据库文件迁移到 MSSQL2005 可能要用的一些命令
  3. IOS开发笔记(Swift):UITableView表格视图的静态使用
  4. python 读取txt
  5. Python 执行代码的两种方式
  6. qt制作一个画板_如何直接用Sketch制作动画|Sketch插件|
  7. 小米第二款5G手机是小米9?升级版小米9 配置强悍!
  8. Esper事件处理引擎 EPL语法-模式匹配
  9. 挂载本地目录到Virtualbox并解决[mounting failed with the error: Protocol error]错误
  10. lammps教程:nve/nvt/npt系综设置方法
  11. 关于Oracle性能分析中 自动工作量资料档案库(AWR)的管理(Oracle10个/11g的新特点) 文平...
  12. 悬浮窗一个怎么够?微信新版本满足你的一心多用
  13. 指纹识别属于计算机技术,指纹识别技术属于人工智能吗 指纹识别技术什么时候发明的-与非网...
  14. 姿态估计之2D人体姿态估计 - CPN(Cascaded Pyramid Network for Multi-Person Pose Estimation)
  15. 用Unity3D开发一个题库系统
  16. memwatch的使用(一)
  17. ❤️连续面试失败后,我总结了57道面试真题❤️,如果时光可以倒流...(附答案,建议收藏)
  18. 随性随笔_201606
  19. 28:Maximum sum
  20. restful 简单理解

热门文章

  1. matlab离群值处理,数据平滑和离群值检测
  2. 【Java源码解析】如何严谨地重写 equals 方法、getClass 方法与 instanceof 关键词用法比较
  3. 配电系统中的瞬时故障
  4. 瞬时转速 matlab,基于瞬时转速的发动机故障诊断研究
  5. Cesium实现自定义的广告牌效果
  6. java中policelisten的用法,中考英语简单句和主谓一致专项语法复习
  7. python3教程合集
  8. 布局福建市场,维也纳酒店欧暇·地中海酒店能否为投资人带来信心与底气?
  9. bubble pop
  10. esp8266与mega2560开发板串口通信