天天看點

用商湯的mmdetection 學習目标檢測中的 Recalls, Precisions, AP, mAP 算法 Part1

學習目标檢測一定少不了的評測方式, 就是透過recalls, precisions, 來計算出類别的AP, 以及最後所有AP的平均值 mAP(mean Average Precision) 也就是我們最關心的數值

這邊先簡單的了解一下confusion matrix, 也就是所謂的混肴矩陣, 我個人不覺得這是一個很好的翻譯, 最好記得英文就行, 在分類任務中這是一個非常重要的評測名額

下圖是一個基本的confusion matrix

用商湯的mmdetection 學習目标檢測中的 Recalls, Precisions, AP, mAP 算法 Part1

下面我直接帶入例子會比較好解釋

假設你的資料集一共是2分類, 第一類是人, 第二類是汽車

而測試集隻有兩張圖檔

Actual Value and Predicted Value

Actual value :實際的值, 在目标檢測領域中, 我們可以了解為ground truth, 就是人或者是汽車在圖檔上的真實坐标

predicted value:這個就很好了解了, 就是指我們模型預測出來的坐标

pos 和 neg 這邊不先定義, 我講下去你就知道了

TP FP FN TN

這裡通常很容易被弄混, 難怪叫做混肴矩陣, 跟着我的步伐走就沒問題,強烈建議拿出紙筆把矩陣畫一次就清楚了

  • TP (TruePositive): 正确預測出類别的 (正确識别為人或是汽車)
  • FP (FalsePositive):錯誤預測類别的 (人當成汽車)
  • FN(FalseNegative): ground truth中沒有被檢測到的
  • TN(TrueNegative):理論上來講就是檢測出不是這個類别的數量, 但是并不适用在目标檢測領域, 因為通常一張圖後會有非常多的bbox是沒有檢測到groundtruth的, 是以這無法計算, 并不影響, 因為也用不上

Recalls and Precisions

Recalls

可以叫做

查全率

又或者是

召回率

, 可以當做是所有類别x中預測為正确的

也就是 T P T P + F N \frac{TP}{TP+FN} TP+FNTP​ , 對應圖檔的藍框公式很直覺, 就是所有的ground truth(所有測試集中的樣本每張圖檔上的人 或者 汽車的總數)裡面檢測出人 或者 汽車的數量

比如圖中有5個标注為汽車的ground truth, 那麼你檢測到的汽車有兩個, 那麼對于這張圖, 汽車類的 recalls 就是 2/5

Precisions

可以叫做

精準率

, 就是你檢測到結果裡面, 實際上真的正确的為多少? T P T P + F P \frac{TP}{TP+FP} TP+FPTP​

TP就是檢測正确的, FP就是檢測錯誤的, 那麼加起來不就是你所有檢測的結果? 對應圖中紅框

比如圖中有3個人的ground truth, 你檢測到的也是三個框, 但是其中隻有一個框檢測吻合到你的ground truth,另外兩個檢測成人了, TP = 1, FP = 2, 那麼precision 就是 1/3

到這邊如果沒明白就先理清楚在往下看吧

首先來用mmdetection 的檢測代碼

mean_ap.py

來說明會更清晰

我不打算放出全部代碼, 這樣太亂了 我們看最主要的部分加深印象就行 !

首先看到eval_map這個函數, 看名字就很直覺 evaluation mAP

def eval_map(det_results,
             gt_bboxes,
             gt_labels,
             gt_ignore=None,
             scale_ranges=None,
             iou_thr=0.5,
             dataset=None,
             print_summary=True):
           

說幾個重要的參數就好

  1. det_results 就是檢測到的結果
  2. gt_bboxes 就是ground truth 的框
  3. gt_labels 就是類别标簽, 比如人是1, 汽車是2
  4. gt_ignore 這如果沒有标注difficulty 不用理會
  5. iou_thr 就是檢測出的和groud truth 的 iou 門檻值

也就是說如果要檢測出recall precision mAP這些名額, 就要以上這些重要的值

那麼計算recall precision 之前 我們就得先算出 TP FP FN 這些重要的名額, 所有一開始的矩陣是不是很重要?

函數

tpfp_default

就是負責計算出TP FP的

我們需要以下參數, 注意該函數是針對單張圖像的

def tpfp_default(det_bboxes, 
				gt_bboxes, 
				gt_ignore, 
				iou_thr, 
				area_ranges=None):
           

我們的訓練集隻有兩張

如果人label = 1, 汽車label=2, 下面直接用vector表示

  1. 第一張圖label = [1, 1, 2, 1, 1] 也就是說有四個人一台車, 一共五個ground truth
  2. 第二張圖 label = [2, 2, 1, 2] 三台車 一個人的ground truth

搬出ground truth

ground truth 最後面的#1 or #2表示label

#groubd
gt_bbox1 = np.array([[358, 288, 498, 387], #1
                    [356, 425, 525, 570], #1
                    [377, 119, 478, 189], #2
                    [180, 68, 314, 142], #1
                    [417, 159, 575, 240]], dtype=np.float32)#1

gt_bbox2 = np.array([[258, 188, 398, 287], #2
                     [256, 325, 425, 470], #2
                     [277, 19, 378, 89], #1
                     [280, 168, 414, 242]], dtype=np.float32)#2
           

然後我們檢測到的結果是下面這樣, 都是bbox的坐标值, 一共兩張圖檔, 圖上都會有每個檢測到的類别的坐标

檢測到的結果 det_results

'''det_results 圖檔 _ 類别'''
det_results1_1 = np.array([[359, 289, 499, 388], 
                    [346, 415, 515, 560], 
                    [367, 109, 468, 179], 
                    [190, 78, 324, 152], 
                        [430, 172, 588, 253]], dtype=np.float32)

det_results1_2 = np.array([[259, 189, 399, 288],
                        [236, 315, 415, 440], 
                        [267, 9, 368, 79],
                        [290, 178, 424, 252]], dtype=np.float32) 
                        
det_results2_1 = np.array([[359, 289, 499, 388],  
                    [346, 415, 515, 560], 
                    [367, 109, 468, 179], 
                    [190, 78, 324, 152],
                        [430, 172, 588, 253], 
                          [230, 72, 388, 53]], dtype=np.float32)

det_results2_2 = np.array([[259, 189, 399, 288], 
                        [236, 315, 415, 440], 
                        [267, 9, 368, 79], 
                        [290, 178, 424, 252],
                          [159, 89, 299, 188]], dtype=np.float32) #not ok
           

可以看出det_result1_1 表示第一張圖的label 1 檢測到的框

那麼det_result2_2 就表示第二張圖檢測到label 2的框

是以可以總結

label1 一共檢測出11個(TP+FP)

label2 一共檢測出 9 個 (TP+FP)

好了, 回到

tpfp_default

為了要檢查出模型檢測的是不是正确的, 也就是TP, 我們就用到了

iou_thr

這個門檻值

一般會是0.5, 那麼還要計算出IOU(交并比), 什麼是IOU請直走左轉找到人就問, 如果連這都寫, 篇幅會太長

透過以下的函數進行計算

ious = bbox_overlaps(det_bboxes, gt_bboxes)
           

得出來的ious會長這樣子

#這是label 1的第一張圖檢測與gt的ious結果
 [[0.9665272  0.         0.         0.        ]
 [0.         0.7804878  0.         0.        ]
 [0.         0.         0.         0.05691057]
 [0.         0.         0.6701031  0.        ]
 [0.         0.         0.         0.6295463 ]]
 
#這是label1 的第二張圖檢測與gt的結果
 [[0.        ]
 [0.        ]
 [0.        ]
 [0.03430409]
 [0.        ]
 [0.        ]]
           
#這是label 2的第一張圖檢測與gt的ious結果
 [[0.00107885]
 [0.        ]
 [0.        ]
 [0.03430409]]
 
 #這是label 2的第二張圖檢測與gt的ious結果
 [[0.9665272  0.         0.36517328]
 [0.         0.6413269  0.        ]
 [0.         0.         0.        ]
 [0.41336057 0.         0.6701031 ]
 [0.00149158 0.         0.01764335]]
           

一臉懵?

沒事的, 我剛看到也是懵

label1 圖1 檢測到的不是五個bbox嗎 那麼拿去和gt計算 就會有五個分數了

那圖2 檢測到的6個bbox 也當然就有6個分數了

不要太注意shape不同, 隻要注意分數就好

很明顯的模型在檢測 label1 的時候, 在圖1的表現好很多 !我們發現到

[0. 0. 0. 0.05691057]

這行分數很低接近于0,因為圖1的label是[1, 1, 2, 1, 1]

在第三個值是label 2, 是以算出來的IOU幾乎為0, 因為并不屬于label1的

在來看到label2 的iou

#這是label 2的第一張圖檢測與gt的ious結果
 [[0.00107885]
 [0.        ]
 [0.        ]
 [0.03430409]]
           

分數基本都趨近于0, 就表示模型在圖1 完全沒找到什麼都沒檢測到

你可以比較一下det_result1_2的坐标和gt_bbox1的坐标就很清楚了, 坐标完全沒重合的感覺

如此一來我們就有了label1 和 label2 所有檢測到的框和ground truth的 IOU值了

得到了IOU在透過一開始給的

iou_thr

門檻值, 不就能篩選出誰是TP 誰是 FP了嗎?

來, 咱們在複習一次 , 跟着我大聲念!

檢測大于門檻值的叫做TP(TruePositive)

低于門檻值的也就是錯誤的叫做FP (FalsePositive)

喝口水繼續

我們将剛剛的ious 進行整理

ious_max = ious.max(axis=1)
ious_argmax = ious.argmax(axis=1)
           

輸出會是下面這個樣子, 是不是, 說shape先别在意的 !反正都會變這個樣子

ious_max [0.9665272 0.7804878 0.05691057 0.6701031 0.6295463 ]

ious_argmax [0 1 3 2 3]

接下來就是透過門檻值篩選, 過程代碼比較多, 不全寫出來

if ious_max[i] >= iou_thr:
     matched_gt = ious_argmax[i]
           

tpfp_default

函數輸出就會是像這樣

tp: [[1. 1. 0. 1. 1.]]

fp: [[0. 0. 1. 0. 0.]]

是不是一目了然? 也能夠對應到分數?iou高說明檢測正确并且歸納在TP 用1表示

而分數低的自然就是FP了 用0表示

拿到了TP FP 就離我們的recalls 和 precisions 不遠了啊 !

好了 這隻是上半場

由于篇幅的關系 我臨時決定分成兩章來寫完

說老實話, 這要是一篇文章能解決我也是佩服佩服

繼續閱讀