## 1. 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的计算

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

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

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)