借鉴知乎这篇解答,来看看以下AP计算代码到底怎么做的,建议先看完回答
目标检测中的mAP是什么含义? - Wentao的回答 - 知乎
先说AP计算的到底是什么,AP计算的是PR折线的面积
pre = np.array([1., 1., 0.66, 0.5, 0.4, 0.5, 0.43, 0.38, 0.44, 0.5])
rec = np.array([0.14, 0.29, 0.29, 0.29, 0.29, 0.43, 0.43, 0.43, 0.57, 0.71])
mrec = np.concatenate(([0.], rec, [1.]))
mpre = np.concatenate(([0.], pre, [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
ap = np.sum((mrec[i + 1] - mrec[i]) * mpre[i + 1])
前四行,输入prediction和recall,两边各增加一个数,后面将有什么用。
for循环,简单来说这两行代码实现的是从mpre最后一个值开始,倒序向前覆盖较大的值。这个代码做了个什么事呢?我们用图像来理解。
原始数据的散点图是这样的:
逆序覆盖较大值之后,点的值变成了如下图所示:
清楚地看到,这里形成了一个阶梯形状的图像。
这里需要解释的是Recall会随着预测的框的数量增加而越来越大,for循环实现的其实是对于所有的Recall取得最大的Precision,代码中倒序传播较大值就是可以使得Precision遇到一个大的值就上一个“阶梯”。
接下来就是处理Recall了
i = np.where(mrec[1:] != mrec[:-1])[0]
>>> i = [0 1 5 8 9 10]
代码意思是说将Recall临近的两个值进行比较,返回不相等的值的索引,这就是选择所有不相等的Recall。
ap = np.sum((mrec[i + 1] - mrec[i]) * mpre[i + 1])
>>> ap = (0.14-0)*1 + (0.29-0.14)*1 + (0.43-0.29)*0.5 +
(0.57-0.43)*0.5 + (0.71-0.57)*0.5 + (1-0.71)*0 = 0.5
以上公式实际求取的是下图的面积: