敞开成长之旅!这是我参与「日新计划 12 月更文挑战」的第1天,点击检查活动概况
看方针检测方面的论文时,都把mAP(mean Average Precision) 当作最终的比较不同模型性能的方针。
平常要用的时分,就调用个开源代码,然后屁颠屁颠地给老板看结果,没想到这次要自己写,找了好几份资料和代码,才把这个概念看懂了大概,看来想在这行有长进,仍是得踏踏实实下苦功。
为了了解mAP的意义,先列出这几个方针检测的基本方针:
– TP:IoU>IoU_Threshold的真值检测框数量。
– FP:IoU≤IoU_Threshold的检测框数量,或者是检测到同一个真值框的多余检测框的数量。
– FN:没有检测到的真值框的数量。
– Precision(精确率):TP/(TP+FP)
– Recall(召回率):TP/(TP+FN)
– F1-Score:2(presicionrecall)/(presicion+recall) 用于衡量precision和recall间的巨细关系
信任了解方针检测的同学们都已经对Presicion和Recall滚瓜烂熟了:
Precision便是想确保没有错检,Recall便是为了确保没有漏检,若提高阈值,错检的个数会削减,但一起漏检的个数会增多,这两个方针不会一起增高,会相互直接拉扯。F1-Score则是为了衡量Recall和Precision间的平衡而提出的方针。
已经有Precision、Recall、F1 score了,为什么还需要mAP呢?
- 方针检测任务里常常为多分类问题,上述的方针并不能反映多分类的性能
- IoU_threshold和置信度都会影响Precision和 recall的值
我们常常还会在论文里看到mAP@后边带着一个小数,能够分开三部分来看。
-
“average precision”(AP)指的是PR曲线下的面积
-
“mean”指的是所有类别的AP平均值
-
@后边的小数指的是不同iou阈值,即IoU_Threshold,用于衡量定位精度
怎么得出PR曲线呢?
一种常见的方法,便是将置信度分数按从大到小排列,取第一个框,核算第一对recall和precision值,再取前两个框,核算recall和precision,再取前三个框…以此类推,直到所有的检测框都核算完。
关于核算PR曲线下的面积,VOC给出了两种不一样的方法:
- voc2007里,对于每个点,取其右边的极大Precision值;每隔0.1个recall取11个点,最终算11个点的precision的平均值作为AP。
(图片来自网络)
- voc2012里是直接核算PR曲线围成的面积巨细。
(图片来自网络)
代码核算过程
Step1: 将检测框按置信度从高到低排列,设定IoU_Threshold,核算Recall-Precision曲线
def calc_detection_mAP(all_gt_dict, all_pred_dict,iou_threshold=0.8,use_07_metric=True):
mAP_dict = {}
for class_name in all_gt_dict.keys():
gt_boxes_list = all_gt_dict[class_name]
pred_boxes_list = all_pred_dict[class_name]
npos = len(gt_boxes_list)
if npos == 0:
continue
pred_boxes_list = sorted(pred_boxes_list, key=lambda x: x[4], reverse=True)
tp = np.zeros(len(pred_boxes_list))
fp = np.zeros(len(pred_boxes_list))
for idx, pred_box in enumerate(pred_boxes_list):
pred_box_restored = list(map(int, pred_box[:4]))
max_overlap = -1
for gt_box in gt_boxes_list:
overlap = iou(pred_box_restored, gt_box)
if overlap > max_overlap:
max_overlap = overlap
if max_overlap >= iou_threshold:
tp[idx] = 1
else:
fp[idx] = 1
fp = np.cumsum(fp)
tp = np.cumsum(tp)
rec = tp / float(npos)
prec = tp / np.maximum(tp+fp, np.finfo(np.float64).eps)
ap = get_ap(rec, prec, use_07_metric)
mAP_dict[class_name] = ap
return mAP_dict
Step2:核算AP的两种不同方法
def get_ap(rec, prec, use_07_metric=False):
if use_07_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 += p/11
else:
mrec = np.concatenate(([0.], rec, [1.]))
mpre = np.concatenate(([0.], prec, [0.]))
for i in range(mpre.size - 1, 0, -1):
mpre[i-1] = np.maximum(mpre[i-1], mpre[i])
i = np.where(mrec[1:] != mrec[:-1])[0]
ap = np.sum((mrec[i+1]-mrec[i]) * mpre[i+1])
return ap
Step3:核算mAP
def calc_mAP(all_gt_dict, all_pred_dict, iou_threshold=0.5, use_07_metric=True):
mAP_dict = calc_detection_mAP(all_gt_dict, all_pred_dict, iou_threshold, use_07_metric)
mAP_list = list(mAP_dict.values())
mAP = np.mean(mAP_list)
return mAP
写在后边
这些根底的方针核算说难不难,说容易,又会写错细节。搞算法,花里胡哨的神经网络层出不穷,但更重要的是牢牢掌握根底知识,思路清晰的思考才能够更快速高效地解决问题。