学习目标检测一定少不了的评测方式, 就是透过recalls, precisions, 来计算出类别的AP, 以及最后所有AP的平均值 mAP(mean Average Precision) 也就是我们最关心的数值
这边先简单的了解一下confusion matrix, 也就是所谓的混肴矩阵, 我个人不觉得这是一个很好的翻译, 最好记得英文就行, 在分类任务中这是一个非常重要的评测指标
下图是一个基本的confusion matrix
下面我直接带入例子会比较好解释
假设你的数据集一共是2分类, 第一类是人, 第二类是汽车
而测试集只有两张图片
Actual Value and Predicted Value
Actual value :实际的值, 在目标检测领域中, 我们可以理解为ground truth, 就是人或者是汽车在图片上的真实坐标
predicted value:这个就很好理解了, 就是指我们模型预测出来的坐标
pos 和 neg 这边不先定义, 我讲下去你就知道了
TP FP FN TN
这里通常很容易被弄混, 难怪叫做混肴矩阵, 跟着我的步伐走就没问题,强烈建议拿出纸笔把矩阵画一次就清楚了
- TP (TruePositive): 正确预测出类别的 (正确识别为人或是汽车)
- FP (FalsePositive):错误预测类别的 (人当成汽车)
- FN(FalseNegative): ground truth中没有被检测到的
- TN(TrueNegative):理论上来讲就是检测出不是这个类别的数量, 但是并不适用在目标检测领域, 因为通常一张图后会有非常多的bbox是没有检测到groundtruth的, 所以这无法计算, 并不影响, 因为也用不上
Recalls and Precisions
Recalls
可以叫做查全率
又或者是召回率
, 可以当做是所有类别x中预测为正确的
也就是
T
P
T
P
+
F
N
\frac{TP}{TP+FN}
TP+FNTP , 对应图片的蓝框公式很直观, 就是所有的ground truth(所有测试集中的样本每张图片上的人 或者 汽车的总数)里面检测出人 或者 汽车的数量
比如图中有5个标注为汽车的ground truth, 那么你检测到的汽车有两个, 那么对于这张图, 汽车类的 recalls 就是 2/5
Precisions
可以叫做精准率
, 就是你检测到结果里面, 实际上真的正确的为多少?
T
P
T
P
+
F
P
\frac{TP}{TP+FP}
TP+FPTP
TP就是检测正确的, FP就是检测错误的, 那么加起来不就是你所有检测的结果? 对应图中红框
比如图中有3个人的ground truth, 你检测到的也是三个框, 但是其中只有一个框检测吻合到你的ground truth,另外两个检测成人了, TP = 1, FP = 2, 那么precision 就是 1/3
到这边如果没明白就先理清楚在往下看吧
首先来用mmdetection 的检测代码mean_ap.py
来说明会更清晰
我不打算放出全部代码, 这样太乱了 我们看最主要的部分加深印象就行 !
首先看到eval_map这个函数, 看名字就很直观 evaluation mAP
def eval_map(det_results,
gt_bboxes,
gt_labels,
gt_ignore=None,
scale_ranges=None,
iou_thr=0.5,
dataset=None,
print_summary=True):
说几个重要的参数就好
- det_results 就是检测到的结果
- gt_bboxes 就是ground truth 的框
- gt_labels 就是类别标签, 比如人是1, 汽车是2
- gt_ignore 这如果没有标注difficulty 不用理会
- iou_thr 就是检测出的和groud truth 的 iou 阈值
也就是说如果要检测出recall precision mAP这些指标, 就要以上这些重要的值
那么计算recall precision 之前 我们就得先算出 TP FP FN 这些重要的指标, 所有一开始的矩阵是不是很重要?
函数tpfp_default
就是负责计算出TP FP的
我们需要以下参数, 注意该函数是针对单张图像的
def tpfp_default(det_bboxes,
gt_bboxes,
gt_ignore,
iou_thr,
area_ranges=None):
我们的训练集只有两张
如果人label = 1, 汽车label=2, 下面直接用vector表示
- 第一张图label = [1, 1, 2, 1, 1] 也就是说有四个人一台车, 一共五个ground truth
- 第二张图 label = [2, 2, 1, 2] 三台车 一个人的ground truth
搬出ground truth
ground truth 最后面的#1 or #2表示label
#groubd
gt_bbox1 = np.array([[358, 288, 498, 387], #1
[356, 425, 525, 570], #1
[377, 119, 478, 189], #2
[180, 68, 314, 142], #1
[417, 159, 575, 240]], dtype=np.float32)#1
gt_bbox2 = np.array([[258, 188, 398, 287], #2
[256, 325, 425, 470], #2
[277, 19, 378, 89], #1
[280, 168, 414, 242]], dtype=np.float32)#2
然后我们检测到的结果是下面这样, 都是bbox的坐标值, 一共两张图片, 图上都会有每个检测到的类别的坐标
检测到的结果 det_results
'''det_results 图片 _ 类别'''
det_results1_1 = np.array([[359, 289, 499, 388],
[346, 415, 515, 560],
[367, 109, 468, 179],
[190, 78, 324, 152],
[430, 172, 588, 253]], dtype=np.float32)
det_results1_2 = np.array([[259, 189, 399, 288],
[236, 315, 415, 440],
[267, 9, 368, 79],
[290, 178, 424, 252]], dtype=np.float32)
det_results2_1 = np.array([[359, 289, 499, 388],
[346, 415, 515, 560],
[367, 109, 468, 179],
[190, 78, 324, 152],
[430, 172, 588, 253],
[230, 72, 388, 53]], dtype=np.float32)
det_results2_2 = np.array([[259, 189, 399, 288],
[236, 315, 415, 440],
[267, 9, 368, 79],
[290, 178, 424, 252],
[159, 89, 299, 188]], dtype=np.float32) #not ok
可以看出det_result1_1 表示第一张图的label 1 检测到的框
那么det_result2_2 就表示第二张图检测到label 2的框
所以可以总结
label1 一共检测出11个(TP+FP)
label2 一共检测出 9 个 (TP+FP)
好了, 回到tpfp_default
为了要检查出模型检测的是不是正确的, 也就是TP, 我们就用到了iou_thr
这个阈值
一般会是0.5, 那么还要计算出IOU(交并比), 什么是IOU请直走左转找到人就问, 如果连这都写, 篇幅会太长
透过以下的函数进行计算
ious = bbox_overlaps(det_bboxes, gt_bboxes)
得出来的ious会长这样子
#这是label 1的第一张图检测与gt的ious结果
[[0.9665272 0. 0. 0. ]
[0. 0.7804878 0. 0. ]
[0. 0. 0. 0.05691057]
[0. 0. 0.6701031 0. ]
[0. 0. 0. 0.6295463 ]]
#这是label1 的第二张图检测与gt的结果
[[0. ]
[0. ]
[0. ]
[0.03430409]
[0. ]
[0. ]]
#这是label 2的第一张图检测与gt的ious结果
[[0.00107885]
[0. ]
[0. ]
[0.03430409]]
#这是label 2的第二张图检测与gt的ious结果
[[0.9665272 0. 0.36517328]
[0. 0.6413269 0. ]
[0. 0. 0. ]
[0.41336057 0. 0.6701031 ]
[0.00149158 0. 0.01764335]]
一脸懵?
没事的, 我刚看到也是懵
label1 图1 检测到的不是五个bbox吗 那么拿去和gt计算 就会有五个分数了
那图2 检测到的6个bbox 也当然就有6个分数了
不要太注意shape不同, 只要注意分数就好
很明显的模型在检测 label1 的时候, 在图1的表现好很多 !我们发现到
[0. 0. 0. 0.05691057]
这行分数很低接近于0,因为图1的label是[1, 1, 2, 1, 1]
在第三个值是label 2, 所以算出来的IOU几乎为0, 因为并不属于label1的
在来看到label2 的iou
#这是label 2的第一张图检测与gt的ious结果
[[0.00107885]
[0. ]
[0. ]
[0.03430409]]
分数基本都趋近于0, 就表示模型在图1 完全没找到什么都没检测到
你可以比较一下det_result1_2的坐标和gt_bbox1的坐标就很清楚了, 坐标完全没重合的感觉
如此一来我们就有了label1 和 label2 所有检测到的框和ground truth的 IOU值了
得到了IOU在透过一开始给的iou_thr
阈值, 不就能筛选出谁是TP 谁是 FP了吗?
来, 咱们在复习一次 , 跟着我大声念!
检测大于阈值的叫做TP(TruePositive)
低于阈值的也就是错误的叫做FP (FalsePositive)
喝口水继续
我们将刚刚的ious 进行整理
ious_max = ious.max(axis=1)
ious_argmax = ious.argmax(axis=1)
输出会是下面这个样子, 是不是, 说shape先别在意的 !反正都会变这个样子
ious_max [0.9665272 0.7804878 0.05691057 0.6701031 0.6295463 ]
ious_argmax [0 1 3 2 3]
接下来就是透过阈值筛选, 过程代码比较多, 不全写出来
if ious_max[i] >= iou_thr:
matched_gt = ious_argmax[i]
tpfp_default
函数输出就会是像这样
tp: [[1. 1. 0. 1. 1.]]
fp: [[0. 0. 1. 0. 0.]]
是不是一目了然? 也能够对应到分数?iou高说明检测正确并且归纳在TP 用1表示
而分数低的自然就是FP了 用0表示
拿到了TP FP 就离我们的recalls 和 precisions 不远了啊 !
好了 这只是上半场
由于篇幅的关系 我临时决定分成两章来写完
说老实话, 这要是一篇文章能解决我也是佩服佩服