Python计算PASCAL VOC2012数据集AP分类任务指标
前言
PASCAL VOC2012数据集竞赛,主要有两个任务,分类和分割,计算分割任务指标的代码已经满大街都是了,这个mAP代码应该是比较权威的了。但是计算分类的指标,我是找了很久都没有找到,只好自己写代码计算一下。
事先说明,计算分类任务AP指标的时候,2010年之前的竞赛和之后的竞赛,虽然都用了AP指标,但计算方式不同,之前的那个算法既然被淘汰了,就不用深究了,这里说的AP指标计算方法,是2010年之后的。两者的区别是,之前的计算方式是通过给Recall取10个阈值,找11个Precision最大值,取平均值;之后的计算方式是每个Recall取一个Precision的最大值,最后算平均。听起来好像之前的计算方式更简单,其实是现在的计算方式更简单。
这里是VOC2012竞赛的说明文档
这里是分类任务AP指标的排行榜。
要计算AP,首先就要了解精确率和召回率;要了解精确率和召回率,首先要了解TP,TN,FP,FN。这玩意儿可以说,如果不彻底搞懂,每次用的时候都得重复看一遍。所以还是彻底搞懂的好。一般讲这个的博客都会给一个二维表格,那个表格是很优雅,但是真的看不懂,记不住。
TP,TN,FP,FN
这里说下我的理解,保证一次记住。T(True)代表分对了,F(False)代表分错了,P(Positive)代表分为正样本,N(Negtive)代表分为负样本。现在问,TP是什么意思,先看后一个字母,是P,代表分成正样本了,然后再看T,代表分对了,那么TP就是分对的正样本,这样自然就知道,groundtruth是什么,模型预测的是什么了。再来一个,FN,N代表负样本,F代表分错了,说明人家本来是一个正样本,结果模型分成负样本了。
召回率(Recall)和精确率(Precision)
有了这个基础,再看精确率和召回率的公式就好多了。通俗的解释,
召回率(Recall):总共有100个正样本,模型预测出来80个,那召回率就是80%,召回率就是单指正样本来说的,只关心你分对的正样本占总共正样本个数的比例。顺便问一下,这80个正样本用TPFN这四个字母来表示,是谁呢?答案应该是TP。那总的正样本个数呢?用字母来表示是谁?应该是TP+FN,没错,是FN,这是和精确率最大的不同。因为FN的groundtruth是正样本,只不过分成了负样本
精确率(Precision):模型分了100个正样本,那就看看这100个里面有多少是对的,多少是错的,例如有70个是正的,那就是70%。同样的,这70个用TPFN来表示,就是TP,精确率的总个数呢,就应该TP+FP。怎么变成FP了呢?因为精确率是以模型预测的正样本为总体的,这里面肯定有对有错,对的就是TP,错的就是FP了
顺便说一下准确率(Accurary),准确率是你没学机器学习之前,自己感觉的那个,就叫准确率。比如给我1000张照片,可能有狗可能没狗,那对于每一张照片,我给一个结果,如果我说对了,就加一分,说对有两种情况,一种是有狗我说有狗,一种是没狗是说没狗。对于有狗我说有狗,这个样本用字母来表示就是TP,没狗我说没狗,用字母来表示就是TN,所以分子就是(TP+TN),分母就是总共的样本个数了。
最后,来看一下这三个公式,是不是豁然开朗了呢
R
e
c
a
l
l
=
T
P
T
P
+
F
N
Recall=\frac{TP}{TP+FN}
Recall=TP+FNTP
P
r
e
c
i
s
i
o
n
=
T
P
T
P
+
F
P
Precision=\frac{TP}{TP+FP}
Precision=TP+FPTP
A
c
c
u
r
a
r
y
=
T
P
+
T
N
T
P
+
F
P
+
T
N
+
F
N
Accurary=\frac{TP+TN}{TP+FP+TN+FN}
Accurary=TP+FP+TN+FNTP+TN
如何画PR曲线
知道了,Precision和Recall,我们先不算AP,先画一下这个PR曲线,
这是我随机了20个样本数据画出来的,你要画PR曲线,其实就是计算N个Precision和Recall就可以了,然后把Recall当做横坐标,Precision当做纵坐标,用python的plot就可以画了。等等,为什么会有N个Precision和Recall呢?撑死也就一对Precision和Recall呀,N个哪来的呢?N又具体等于几呢?
我们看一个例子,不过就不用20个数据了,我们用5个数据,
pred | gt |
---|---|
0.86 | 1 |
0.24 | 1 |
0.63 | 1 |
0.34 | 0 |
0.14 | 0 |
pred是模型预测的结果,gt是groundtruth,现在我们来计算Precision和Recall的数组
首先,我们选一个超大的阈值
∞
\infty
∞,看有哪个样本的pred超过这个阈值的,如果超过了,就把这个样本提取出来,那么现在你看,Recall是多少?不用想就是0,为什么呢?因为一个样本都没有,分子是0,分母是3(分母是3是因为gt只有3个,即TP+FN=3)。那Precision呢,因为一个样本都没有,那我就不用预测了呀,那就算我对吧,所以Precision是1,由此我们跨出了第一步:
Precision | Recall | Threshold |
---|---|---|
1 | 0 | ∞ \infty ∞ |
接下来,把阈值降低,降到多少呢,就降到pred里面阈值最大的那个,即0.86,好了,现在阈值变了,我们是不是可以提取出来一个样本了呢,我们就把pred=0.86,gt=1提取出来了。只要我们提取的样本里阈值大于等于0.86,我们就算他是正样本,OK,现在算一下Precision和Recall。Precision=1,因为我们这个样本预测为1,gt也是1,预测对了,而且总共就一个样本。Recall是1/3,总算有一个样本被召回进来了。于是,我们得到:
Precision | Recall | Threshold |
---|---|---|
1 | 0 | ∞ \infty ∞ |
1 | 1/3 | 0.86 |
下面,重复就可以了,阈值接着降低,下一个我们选0.63,再接着选0.34,0.24,得到下表
Precision | Recall | Threshold |
---|---|---|
1 | 0 | ∞ \infty ∞ |
1 | 1/3 | 0.86 |
1 | 2/3 | 0.63 |
2/3 | 2/3 | 0.34 |
1/3 | 1 | 0.24 |
ps:这个表格有一处错误,自己计算一下,看看是哪里错了。小错误,不影响阅读,如果找到了,接着读,就可以验证你找的对不对*
有一个问题,为什么阈值不接着往下取了呢,还有0.14呢,这是因为,Recall已经是1了,说明已经全部召回了,就没必要再搞了
由此,我们得到了Precision和Recall的数组,放到plot画出来就得到了PR曲线
AP指标
OK,现在我们会计算Precision和Recall,还会画PR曲线了,终于可以计算AP指标了。其实已经非常简单了。看我们刚才列的表格,把刚才的第一列去掉,因为那个是为了画图方便才设置的(0,1),算AP的时候是不需要的。
Precision | Recall | Threshold |
---|---|---|
1 | 1/3 | 0.86 |
1 | 2/3 | 0.63 |
2/3 | 2/3 | 0.34 |
1/3 | 1 | 0.24 |
可以看到Recall有三个等级,1/3,2/3和1,2/3比较特殊,因为他对应的Precision有两个,取哪个呢?取大的。最后,把这三个Recall对应的Precision求个平均值就可以啦。
这里的AP=0.9166666667,你算对了吗?
代码
最后,我们附上一段代码,都是随机数据,可以直接运行
import numpy as np
import matplotlib.pyplot as plt
from sklearn.metrics import average_precision_score as ap
from sklearn.metrics import precision_recall_curve as pr
def main():
data_len = 5 # 更改这个值来尝试更多数据
np.random.seed(1001)
label = np.random.randint(0, 2, size=data_len)
score = np.random.choice(np.arange(0.1, 1, 0.01), data_len)
presicion, recall, thresholds = pr(label, score)
ap_value = ap(label, score)
print(value)
print(label)
print(score)
print(presicion)
print(recall)
print(thresholds)
print(ap_value)
## 画图
plt.plot(recall, presicion, 'k')
plt.title('PR')
plt.xlim([-0.01, 1.01])
plt.ylim([-0.01, 01.01])
plt.ylabel('Precision')
plt.xlabel('Recall')
plt.show()
if __name__ == "__main__":
main()
print('Done.')
致谢
感谢以下三篇博客的作者,参考了他们的内容和代码,才让我完全理解了AP指标的计算过程
花心大罗博l
东写西读李老湿
张乐乐章