目标检测 评价指标
The following article is from CV算法恩仇录 Author 悬鱼铭
点击下方卡片,关注“新机器视觉”公众号
重磅干货,第一时间送达
今天通俗介绍了目标检测的评价指标,有基本原理及代码实现,非常适合初学者。
目录
正样本与负样本 真正 (TP)、假正 (FP)、真负 (TN)、假负 (FN) 交并比(IoU) 准确率(Precision) 召回率 (Recall) 几何平均分(F Score) 单类平均准确率 (Average-Precision) 代码实现 mAP
正样本与负样本
样本在计算机视觉的评价中是非常重要的概念,正样本比较好理解,是要检测的物体,负样本是不要检测的目标。这里负样本会有一些问题,首先负样本定义比较主观,其次负样本和正样本的量纲不在一个级别,那么实际算法中会把检测出的待选区域中的一部分作为正样本,一部分作为负样本,例如 Faster-RCNN 及 SSD 等。
真正(TP)、假正(FP)、真负(TN)、假负(FN)
正确的正向预测(True Positive,TP):正样本被正确检测的数量,需要满足 3 个条件:
置信度(Confidence Score)大于阈值,实际上预测出的所有的框都要满足这个条件; 预测类型与标签类型匹配; 预测的 Bounding Box 与 Ground Truth 的交并比 (Intersection over Union,IoU,后续会详细介绍) 大于阈值 (e.g. 0.5) ,当有多个满足条件的预选框,则选择置信度最大的作为TP,其余的作为 FP。
错误的正向预测(False Positive,FP):负样本被检测为正样本的数量,也称误报,预测的 Bounding Box 与 Ground Truth 的 IoU 小于阈值的检测框(定位错误)或者预测的类型与标签类型不匹配(分类错误)。
错误的负向预测(False Negative,FN):正样本没被检测为负样本的数量,也称漏报,指没有检测出的 Ground Truth 区域。
正确的负向预测(True Negative,TN):是负样本且被检测出的数量,无法计算,在目标检测中,通常也不关注 TN。
交并比(IoU)
交并比 (Intersection over Union,IoU) 又称交并比,计算 2 个区域的重叠比,用 2 个区域的交集除以其并集,公式如下,DR 表示检测结果,GT 表示 Ground Truth。
以下是 IoU 的示意图:
目标检测中的 IoU 是矩形框计算,计算并集部分通过 2 个计算区域的面积减去交集部分即可,重叠情况如下图所示:
在计算重叠区域面积是在找其中计算区域的 min、max 关系,用 表示重叠区域宽度,用 表示重叠区域高度, 选择传入参数中较小值, 选择传入参数中较大值,通过计算得到重叠区域的宽与高,关系如下:
代码实现如下:
'''
dr_box [xmin,ymin,xmax,ymax]
gt_box [xmin,ymin,xmax,ymax]
'''
def iou(dr_box,gt_box):
union_height = min(dr_box[2], gt_box[2]) - max(dr_box[0], gt_box[0])
union_width = min(dr_box[3], gt_box[3]) - max(dr_box[1], gt_box[1])
inter = 0 if union_height<0 or union_width<0 else union_height*union_width
union = (dr_box[2] - dr_box[0]) * (dr_box[3] - dr_box[1]) + \
(gt_box[2] - gt_box[0]) * (gt_box[3] - gt_box[1]) - inter
iou = inter / union
return iou
准确率(Precision)
准确度 (Percision) 也叫查准率,是在识别出的物体中,正确的正向预测 (True Positive,TP) 所占的比率。
代表一共识别出的物体数量,例如下图 3 所示口罩人脸检测的一个效果图,图中 9 个检测框,7 个正确检测出口罩,,2 个未正确检测出口罩,,参照公式 。
召回率 (Recall)
召回率 (Recall),是正确识别出的物体占总物体数的比率。
表示一共有多少个需要检测的物体,继续参照图 3,图中一共 8 个带着口罩的目标,其中 7 个正确检测出口罩,,1 个未检测出口罩,,参照公式得出 。
几何平均分(F Score)
Precision 与 Recall 看起来很相似,实际上这两个是“冤家”。为什么这么说呢?因为在大多数情况下,这两者有一定的互斥性。
理想状态下,模型可以检测出所有要检测的物体,并且没有误报,两个指标都是 100%,实际中不太可能出现。
模型检测出的检测框有一个分类类别和相应的置信度,在稍微复杂的场景下,并不是所有要检测的物体的置信度都很高,或者都能检测出来,也不是所有检测出的全部物体是我们所想要的目标。图 4 展示了 Precision 与 Recall 的关系,随着 Recall 的提高,Precision 值降低了。
接下来用一个例子说明 Precision 与 Recall 的关系,有一个物体检测结果,一个图里的检测出的物体按照置信度倒叙排列进行编号。
按照顺序逐个计算 Precision 及 Recall 值,相当于逐渐在降低阈值(threshold),其结果如下方表格所示。
Precision 与 Recall 单独拿出来作为评价标准都太过片面,可以设置一个较大的阈值来获取高 Precision 或者通过设置个较低的阈值来获得较高的 Recall 值。我们还是希望有一个数能够衡量性能,所以综合 Precision 和 Recall,可以得到一个 F Score,计算公式如下:
F Score 是 Precision 和 Recall 的调和平均数 (harmonic mean),B 的作用是个权重,调整 P 与 R 的比重关系。平均数很好理解,加权平均也不复杂,那“调和”是什么意思呢?调和平均数(harmonic mean)有个特点,Precision 与 Recall 其中有一个值较小,那么整体就会很小,也就是对两个数中如果有特别小的数的惩罚(penalty)比较大。
在论文中,经常可以看到用 F1 值作为评价标准之一,将 B 设置成 1,这就是 F1 Score。
单类平均准确率(Average-Precision)
平均准确度 (Average Precision,AP) 是非常流行的目标检测器 (目标检测算法模型) 的度量指标,从字面上来看 AP 是平均精准度,对于单独的追求 Precision 的数值并不能很好的衡量模型的效果,所以 AP 的计算方式并不是简单的求平均的过程。
在下图中的 Precision 呈现 ”Z“ 形状,随着 TP 增加,FP 减少。
一般定义 AP 为 PR 曲线之下的面积,公式如下:
Precision 与 Recall 的值域在 0 到 1 之间,所以 AP 的值域也是在 [0, 1] 这个范围。知道了 AP 的值域还有定义,在计算前还有个棘手的问题,这里的 P(r) 并不是一个具体的函数,而是根据样本进行计算,是一个数值,在图中是一个点,可以描绘成折线图。
请看图 6 中,在 Recall = 0.7 的时候,Precision 的值是多少?这样的一个问题我们是没办法简单的用插值的形式来进行求解的,同样的我们也没法用函数计算的方式去解决,所以前人就提出一种差值方法:给任意一个 Recall 值,它对应的 Precision 值就等于它最近的右侧的哪个“有值” Precision 中最大的值。
举个例子,以图 6 中的 PR 曲线,Recall = 0.7 的点, Precision 取值为 0.67,求 AP 的过程中,在计算之前,我们需要对图像中的 “Z” 部分做个“平滑”处理,如图所示:
我们将每个 Precision 值替换为该 Recall 值右侧的 Precision 值,这么说比较拗口,看图中红线部分,Precision-Recall 曲线变成了单调减小,不再是之前的 "Z" 形变化。这时候计算出的 AP 值将不太容易受到排名的微小变化的影响。公式变成如下:
Pascal VOC 2008 中设置 IoU 的阈值为0.5,在平滑处理的 PR 曲线上,图 7 中取横轴 0.2-1 的 9 等分点(0.2、0.3、0.4、0.5、0.6、0.7、0.8、0.9、1.0)的 Precision 的值,计算其平均值为最终 AP 的值。
上述计算有两个问题,第一个是使用 9 个采样点在精度方面会有损失。第二个是,在比较两个 AP 值较小的模型时,很难体现出两者的差别。
所以这种方法在 2009 年的 Pascal VOC 之后便不再采用了。在 Pascal VOC 2010 之后,便开始采用这种精度更高的方式。绘制出平滑后的 PR 曲线后,用积分的方式计算平滑曲线下方的面积作为最终的 AP 值,理解为经过插值的 PR 曲线与 X 轴包围的面积,这种方式也称为:AUC (Area under curve)。
上图可知,AP = A1 + A2 + A3,具体计算如下:
mAP (mean Average Precision) 是在每一个类别都计算出 AP 后再计算 AP 的平均值,接下来用代码实现。
代码实现 mAP
这部分学习用代码实现 Pascal mAP 的计算,下面给出最核心的代码。第一部分计算 IoU 和 TP、FP。
# image_ids为列表类型,存放图片数据id,也就是图片名称
# 这里得到的列表并不是全部的训练图片,而是要计算的分类(class)对应的训练图片
nd = len(image_ids)
# 初始化TP与FP
tp = np.zeros(nd)
fp = np.zeros(nd)
# 逐个遍历每个结果
for d in range(nd):
R = class_recs[image_ids[d]]
# 预测的Bounding Box信息
bb = BB[d, :].astype(float)
# 最大IoU,初始化为极小值
ovmax = -np.inf
# GroundTruth Bounding Box
BBGT = R['bbox'].astype(float)
if BBGT.size > 0:
# 通过计算相交顶点来计算相交区域面积
ixmin = np.maximum(BBGT[:, 0], bb[0])
iymin = np.maximum(BBGT[:, 1], bb[1])
ixmax = np.minimum(BBGT[:, 2], bb[2])
iymax = np.minimum(BBGT[:, 3], bb[3])
iw = np.maximum(ixmax - ixmin + 1., 0.)
ih = np.maximum(iymax - iymin + 1., 0.)
inters = iw * ih
# 计算2个矩形面积减去相交面积得到总面积
uni = ((bb[2] - bb[0] + 1.) * (bb[3] - bb[1] + 1.) +
(BBGT[:, 2] - BBGT[:, 0] + 1.) *
(BBGT[:, 3] - BBGT[:, 1] + 1.) - inters)
# 计算IoU并找到最大的IoU
overlaps = inters / uni
ovmax = np.max(overlaps)
jmax = np.argmax(overlaps)
# 根据阈值来判断是TP还是FP
if ovmax > ovthresh:
if not R['difficult'][jmax]:
if not R['det'][jmax]:
tp[d] = 1.
R['det'][jmax] = 1
else:
fp[d] = 1.
else:
fp[d] = 1.
有了上面的内容,就可以进行 Precision 与 Recall 的计算。
# 计算FP的数量
fp = np.cumsum(fp)
# 计算TP的数量
tp = np.cumsum(tp)
# npos表示样本数量,根据公式求得recall值
rec = tp / float(npos)
# 根据公式求Precision值,为了防止出现数值问题,这里做了下容错
prec = tp / np.maximum(tp + fp, np.finfo(np.float64).eps)
有了 Recall 与 Precision 的值,可以通过绘制 Precision-Recall plot 并计算其面积得到 AP 值。
def voc_ap(rec, prec):
# correct AP calculation
# first append sentinel values at the end
mrec = np.concatenate(([0.], rec, [1.]))
mpre = np.concatenate(([0.], prec, [0.]))
# 为求得曲线面积,对 Precision-Recall plot进行“平滑”处理
for i in range(mpre.size - 1, 0, -1):
mpre[i - 1] = np.maximum(mpre[i - 1], mpre[i])
# 找到 recall值变化的位置
i = np.where(mrec[1:] != mrec[:-1])[0]
# recall值乘以相对应的 precision 值相加得到面积
ap = np.sum((mrec[i + 1] - mrec[i]) * mpre[i + 1])
return ap
今天先分享到这里了,祝你有所进步~
本文仅做学术分享,如有侵权,请联系删文。