1. 引言
Average Precision的原理可见 多标签图像分类任务的评价方法-mAP ,个人感觉文章对计算流程、PR曲线(Precision-Recall)、图例都展示得比较好。
另外,忘记从哪篇文章中提到的一句讲得特别精髓,大致意思如下:
对confidence socre降序排列后,再把第k行作为top-k的分界线,进行Precision和Recall的计算。
所以我们计算AP是对conf排序后,从上往下的计算,一直从top-1到top-N。
通常情况下,从top-1到top-N伴随的是召回率的提升和精确率的下降。
理想情况下是两者恒为1。
2. 代码实现
这里我们假设某个类的precision和recall序列都已求出,本文主要关注AP的实现:
def voc_ap(rec, prec, use_07_metric=False):
"""
rec: recall序列,单调递增,[0.5, 0.5, 1]
prec: precision序列,呈锯齿状,[1, 0.5 , 0.66]
use_07_metric: 是否采用VOC07的AP计算方式,True or Flase
这里rec和prec的每个值,rec[i]和prec[i]分别代表top-i时,recall和precision是多少
"""
if use_07_metric:
# 11 point metric
ap = 0.
# 设定一组阈值, 然后对于大于这个阈值的recall,都能取到最大的precision
# 因为precision呈锯齿状,所以P(r'>r)的最大值有可能在更后面
for t in np.arange(0.,1.1,0.1):
if np.sum(rec >= t) == 0:
p = 0
else:
# p取recall大于t的结果里面,precision最大的值
p = np.max(prec[rec>=t])
ap = ap + p/11. # 11点求均值
else:
# correct ap caculation
mrec = np.concatenate(([0.],rec,[1.])) # 左接0,右接1
mpre = np.concatenate(([0.],prec,[0.])) # 左右接0
# precision反过来遍历
for i in range(mpre.size -1, 0, -1):
# 这里目的是让mpre[i-1]不小于mpre[i]
# 即平滑了PR曲线的锯齿状
# 例如[0.5, 0.66] -> [0.66, 0.66]
# 平滑过后的mpre单调递减
mpre[i-1] = np.maximum(mpre[i-1],mpre[i])
# 找出mrec变化的地方
# mrec = [0. , 0.5, 0.5, 1. , 1. ]
# i = [0, 2]
i = np.where(mrec[1:] != mrec[:-1])[0]
# mpre = [1, 1, 0.66, 0.66, 0]
# 算法是针对每个recall值,取r'>r的precision最大值
# 因为mpre已经平滑了PR曲线,所以r'>r的P最大值就是mpre[i+1](因为mpre单调递减)
# 从这里mrec[i+1] - mrec[i]可以看出,前面在rec右接1,就是为了处理这里的边界
# 而prec右接0只是为了对齐mrec
# rec左接0是为了确保左边有0可以减,用来算x轴的距离
# 而prec左接0也是为了对齐
# 最后是求平滑后的PR曲线的面积
# 上面超链接写的是Precision的平均值,好像不对
ap = np.sum((mrec[i + 1] - mrec[i]) * mpre[i + 1])
return ap
上面的AP是针对某个类别进行计算的,mAP只要对所有类别(剔除背景)的AP求平均就好啦~
3. 深入思考
在预测任务中,Precision和Recall是两个比较对立的指标(当然具体的业务场景可能对特定的指标有更高的要求)。
从top-1到top-N,target在不断被预测中,同时也不可避免的会有误判情况,因此Recall会不断的提高,Precision也会随着误判而降低。
根据VOC07求AP的算法思路,划分11个阈值,并求
P
m
a
x
(
r
>
t
)
P_{max}(r>t)
Pmax(r>t),t
为阈值。考虑到此时的PR曲线都是折线图,因此就是取t右边最高的峰值。
语义就是让模型的召回率最少达到 t
时,精确率最高能达到
P
m
a
x
P_{max}
Pmax,再对11个t
求平均。
然而阈值的分布还是人为设定的,并且阈值之间的距离仍然不可忽略,对于两个模型的mAP值对比,难免会有误差。
VOC10之后采用的mAP计算方式,在继承了原算法的思路:召回率为t时,精确率最高能达到 的同时,通过积分的方式减少误差(PR曲线下方的面积)。
并且top-1到top-N并不是每次都能成功命中target,也即召回率的变化次数并不高,因此算法仅捕捉召回率变化瞬间,用平滑PR曲线的方式获取最大的Precision。