深度学习模型评估指标:mAP计方法与voc_eval.py源码解读

本文详细介绍了深度学习模型评估指标mAP(mean Average Precision)的基本计算原理,解析了voc_eval.py源码的五个关键步骤,包括GT标签信息处理、结果排序、IOU计算以及AP、REC和PREC的计算。通过对字母F的识别实例,展示了precision-recall图的分析过程,帮助理解mAP的计算方法。
摘要由CSDN通过智能技术生成


我们在上一篇文章《YOLOv3计算自己数据集训练模型的mAP》中介绍了使用valid计算出来验证集的检测结果结果,存于results文件,然后voc_eval函数计算mAP的操作方法。但是voc_eval函数的计算原理是什么?作者是如何通过代码实现的呢?本文尝试进行一个分析介绍。

1.mAP基本计算原理

mAP是mean Average Precision的缩写,Average Precision是每个类型的结果中,每个检测结果的precision(精确度或查准率)在recall(召回率或查全率)上的平均,简称AP;mean是针对每个类型检测结果的平均,就是对AP的平均。
voc_eval函数计算的是每个类型的AP,计算的时候,通过计算precision在recall上的积分,然后再除以recall的域,recall的域为(0,1)。在实际计算的时候,0和1这两种状态是不存在的,所以要对其进行补齐,这时候,对应的precision都为0.
所以,求解mAP的核心工作就是计算precision和recall,关于precision和recall我们将在后面的文章单独介绍,这里简单说说precision和recall的含义。precision就是某种类型的一堆目标检测结果的正确率。换句话说,我们这一堆检测结果中有真的,有假的,我们通过precision计算真值在这些检测出来的结果中的比率。recall就是某种类型的一堆目标中正确检测出来的目标的比例,考察这些目标有没有能够真正检测出来多少。

2.源码分析

源码中计算步骤分为五步:
①第一步:获取所有的GT标签信息,存入字典recs中或文件annots.pkl中,便于使用。这里用“或”表示如果第一次从recs中存入annots.pkl中,那么下次使用,就直接从annots.pkl中读取到recs中即可。GT标签信息在数据集中是存储在Annotations文件夹的成百上千的xml文件中的,每次都读取一遍实在没有必要,于是就在第一次读取的时候将name(类型名),bbox(位置尺寸信息)存入annots.pkl二进制文件。
②第二步:从字典recs中提取当前类型的GT标签信息,存入字典class_recs中,key为图片名imagename。注意,这个key并不是唯一的,有时候一张图片可能有多个相同目标,这时候就要通过计算IOU匹配正确的GT。
③第三步:从当前class的result文件中读取结果,并将结果按照confidence从大到小排序,排序后的结果存在BB和image_ids变量中,BB用于存储位置尺寸信息,image_ids就是图像名,用于和存GT的class_recs中的内容进行匹配。
④第四步:对比GT参数和result,计算出IOU,在fp和tp相应位置标记1。这里在计算的原理如下:
首先,对每一条结果根据confidence进行从大到小排序,第一次遇到且IOU高于阈值,就认为是正目标正确检测,即TP += 1;IOU低于阈值FP+=1。
这个过程中肯定存在计算IOU时,一个图像中有两个GT的,就需要选择一个大的,确定是这个目标。
接着,当相同的图片中又出现一次结果的时候,看看这次检测到的是什么目标:
第一种情况,图中正好有另一个GT,又检测到了,这时候jmax是另一个,TP += 1。
第二种情况,图中正好有另一个GT,但是检测的还是之前那个,相当于一个目标命中两次也计算为 FP+=1。
一般情况下,尺度差别较大的目标不会同时被两种尺度模式同时检测出结果,因为尺度相差比较大,基本不可能是统一个目标。但是如果目标介于两个anchor之间,也很容易预测错误,即两个尺度模式都预测到。本人自制数据集为0-9,a-z,A-Z的验证码数据集,由于所有的目标都是一样尺寸,两个尺度模式都能够预测出来,出现第二种情况FP的概率比较高,因此这种验证码尺寸都一样的就不能使用多尺度。
这里还应注意的是降低阈值可以提高mAP,但是降低到一定成都就不行了,因为虚警率也提升了。
⑤第五步:计算ap,rec,prec。这里使用了voc_ap函数进行计算。
这里需要注意的是计算tp和fp的时候使用了numpy.cumsum(a, axis=None, dtype=None, out=None)函数,这个函数计算结果是第i个位置为a[0]到a[i]的累加,用于积分计算。

下面是本人整理的voc_eval中这五步的详细源码及注释:

voc_eval函数

def voc_eval(detpath,
             annopath,
             imagesetfile,
             classname,
             cachedir,
             ovthresh=0.25,
             use_07_metric=False):
    """rec, prec, ap = voc_eval(detpath,
                                annopath,
                                imagesetfile,
                                classname,
                                [ovthresh],
                                [use_07_metric])
    Top level function that does the PASCAL VOC evaluation.
    detpath: Path to detections
        detpath.format(classname) should produce the detection results file.
    annopath: Path to annotations
        annopath.format(imagename) should be the xml annotations file.
    imagesetfile: Text file containing the list of images, one image per line.
    classname: Category name (duh)
    cachedir: Directory for caching the annotations
    [ovthresh]: Overlap threshold (default = 0.5)
    [use_07_metric]: Whether to use VOC07's 11 point AP computation
        (default False)
    """
    # assumes detections are in detpath.format(classname)
    # assumes annotations are in annopath.format(imagename)
    # assumes imagesetfile is a text file with each line an image name
    # cachedir caches the annotations in a pickle file

#################################################################################################
##### 第一步:获取所有的GT标签信息,存入字典recs中或文件annots.pkl中,便于使用 #####################
#################################################################################################
    # 标签信息都是GT的信息
    # 提取annotations标签文件缓存路径
    # 如果没有缓存文件,就读取信息并创建一个二进制缓存文件annots.pkl
    if not os.path.isdir(cachedir):
        os.mkdir(cachedir)
    cachefile = os.path.join(cachedir, 'annots.pkl')
    # 从图像名称文件中读取图像名称,存入imagenames列表中
    with open(imagesetfile, 'r') as f:
        lines = f.readlines()
    imagenames = [x.strip() for x in lines]

    # 根据imagenames列表存储或读取标签信息
    if not os.path.isfile(cachefile):
        # 载入标签文件,recs这个字典中,存储了验证集所有的GT信息
        recs = {
   }
        for i, imagename in enumerate(imagenames):
            #解析标签xml文件,annopath为/{}.xml文件,加format表示为{}中赋值
            #imagename来源于从imagesetfile中提取,循环采集所有的的信息
            recs[imagename] = parse_rec(annopath.format(imagename))
            #解析标签文件图像进度条
            if i % 100 == 0:
                print('Reading annotation for {:d}/{:d}'.format(
                    i + 1, len(imagenames)))
        # 将读取标签内容存入缓存文件annots.pkl,这是个数据流二进制文件
        print('Saving cached annotations to {:s}'.format(cachefile))
        with open(cachefile, 'wb') as f:
            pickle.dump(recs, f)#使用了pickle.dump,存入后保存成二进制文件
    else:
        # 有标签缓存文件,直接
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

北溟客

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值