最近在做模型评测,在评测指标的计算过程上卡了很久,之前在课本上看的计算过程以及很多博客上看计算方法,介绍得比较简单,真正自己算的时候才发现细节真的实在太多了,花了一天时间理清了计算过程。
1.目标检测结果输出与指标计算流程
(1)以基于anchor的目标检测为例,模型输出大量检测框的类别分数和对应的offset,offset解码得到预测框的左上角和右下角坐标:(x1,y1,x2,y2);
(2)解码之后得到的检测框数量是远远大于真实数量的,存在多个预测框重叠的情况,于是使用NMS的方法对预测框进行过滤,注意:NMS只是过滤重叠的预测框,此时还没有跟真实框进行匹配,也就是下一步。
(3)预测框与真实框匹配,使用iou进行计算,大于一定阈值(一般设置为0.5或0.7),则认为是匹配上的。这里有两个问题:
**a.一个预测框可能与多个真实物体iou大于阈值:**选取iou最大的那个,实际操作是计算得到一个预测框与所有真实框的iou列表,进行降序排列,取最大的iou与阈值对比,大于阈值则将其匹配,作为TP;
**b.多个预测框匹配到一个真实物体:**取置信度最大的那个,作为TP,剩下的都是FP
(4)得到TP和FP的值了就可以计算precision、recall、AP、mAP了,这里也是有很多细节的,precision、recall比较好算:
r e c a l l = T P N _ g r o u d _ t r u t h recall=\frac{TP}{N\_groud\_truth} recall=N_groud_truthTP
p r e c i s i o n = T P T P + F P precision=\frac{TP}{TP+FP} precision=TP+FPTP
AP和mAP涉及到置信度阈值的,可以参考这篇博客,讲得很详细。
2.AP、mAP计算代码
numpy版本,网上大部分是这个版本,直接复制粘贴了:
def voc_ap(rec, prec, use_07_metric=False):
""" ap = voc_ap(rec, prec, [use_07_metric])
Compute VOC AP given precision and recall.
If use_07_metric is true, uses the
VOC 07 11 point method (default:False).
"""
if use_07_metric:
# 11 point metric
ap = 0.
for t in np.arange(0., 1.1, 0.1):
if np.sum(rec >= t) == 0:
p = 0
else:
p = np.max(prec[rec >= t])
ap = ap + p / 11.
else:
# correct AP calculation
# first append sentinel values at the end
mrec = np.concatenate(([0.], rec, [1.]))
mpre = np.concatenate(([0.], prec, [0.]))
print(mpre)
# compute the precision envelope
for i in range(mpre.size - 1, 0, -1):
mpre[i - 1] = np.maximum(mpre[i - 1], mpre[i])
# to calculate area under PR curve, look for points
# where X axis (recall) changes value
i = np.where(mrec[1:] != mrec[:-1])[0]
ap = np.sum((mrec[i + 1] - mrec[i]) * mpre[i + 1])
print(mpre)
return ap
torch版本:
def voc_ap(recall,precision,use_07_metric=False):
if use_07_metric:
recall_thresholds = torch.arange(start=0, end=1.1, step=.1).tolist() # (11)
precisions = torch.zeros((len(recall_thresholds)), dtype=torch.float).to(device) # (11)
for i, t in enumerate(recall_thresholds):
recalls_above_t = recall >= t
if recalls_above_t.any():
precisions[i] = precision[recalls_above_t].max()
else:
precisions[i] = 0.
AP = precisions.mean() # c is in [1, n_classes - 1]
else:
mrecall=torch.cat((torch.tensor([0.]).to(device),recall,torch.tensor([1.]).to(device)),0)
mprecision=torch.cat((torch.tensor([0.]).to(device),precision,torch.tensor([0.]).to(device)),0)
for i in range(mprecision.size(0)-1,0,-1):
mprecision[i-1]=torch.max(mprecision[i-1],mprecision[i])
i=torch.nonzero(mrecall[1:]!=mrecall[:-1][0]).squeeze()
AP=torch.sum((mrecall[i+1]-mrecall[i])*mprecision[i+1])
return AP