本篇不講任何正負樣本定義的方法以及各種采樣的方法,只從實際訓練角度結合量產經驗思考正負樣本背后的本質問題。
1. 什么是正負樣本?
對于YOLO系列的結構,正負樣本就是feature map上的每一個grid cell(或者說對應的anchor)。
對于RCNN系列的結構,RPN階段定義的正負樣本其實和YOLO系列一樣,也是每一個grid cell。RCNN階段定義的正負樣本是RPN模塊輸出的一個個proposals,即感興趣區(qū)域(region of interesting,roi),最后會用RoIPooling或者RoIAlign對每一個proposal提取特征,變成區(qū)域特征,這和grid cell中的特征是不一樣的。
對于DETR系列,正負樣本就是Object Queries,與gt是嚴格的一對一匹配。而YOLO,RCNN是可以多對一的匹配。
通常情況下,檢測問題會涉及到3種不同性質的樣本:
正樣本(positive)
對于positive,它存在的意義是讓模型具備判斷前景的能力,不僅要讓模型知道圖像上這個位置存在前景目標,還要把具體的位置用bbox框出來,所以一旦判定某個grid cell或者proposal是正樣本,你就需要對其負責cls+bbox的訓練。
忽略樣本(ignore)
ignore最大的用處就是可以處理模棱兩可的樣本,以及影響模型訓練的樣本。所以對于ignore,對其不負責任何訓練,或者對其負責bbox的訓練,但是不負責cls的訓練。
負樣本(negative)
對于negative,它存在的意義是讓模型具備區(qū)分背景的能力,不讓模型產生背景的誤檢,所以對于negative只負責cls的訓練,不負責bbox的訓練。
看到有同學問:能不能舉幾個實際ignore的例子理解一下。
那舉幾個例子:
1. 如果你發(fā)現遮擋達到80%以上的vehicle或者模糊的vehicle,如果當正樣本訓練的話,loss收斂不了,那么就可以把這些原本是正樣本的納入ignore中,不參與訓練。如果你確實不想把遮擋80%以上的vehicle召回出來,那么歸入負樣本。
2. 卡車上運載著的多層passenger car,不想把這種passenger car召回,那么可以把這些原本是負樣本的納入ignore,因為這些負樣本顯然和正常的passenger car的正樣本特征存在一定矛盾。
3. 像bus這種,由于車身鏡像的作用,就把旁邊的vehicle照進來。
2. 怎么定義哪些是正樣本/ignore/負樣本
常規(guī)使用的方法:
借助每個grid cell中人為設置的anchor,計算其與所有gt(ground truth)的iou,通過iou的信息來判定每個grid cell屬于positive/ignore/negative哪種。
以當前gt為中心的一定范圍內,去判定每個grid cell屬于哪種樣本。
在具體的自動駕駛量產項目中,往往會根據實際需求,比如對precision和recall的要求,在與gt匹配的邏輯中,會從類別、大小等角度去考慮,另外還會考慮特殊標記的gt框(hard、dontcare)。
有以下幾個原則:
數量少的類別A,為其盡可能匹配適當多一點的anchor,數量多的類別B,為其匹配少量且高質量的anchor。這樣做目的是提高A的recall,提高B的precision,保證每個batch中,各類別間生成的正樣本數量趨于1:1
為小目標匹配高質量的anchor,忽略其周圍低質量的anchor。這樣做是為了減少小目標的誤檢,可能在一定程度上犧牲了召回。
對于中大目標,就要考慮具體那個類別的數量了,數量少的類別匹配多一點,數量多就少匹配。
對于特殊標記的gt框,如hard、dontcare, 如果一些負樣本和這些hard、dontcare強相關,那么把這些負樣本變成ignore,避免讓樣本間產生歧義。
正負樣本的定義過程是一個迭代的過程,會根據模型的實際訓練過程以及測試效果來動態(tài)調整, 比如模型對某個類recall偏低,那么此時我們就要增加該類生成正樣本的數量了。
定義的過程就是將正負樣本嚴格區(qū)分開,為后續(xù)的采樣提供方便。 如下圖,將從正樣本過渡到負樣本的這些樣本歸入ignore。
注意:ignore樣本的區(qū)間不能太大,存在一個上限。如果太大,模型在推理階段會存在一定的隨機性。
3. 采樣哪些正負樣本參與訓練
個人認為:該部分是訓練檢測模型最為核心的部分,直接決定模型最后的性能。理解正負樣本的訓練,實質是理解正負樣本的變化是如何影響precision和recall的。
我們先考慮3個基本問題,對于某個類別gt:
假設我們希望precision=1,不考慮recall,那么屬于該gt的并且參與訓練的正負樣本理想情況會是什么樣的?
正樣本:數量適當,并且質量要極高。(因為不考慮recall,那正樣本的選取我只要選擇那些極高質量的正樣本參與訓練就可以了,推理的時候就可以保證,模型一定認為是前景的才輸出)
負樣本:多樣性越豐富越好,數量越多越好(實際已經滿足數量多的情況)。
2. 假設我們希望recall=1,不考慮precision呢?
正樣本:數量越多越好。(因為不考慮precision,那我就把所有樣本當正樣本訓練就好了,推理的時候,不管是前景還是背景,通通都認為是前景輸出,一定能保證recall=1)
負樣本:數量為0最好。
3. 現在我們希望precision=1, recall=1呢?
正樣本:數量越多越好,并且質量越高越好。(因為考慮到recall=1,也就是要讓模型盡可能的把所有前景目標都召回出來,那么越多且質量越高的正樣本就越好)
負樣本:多樣性越豐富越好,并且數量越多越好。
從以上3個問題分析得到,對于某個類別的gt,屬于該gt的正樣本中,數量和質量是矛盾的。數量越多,那么質量必然下降,recall會偏高,precision會偏低。反之,數量越少,質量會高,但是recall會偏低,precision會偏高。對于負樣本來說,要求它數量越多,并且多樣性越豐富,這并不矛盾,實際是可以做到這點。
有人會問,不看mAP嗎?
mAP是綜合衡量了recall從0到1變化的過程中(實際recall達不到1),precision的變化曲線,mAP并不直觀,實際把mAP當做其中一個衡量指標而已。
所以,我們采樣的目標就是:
正樣本:質量高,數量適當
負樣本:多樣性越豐富,數量適當(或者說是正樣本數量的n倍,n一般取值[3,10])
一般情況下,定義的那些正樣本都會采樣參與訓練,負樣本就隨機采樣一些去訓練。但在訓練的過程中你需要考慮幾點:
1. 定義的那些正樣本,模型真的都能搞定嗎?
在量產級的數據集中,往往會有百千萬量級的目標,雖然在定義正樣本的時候考慮到了很多因素,但是面對百千萬量級的目標,往往會存在一定比例的正樣本,模型壓根就學不會,訓練后期模型loss就在一個小區(qū)間里震蕩, 所以我們就要對這些樣本做進一步處理,把其歸為ignore,減少他們對模型訓練的影響。
對于FN(漏檢),我們就要根據具體的需求分析這些FN到底是否需要檢出,如果需要檢出,就需要調整定義這些FN的正樣本的匹配邏輯,讓其產生適合訓練的正樣本。
2. 面對數量眾多的負樣本,怎么針對性的采樣(適應自己的項目)。
其實在項目前期,負樣本的采樣可以選擇隨機,但當你進行大量路采數據測試后,總結發(fā)現模型輸出的FP,比如,發(fā)現模型輸出大框背景的頻次偏高,那么這個時候我們就要改變隨機采樣負樣本的策略,就要針對性的增加小分辨率feature map上的負樣本的采樣。如果模型經常把特定背景(樹尖,房屋)檢測為目標,那么我們需要1. 檢查gt的標注質量。2. 想辦法采樣到這類的負樣本參與訓練。
3. 盡可能保證每個batch中,類別間采樣的正樣本比例接近1:1。
在量產級數據中,因為是實車采集,往往會出現類別不均衡現象,隨著數據量的不斷增加,這種不均衡會被嚴重放大,如果直接采樣全部正樣本采樣訓練,模型很可能出現precision和recall偏向類別多的那個類,比如類A,這個時候就需要考慮適當降低類A的采樣,同時考慮適當增加類B類C的采樣訓練,來達成類別間正樣本的比例接近1:1。
所以,正負樣本的采樣是根據當前模型的檢測效果來動態(tài)改變優(yōu)化的,但是不管怎么改變,對正負樣本的采樣不會偏離理想狀態(tài)的,只不過離理想狀態(tài)的距離由自己手頭的數據集標注質量決定。
當然,如果你的數據集足夠干凈,定義的那些正負樣本也接近理想狀態(tài),那么就可以讓模型通過OHEM之類的方式,根據模型當前狀態(tài)來自動選取參與訓練的正負樣本。
如有錯誤,請指正。