meanAveragePrecision(mAP)的网络评估在介绍mAP的概念之前,我们先回顾几个概念:TP:TruePositive,真实类,预测正类作为正类的数量。TN:TrueNegtive,真负类,预测负类为负类的个数。FP:FalsePositive,假阳性类,将阴性类预测为阳性类。FN:FalseNegtive,假负类,将正类预测为负类。positiveegtivetrueTPFPfalseFNTN根据以上内容,我们可以得到准确率(Accuracy)、精确率(precision)、召回率(recall)和F1-score。#所有样本中,正确分类样本的比例Accuracy=(TP+TN)/(TP+TN+FP+FN)#所有预测正样本中,正确预测精度的比例=TP/(TP+FP)#在所有正样本中,预测正样本的比例recall=TP/(TP+FN)#precision和recall的权衡F1=2*precision*recall/(precision+recall)mAP是目标检测中的常用评价标准任务,什么是mAP,为什么要用mAP。在目标检测任务中,需要判断一个预测的边界框是否正确。我们会计算预测边界框和真实框的iou,然后设置一个阈值。如果iou>threshold,那么它被认为是正确的。如果提高iou阈值,准确率会提高,召回率会降低。如果iou降低阈值,召回率会增加,准确率会降低。从这个角度来看,仅仅使用一个阈值来评估网络模型肯定是不够的,那么如何在precision和recall之间取得一个tradeoff呢。由于一个阈值不够,所以使用多个阈值来获得多个精度和召回率。这样就可以得到如下的precision-recall曲线,也称为PR曲线,PR曲线与坐标轴围成的面积为AP。Voc2010之前只要选择Recall取[0,0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1.0]共11个值,对应11个点,然后计算PR曲线和坐标axis的封闭区域作为AP。在VOC2010及之后的版本中,对于每一个不同的Recall值(包括0和1),需要选择Precision大于等于这些Recall值时的最大值,然后计算PR曲线下的面积作为AP值。每个类别可以得到一条PR曲线,对应一个AP。将所有类别的AP取平均得到mAP。这里计算插值平均AP,还有另一种计算方法。它们的区别可以参考这里。下图显示了原始PR曲线(绿色)和插值后的PR曲线(蓝色虚线)。直接计算原始PR曲线与坐标轴围成的面积比较困难(需要积分计算),但蓝色虚线与坐标轴围成的面积的计算更为方便简单。插值法填充PR曲线的上升部分,保证PR曲线为下降曲线。计算步骤如下,计算每个类别的Precision和Recall。分别为每个类别插入PR曲线。分别计算每个类别插值后PR曲线的面积,得到每个类别的AP。将每个类别的AP取平均得到mAP。mAP计算代码如下:1.首先计算每个类别的TP和FP,得到每个类别的准确率和召回率。defcalc_detection_voc_prec_rec(pred_bboxes,pred_labels,pred_scores,gt_bboxes,gt_labels,gt_difficults=None,iou_thresh=0.5):"""PascalVoc数据集的评估代码,用于计算精度和召回率Args:pred_bboxes(list):预测框的可迭代列表,其中的每个元素都是一个数组。pred_labels(list):预测标签的可迭代列表。pred_scores(list):预测概率的可迭代列表。gt_bboxes(list):groundtruthboxes的可迭代列表。gt_labels(list):可迭代的groundtruthboxlabels列表。gt_difficults(list):groundtruthbox预测难度的可迭代列表。默认为None,表示难度级别低。iou_thresh(float):如果预测框对应于If的iou真实框大于阈值,则认为预测正确。返回:rec(list):数组列表,rec[l]表示第l类的召回率,如果第l类不存在,则设置toNone.pre(list):数组列表,pre[l]表示第l类的准确率,如果第l类不存在,则置为None。"""#将所有列表转换为可迭代对象pred_bboxes=iter(pred_bboxes)pred_labels=iter(pred_labels)pred_scores=iter(pred_scores)gt_bboxes=iter(gt_bboxes)gt_labels=iter(gt_labels)ifgt_difficultsisNone:gt_difficults=itertools.repeat(无)其他:gt=_iteric(gt_difficults)#每个类别级别是真实框的个数n_pos=defaultdict(int)#score=defaultdict(list)#表示每个预测框是否匹配真实框match=defaultdict(list)#pred_bbox,pred_label,pred_score,gt_bbox#gt_label,gt_difficult这6个list的长度是一样的#每次迭代相当于一个batchforpred_bbox,pred_label,pred_score,gt_bbox,gt_label,gt_difficultin\six.moves.zip(pred_bboxes,pred_labels,pred_scores,gt_bboxes,gt_labels,gt_difficults):ifgt_difficultisNone:gt_difficult=np.zeros(gt_bbox.shape[0],dtype=bool)#在np.unique(np.concatenate((pred_label,gt_label)).astype(int)):#取出属于l类的预测框和预测分数=pred_score_l.argsort()[::-1]pred_bbox_l=pred_bbox_l[order]pred_score_l=pred_score_l[order]#取出属于第l类的真实框,默认都是n_pos[l]+=np.logical_not(gt_difficult_l).sum()score[l].extend(pred_score_l)#如果没有预测框iflen(pred_bbox_l)==0:continue#如果真实框的个数为0,则没有匹配iflen(gt_bbox_l)==0:match[l].extend((0,)*pred_bbox_l.shape[0])continuepred_bbox_l[:,2:]+=1gt_bbox_l[:,2:]+=1#计算预测框和真实框的iouiou=bbox_iou(pred_bbox_l,gt_bbox_l)#得到每个预测框的iou最大的真实框的indexgt_index=iou.argmax(axis=1)#iou小于阈值,表示没有真实框对应的预测框,然后将索引设置为-1gt_index[iou.max(axis=1)
