學習目标檢測一定少不了的評測方式, 就是透過recalls, precisions, 來計算出類别的AP, 以及最後所有AP的平均值 mAP(mean Average Precision) 也就是我們最關心的數值
這邊先簡單的了解一下confusion matrix, 也就是所謂的混肴矩陣, 我個人不覺得這是一個很好的翻譯, 最好記得英文就行, 在分類任務中這是一個非常重要的評測名額
下圖是一個基本的confusion matrix
下面我直接帶入例子會比較好解釋
假設你的資料集一共是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):
說幾個重要的參數就好
- det_results 就是檢測到的結果
- gt_bboxes 就是ground truth 的框
- gt_labels 就是類别标簽, 比如人是1, 汽車是2
- gt_ignore 這如果沒有标注difficulty 不用理會
- 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表示
- 第一張圖label = [1, 1, 2, 1, 1] 也就是說有四個人一台車, 一共五個ground truth
- 第二張圖 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 不遠了啊 !
好了 這隻是上半場
由于篇幅的關系 我臨時決定分成兩章來寫完
說老實話, 這要是一篇文章能解決我也是佩服佩服