mAP是目標檢測中的基本指標,詳細理解有助於我們評估算法的有效性,並針對評測指標對算法進行調整。
1.基本概念定義
- 在目標檢測中IoU為檢測框與GroundTruth重疊的比例,如果大於0.5則算作正確True,小於0.5則算作錯誤False;
其中0.5是VOC比賽中設定的閾值,具體見論文"The PASCAL Visual Object Classes (VOC) Challenge"Page_11。需要說明的是,閾值的改變會影響檢測結果,TP\FP\FN\TN等都會隨着閾值的改變而改版。
具體的IoU定義和計算如下圖所示:
對於目標檢測來說,IoU大於閾值、檢測到框算作是positive;
- 真陽性TP:正確樣本預測為正,在目標檢測中為IoU大於閾值記為TP;
- 假陽性FP:錯誤樣本預測為正,檢測框的IoU小於閾值記為FP;
- 假陰性FN:正確樣本預測為負,沒有檢測到框;
- 真陰性TN:錯誤樣本預測為負;
對每個樣本的檢測可以通過上面TP/FP/FN的計數來計算出精度與召回率。
-
Precision精度,即TP/(TP+FP)。指所有結果中正確的檢索所占比例。結果中包含了真陽性結果TP和假陽性結果FP。其含義為所有被預測為正類的結果(其中也包含了被錯誤預測成正類的FP)中預測正確的結果所占比例。
-
Recall召回率,即TP/(TP+FN)。指模型查找出正確對象的概率。其中包含了正確預測的正類TP,也包括了將正類錯誤預測成負類的FN。召回率的本質可以理解為查全率,即結果中的正樣本占全部正樣本的比例。
-
PR:精度-召回率( )曲線。這條曲線的兩個變量程負相關,精度越高,召回率越低;召回率越高,精度越低。如果將所有對象都預測為正類,沒有被錯誤預測成負類的正類(FN為0)那么召回率將為100%;如果將所有對象的預測都為負,沒有被錯誤預測成正的樣本,那么精度就將為100%,這兩個指標間存在着此消彼長的關系,理想的曲線是向右上方凸出的、包圍面積大的曲線。
-
AP均勻精度Average Precision:PR曲線下所圍成的面積,面積越大越好;這里的average指的是針對不同recall的平均精度。
-
mAP平均均勻精度,mean Average Precision,指各類計算出AP在不同類別上的均值。這里的mean指的是對於檢測算法在數據集上各類對象的表現。
-
Top-1/Top 錯誤率:指的是預測概率最高的類別輸入目標類別的准確率。top1就是最高類別為目標類別的准確率,top5就是最高的五個中命中目標類別的准確率。
2.AP計算
要計算mAP,首先需要計算每一個類別的AP值,主要有兩種方法來計算PR曲線下的均值。
- 一種方法是選取
等11個值時,其recall>當前recall對應精度precision的最大值,並由這11個最大精度值求取均值即為AP。這種方法也稱為11-point interpolated average precision。根據論文對AP的定義:
where:
舉個栗子,如果對於待檢測的圖像里,我們要檢測犬這個類別並計算AP,網絡針對這些圖片輸出了bbox(如果我們取IoU>0.5為正例的話,對應groundTruth為1):
box num | confidence | label-GT |
---|---|---|
0 | 0.9 | 1 |
1 | 0. 85 | 1 |
2 | 0. 7 | 0 |
3 | 0. 6 | 0 |
4 | 0. 7 | 1 |
5 | 0. 7 | 0 |
6 | 0. 65 | 1 |
7 | 0. 78 | 1 |
8 | 0. 71 | 1 |
8 | 0. 7 | 1 |
9 | 0. 8 | 0 |
10 | - | 1 |
11 | 0.51 | 1 |
那么此時我們可以看到,這是十個獨立的框框,其中正例為TP=7(0,1,4,6,7,8,10,11),負例FP=5(2,3,5,9,重復檢測的8 也算),GT10(一共有八個GT=1)沒有檢測到那么FN=1.所以根據上面的計算公式我們將按照執行度排序后給出對應的精度和召回率:
box num | confidence | label-GT | rank | precision | recall |
---|---|---|---|---|---|
0 | 0.9 | 1 | *1 | pr = 1/(1+0) = 1 | recall = 1/8=0.13 |
1 | 0. 85 | 1 | *2 | pr =2/(2+0)=1 | recall =2/8 = 0.25 |
9 | 0. 8 | 0 | *3 | pr = 2/(2+1)=0.66 | recall = 2/8 =0.25 |
7 | 0. 78 | 1 | *4 | pr = 3/(3+1) = 0.75 | recall =3/8 =0.38 |
8 | 0. 71 | 1 | *5 | pr =4/(4+1) =0.8 | recall = 4/8 = 0.5 |
2 | 0. 7 | 0 | *6 | pr = 4/(4+2)= 0.66 | recall = 4/8 =0.5 |
4 | 0. 7 | 1 | *7 | pr = 5/(5+2) = 0.71 | recall = 5/8 = 0.63 |
5 | 0. 7 | 0 | *8 | pr = 5/(5+3) = 0.63 | recall = 5/8 =0.63 |
8 | 0. 7 | 1 | *9 | pr = 5/(5+4) =0.55 | recall = 5/8=0.63 |
6 | 0. 65 | 1 | *10 | pr =6/(6+4) = 0.6 | recall = 6/8 = 0.75 |
3 | 0. 6 | 0 | *11 | pr = 6/(6+5) =0.55 | recall = 6/8= 0.75 |
11 | 0.51 | 1 | *12 | pr = 7/(7+5) = 0.54 | recall = 7/8=0.88 |
10 | - | 1 | *12 | pr = 7/(7+5)=0.54 | recall = 7/8 =0.88 |
隨后計算AP,選取Recall>=[0:0.1:1]精度最大值:1,1,1,0.8,0.8,0.8,0.71,0.6,0.54.0,0,。AP = sum(1,1,1,0.8,0.8,0.8,0.71,0.6,0.54.0,0)/11 = 0.66.
隨后,對於貓貓、狗狗、浣熊、熊貓等等各個類別的AP都求出來以后在類別上做平均就是mAP值了。
- 另一種方法是針對每一個不同Recall值(M個正例就有M個recall值),選取大於Recall時的最大精度值,並基於這些值計算AP,也即計算每一個recall區間對應pr曲線下的面積。
上面例子中有八個正例,對於的recall就出recall>=對應recall(包括0,1)的精度最大值:
ref
https://www.zhihu.com/question/53405779/answer/419532990
https://datascience.stackexchange.com/questions/25119/how-to-calculate-map-for-detection-task-for-the-pascal-voc-challenge
3.mAP計算
mAP指的是對所有類別都計算好AP,然后在類別上求平均即可得到mAP
4.實際代碼(以Gluon為例)
#line 194
#https://github.com/dmlc/gluon-cv/blob/master/gluoncv/utils/metrics/voc_detection.py
def _recall_prec(self):
""" get recall and precision from internal records """
n_fg_class = max(self._n_pos.keys()) + 1
prec = [None] * n_fg_class #每個類別中樣本數量的字典,定義精度prec
rec = [None] * n_fg_class #定義召回率recall
for l in self._n_pos.keys():
score_l = np.array(self._score[l]) #confidence,預測bbox的置信度
match_l = np.array(self._match[l], dtype=np.int32) #預測結果,后面用於匹配正樣本和負樣本,match上的為正例,預測正確。
#self._score,self._match,self._n_pos,參考代碼的line 43-45,來自於from collections import defaultdict
order = score_l.argsort()[::-1] #對預測結果置信度進行排序
match_l = match_l[order] #按照置信度重新排序樣本
tp = np.cumsum(match_l == 1) #正例,正確的個數TP
fp = np.cumsum(match_l == 0) #負例,錯誤的個數FP
# If an element of fp + tp is 0,
# the corresponding element of prec[l] is nan.
with np.errstate(divide='ignore', invalid='ignore'):
prec[l] = tp / (fp + tp) #按照順序計算精度
# If n_pos[l] is 0, rec[l] is None.
if self._n_pos[l] > 0:
rec[l] = tp / self._n_pos[l] #計算召回率(查全率,分母為所有正確的個數)
return rec, prec
#
# 得到精度和召回率的array后,就可以計算AP了:
#
```python
#方法一
def _average_precision(self, rec, prec):
""" calculate average precision, override the default one, special 11-point metric Params: ---------- rec : numpy.array cumulated recall prec : numpy.array cumulated precision Returns: ---------- ap as float """
if rec is None or prec is None:
return np.nan
ap = 0.
for t in np.arange(0., 1.1, 0.1): #十一個點的召回率,對應精度最大值
if np.sum(rec >= t) == 0:
p = 0
else:
p = np.max(np.nan_to_num(prec)[rec >= t])
ap += p / 11. #加權平均
return ap
#方法二
def _average_precision(self, rec, prec):
""" calculate average precision Params: ---------- rec : numpy.array cumulated recall prec : numpy.array cumulated precision Returns: ---------- ap as float """
if rec is None or prec is None:
return np.nan
# append sentinel values at both ends
mrec = np.concatenate(([0.], rec, [1.]))
mpre = np.concatenate(([0.], np.nan_to_num(prec), [0.]))
# 將精度和召回率值全部鏈接起來
# compute precision integration ladder
# 整數間隔內的最大值,對應正例的recall計算max precision
for i in range(mpre.size - 1, 0, -1):
mpre[i - 1] = np.maximum(mpre[i - 1], mpre[i])
# 計算精度
# look for recall value changes
i = np.where(mrec[1:] != mrec[:-1])[0]
# sum (\delta recall) * prec 將所有的精度和召回率加起來得到ap
ap = np.sum((mrec[i + 1] - mrec[i]) * mpre[i + 1])
return ap
ref:
https://www.zhihu.com/question/53405779/answer/419532990
code:https://github.com/dmlc/gluon-cv/blob/master/gluoncv/utils/metrics/voc_detection.py
Detectron:https://github.com/facebookresearch/Detectron/blob/master/detectron/datasets/voc_eval.py
https://blog.csdn.net/chengyq116/article/details/81290447
https://blog.csdn.net/zdh2010xyz/article/details/54293298
https://blog.csdn.net/littlehaes/article/details/83278256
https://blog.csdn.net/lz_peter/article/details/78133069
https://www.jianshu.com/p/1afbda3a04ab
https://datascience.stackexchange.com/questions/25119/how-to-calculate-map-for-detection-task-for-the-pascal-voc-challenge
https://en.wikipedia.org/wiki/Precision_and_recall
Threshold P11 of VOC :http://homepages.inf.ed.ac.uk/ckiw/postscript/ijcv_voc09.pdf
Voc evalutaion:http://host.robots.ox.ac.uk/pascal/VOC/voc2012/htmldoc/devkit_doc.html#SECTION00044000000000000000
code:https://github.com/dmlc/gluon-cv/blob/master/gluoncv/utils/metrics/voc_detection.py
關於TP/FP/FN以及精度召回率的另一種解釋
如果使用目標檢測的概念來輔助理解的話,我們可以將算法檢測框看做是所有預測為正的區域(包含了TP,FP),而將原始標注框看做是所有實際正確的區域(包含了FN,TP):
精度:如果利用目標檢測中框的概念來理解可以看做是下圖的表示,其中檢測框為預測認為Positive的、重疊部分為實際正確的部分:
召回率:而原始的標注框可以表示所有的True、重疊部分為實際正確的部分,也即為召回率: