Press "Enter" to skip to content

解析PR曲线与目标检测中的mAP指标

本站内容均来自兴趣收集,如不慎侵害的您的相关权益,请留言告知,我们将尽快删除.谢谢.

文章目录

 

本篇博客主要是介绍 PR 曲线与目标检测中的 mAP 指标。

 

1. PR曲线的绘制

 

有关 PR 曲线的解释,可以参考我的这篇博客: 机器学习中常用评价指标(分类篇)

 

import numpy as np
from matplotlib import pyplot as plt
seed = 10001
np.random.seed(seed)
# 预测框个数
num_pred_boxes = 30
# 真实框个数
num_gt_boxes = 15
def draw_pr(rec, prec):
    plt.plot(rec, prec, label='PR curve')
    plt.xlabel('Recall')
    plt.ylabel('Precision')
    plt.xlim([0.0, 1.0])
    plt.ylim([0.0, 1.0])
    plt.title('Precision-Recall')
    plt.legend()
    plt.savefig('pr.png')
    plt.show()
if __name__ == '__main__':
    tp = np.zeros(num_pred_boxes)
    fp = np.zeros(num_pred_boxes)
    for i in range(num_pred_boxes):
        if np.random.rand() > 0.55:
            tp[i] = 1
        else:
            fp[i] = 1
    # 按列累加
    tp = np.cumsum(tp)
    fp = np.cumsum(fp)
    # recall
    rec = tp / float(num_gt_boxes)
    # precision
    prec = tp / (tp + fp)
    draw_pr(rec, prec)

 

 

2. AP的计算

 

即使有了 PR 曲线,评价模型仍然不直观,如果直接取曲线上的点,在哪里选取都不合适,因为召回率高的时候精确率会很低,精确率高的时候往往召回率很低,这时, AP 就派上用场了。 AP(Average Precision) 表示的是样本的平均精度,从公式中可以看出, AP 代表了曲线的面积,综合考量了不同召回率下的准确率,不会对 PrecisionRecall 有任何偏好。 AP 的计算公式如下:

 

A P = ∫ 0 1 P ( r )   d r AP = \int _0^1 P(r)\ dr A P = ∫ 0 1 ​ P ( r )   d r

 

通常来讲, AP 的值越高,分类器的性能越好。

 

但是在实际应用中( VOC 2010 以后),我们不是直接对积分进行计算,而是对其平滑操作来简化计算,对 PR 曲线上的每个点, precision 的值取该点右侧的最大值,然后对平滑后的曲线求面积。以上图为例,平滑后的 PR 曲线如下:

 

此时的计算公式为:

 

A P = ∑ i ( R ( i + 1 ) − R ( i ) ) P s m o o t h ( i ) AP = \sum _i \big(R(i+1)-R(i)\big) P_{smooth}(i) A P = i ∑ ​ ( R ( i + 1 ) − R ( i ) ) P s m oo t h ​ ( i )

 

VOC 2007 中计算 AP 的方法是不计算所有的点,而是在 [0, 1] 区间上 10 等分,即采样得到 11 个点,仍然取当前点右侧的最大值作为当前点的 precision 的值,仍然以第一节的图为例,采样后的图如下:

 

此时的计算公式为:

 

A P = 1 11 ∑ i P s m o o t h ( i ) AP = \frac{1} {11} \sum _i P_{smooth}(i) A P = i ∑ ​ P s m oo t h ​ ( i ) 其中, i 的取值为 [0, 0.1, 0.2, ... , 1.0] 。很明显,通过 11 个不同位置的 recall 来计算 AP 时会有精度损失,所以现在通常采用第一种方法来计算 AP

 

代码实现两种 AP 的计算方式:

 

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.]))
        # 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]
        # and sum (\Delta recall) * prec
        # 计算PR曲线向下包围的面积
        ap = np.sum((mrec[i + 1] - mrec[i]) * mpre[i + 1])
    return ap

 

mAP(mean Average Precision) 表示的是所有类别 AP 值的均值, mAP 越高,表示目标检测的精度越高。其计算公式如下:

 

m A P = 1 N ∑ A P mAP = \frac {1} {N} \sum AP m A P = ∑ A P 其中, N 表示类别的数量。

 

3. 完整代码

 

import numpy as np
from matplotlib import pyplot as plt
seed = 10001
np.random.seed(seed)
# 预测框个数
num_pred_boxes = 30
# 真实框个数
num_gt_boxes = 15
def draw_pr(rec, prec, use_07_metric=False):
    plt.plot(rec, prec, label='PR curve')
    plt.xlabel('Recall')
    plt.ylabel('Precision')
    plt.title('Precision-Recall')
    if use_07_metric:
        plt.xticks(np.arange(0., 1.1, 0.1))
        plt.yticks(np.arange(0., 1.1, 0.1))
        rec_voc = np.arange(0., 1.1, 0.1)
        prec_voc = []
        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])
            prec_voc.append(p)
        plt.plot(rec_voc, prec_voc, label='PR VOC2007')
        plt.scatter(rec_voc, prec_voc, color='g', alpha=0.7, label='PR VOC2007 points')
    else:
        plt.xlim([0.0, 1.0])
        plt.ylim([0.0, 1.0])
        for i in range(len(rec)):
            prec[i] = prec[i:].max()
        plt.plot(rec, prec, label='PR curve smooth')
        # plt.scatter(rec, prec, color='g', alpha=0.7)
    plt.legend()
    plt.savefig('pr.png')
    plt.show()
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.]))
        # 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]
        # and sum (\Delta recall) * prec
        # 计算PR曲线向下包围的面积
        ap = np.sum((mrec[i + 1] - mrec[i]) * mpre[i + 1])
    return ap
if __name__ == '__main__':
    tp = np.zeros(num_pred_boxes)
    fp = np.zeros(num_pred_boxes)
    for i in range(num_pred_boxes):
        if np.random.rand() > 0.55:
            tp[i] = 1
        else:
            fp[i] = 1
    # 按列累加
    tp = np.cumsum(tp)
    fp = np.cumsum(fp)
    # recall
    rec = tp / float(num_gt_boxes)
    # precision
    prec = tp / (tp + fp)
    draw_pr(rec, prec, use_07_metric=False)
    ap = voc_ap(rec, prec, use_07_metric=False)
    print(ap)

Be First to Comment

发表回复

您的电子邮箱地址不会被公开。