AI編輯:深度眸
0 概要論文名稱:FCOS: A simple and strong anchor-free object detector
論文地址:https://arxiv.org/pdf/2006.09214v3.pdf
論文名稱:FCOS: Fully Convolutional One-Stage Object Detection
論文地址:https://arxiv.org/pdf/1904.01355v1.pdf
官方代碼地址:https://github.com/aim-uofa/AdelaiDet/
FCOS是目前最經典優雅的一階段anchor-free目標檢測算法,其模型結構主流、設計思路清晰、超參極少和不錯的性能使其成為後續各個改進算法的baseline,和retinanet一樣影響深遠,故非常有必要對其進行深入分析。
一個大家都知道的問題,anchor-base的缺點是:超參太多,特別是anchor的設置對結果影響很大,不同項目這些超參都需要根據經驗來確定,難度較大。 而anchor-free做法雖然還是有超參,但是至少去掉了anchor設置這個最大難題。fcos算法可以認為是point-base類算法也就是特徵圖上面每一個點都進行分類和回歸預測,簡單來說就是anchor個數為1的且為正方形anchor-base類算法。
在目前看來,任何一個目標檢測算法的核心組件都包括backbone+neck+多尺度head+正負樣本定義+正負樣本平衡採樣+loss設計,除了正負樣本平衡採樣不一定有外,其他每個環節都是目前研究重點,到處存在不平衡問題,而本文重點是在正負樣本定義上面做文章。
貼一下github:
https://github.com/hhaAndroid/mmdetection-mini
歡迎star
FCOS結構和retinanet幾乎相同,但是有細微差別,下面會細說。
不清楚retinanet結構的請看:mmdetection最小復刻版(二):RetinaNet和YoloV3分析
retinanet的結構大概可以總結為:
resnet輸出是4個特徵圖,按照特徵圖從大到小排列,分別是c2 c3 c4 c5,stride=4,8,16,32。Retinanet考慮計算量僅僅用了c3 c4 c5
先對這三層進行1x1改變通道,全部輸出256個通道;然後經過從高層到底層的最近鄰上採樣add操作進行特徵融合,最後對每個層進行3x3的卷積,得到p3,p4,p5特徵圖
還需要構建兩個額外的輸出層stride=64,128,首先對c5進行3x3卷積且stride=2進行下採樣得到P6,然後對P6進行同樣的3x3卷積且stride=2,得到P7
下面介紹fcos和retinanet算法的區別
左邊是fcos配置,右邊是retinanet配置。
(1) resnet骨架區別
在resnet骨架中,style='caffe'參數和style='pytorch'的差別就在Bottleneck模塊,該模塊的結構如下:
主幹網絡是標準的1x1-3x3-1x1結構,考慮stride=2進行下採樣的場景,對於caffe模式來說,stride參數放置在第一個1x1卷積上,對於pytorch模式來說,stride放在第二個3x3卷積上:
if self.style == 'pytorch': self.conv1_stride = 1 self.conv2_stride = stride else: self.conv1_stride = stride self.conv2_stride = 1唯一區別就是這個,至於為啥存在caffe模式,是因為早期mmdetection採用了detectron權重(不想重新訓練imagenet),其早期採用了caffe2來構建模型,後續detectron2才切換到pytorch中,屬於歷史遺留問題。
(2) 骨架訓練策略區別
注意看上面的對比配置,可以發現除了上面說的不同外,在caffe模式下,requires_grad=False,也就是說resnet的所有BN層參數都不更新並且全局均值和方差也不再改變,而在pytorch模式下,除了frozen_stages的BN參數不更新外,其餘層BN參數還是會更新的。我不清楚為啥要特意區別。總的來說,在caffe模式下訓練的內存肯定會少一些,但是效果究竟誰的更好,看了下對比結果發現差不多。
1.2 FPN結構區別
和retinanet相比,fcos的FPN層抽取層也是不一樣的,需要注意。
retinanet在得到p6,p7的時候是採用c5層特徵進行maxpool得到的(對應參數是add_extra_convs='on_input',),而fcos是從p5層抽取得到的(對應參數是extra_convs_on_inputs=False),而且其還有relu_before_extra_convs=True參數,也就是p6和p7進行卷積前,還會經過relu操作,retinanet的FPN沒有這個算子(C5不需要是因為resnet輸出最後就是relu)。從實驗結果來看,從p5抽取的效果是好於c5的,baseline的mAP從38.5提高到38.9。
1.3 數據歸一化區別
當切換style='caffe'時候,需要注意圖片均值和方差也改變了:
mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=Truemean=[103.530, 116.280, 123.675], std=[1.0, 1.0, 1.0], to_rgb=False1.4 head模塊區別
和retinanet相比,fcos的head結構多了一個centerness分支,其餘也是比較重量級的兩條不共享參數的4層卷積操作,然後得到分類和回歸兩條分支。
2 fcos算法深入分析2.1 fcos輸出格式
fcos是point-base類算法,對於特徵圖上面任何一點都回歸其距離bbox的4條邊距離
假設x是特徵圖上任意點坐標,x0y0x1y1是某個gt bbox在原圖上面的坐標,那麼4條邊的邊界(都是正數)其實很好算,公式如上所示。需要注意的是由於大小bbox且數值問題,一般都會對值進行變換,也就是除以s,主要目的是壓縮預測範圍,容易平衡分類和回歸Loss權重。
如果某個特徵圖上面點處於多個bbox重疊位置,則該point負責小bbox的預測。
2.2 正負樣本定義一個目標檢測算法性能的優異性,最大影響因素就是如何定義正負樣本。而fcos的定義方式非常mask sense,通俗易懂。主要分為兩步:
(1) 設置regress_ranges=((-1, 64), (64, 128), (128, 256), (256, 512),(512, INF),用於將不同大小的bbox分配到不同的FPN層進行預測即距離4條邊的最大值在給定範圍內
(2) 設置center_sampling_ratio=1.5,用於確定對於任意一個輸出層距離bbox中心多遠的區域屬於正樣本,該值越大,擴張比例越大,正樣本區域越大為了說明其重要性,作者還做了很多分析實驗。
(1) 為啥需要第一步?
regress_ranges的意思是對於不同層僅僅負責指定範圍大小的gt bbox,例如最淺層輸出,其範圍是0-64,表示對於該特徵圖上面任何一點,假設其負責的gt bbox的label值是left、top、right和bottom,那麼這4個值裡面取max必須在0-64範圍內,否則就算背景樣本。為啥需要限制層回歸範圍?目的應該有以下幾點:
1. 在SNIP論文中說到,CNN對尺度是非常敏感的,一個層負責處理各種尺度,難度比較大,採用FPN來限制回歸範圍可以減少訓練難度
2. 提高best possible recall通常我們知道anchor設置的越密集,理論上召回率越高,現在fcos是anchor-free,那麼需要考慮理論上的最好召回率是多少,如果理論值都比較低,那肯定性能不行。best possible recall定義為訓練過程中所有GT的最大召回率,如果訓練過程中一個GT被某個anchor或者location匹配上那麼就認為被召回了。
low-quality matches意思是retinanet配置中的min_pos_iou參數,默認是0。在不使用low-quality matches時候,對於某些gt,如果其和anchor的最大iou沒有超過正樣本閾值,那麼就是背景樣本,一開啟low-quality matches則可能能夠撈回來,開啟這個操作可以顯著提高BPR,從表中也可以看出,從88.16變成99.32。至於在ALL模式下為啥不是1,作者分析原因是有些bbox特別小並且挨著,經過下採樣後在同一個位置,導致出現相互覆蓋。
對於FCOS,即使在不開啟FPN時候,其BPR也很高,因為其bbox中心範圍內都算正樣本,BPR肯定比較高,在開啟FPN後和retinanet相比差別不大,對最終性能沒有影響。為了強調FPN層的作用性,作者還對模糊樣本數進行分析:
模糊樣本就是某個特徵圖位置處於多個gt bbox重疊位置,也就是圖表中的大於1個數。可以發現在開啟FPN後,模糊樣本明顯減少,因為不同大小的gt bbox被強制分配到不同層預測了,當結合中心採樣策略後可以進一步提高。
(2) 為啥需要第二步?
中心採樣的作用有兩個1. 減少模糊樣本數目
2. 減少標註噪聲幹擾
bbox標註通常都有噪聲或者會框住很多無關區域,如果這些無關區域也負責回歸則比較奇怪,這其實是目前非常常用的處理策略,在文本檢測領域廣泛應用。
(a)是早期做法也就是對於本文不採用中心採樣策略的做法;(b)是本文做法;(c)是 centernet做法;(d)是centernet改進論文做法。從這裡可以看出,(d)的做法應該比fcos更加mask sense,可解釋性更強。
總的來說,作者認為FCOS的功臣可以歸功於FPN層的多尺度預測和中心採樣策略。
(3) 核心代碼分析
為了方便理解中心採樣策略代碼,我對代碼進行了分解,具體在tools/fcos_analyze/center_samper_demo.py。其核心代碼邏輯是:
(1) 利用meshgrid函數獲取特徵圖上面每個點的2d坐標
(2) 擴展維度,使得所有參數計算的tensor維度相同,方便後續計算
(3) 如果不採用中心採樣策略,則直接對特徵點上面每個點計算和所有gt bbox的left/top/right/bottom值。然後取4個值中的最小值,如果小於0,則該point至少有一條邊不再bbox內部,屬於背景point樣本,否則就是正樣本
(4) 如果採用中心採用策略,則基於gt bbox中心點進行擴展出正方形,擴展範圍是center_sample_radius×stride,正方形區域就當做新的gt bbox,然後和(3)步驟一樣計算正樣本即可。還需要注意一個細節:如果擴展比例過大,導致中心採樣區域超過了gt bbox本身範圍了,此時需要截斷操作
以上操作就可以確定哪些區域是正樣本了,我仿真的結果如下所示:
藍色是gt bbox,白色區域是正樣本區域。從這裡也可以看出,其實FCOS中心採樣策略還有改進空間,因為他的正樣本區域沒有考慮gt bbo寬高信息,對於圖片中包含人這種長寬比比較大的場景,這種做法其實不好。需要注意的是為了可視化代碼簡單,我是直接對特徵圖mask進行上採樣得到的,實際上在原圖上對應的白色區域是一個點,而不是一個白塊。
(4) 理解糾正
我們再來看下上面的一句話:(1) 設置regress_ranges=((-1, 64), (64, 128), (128, 256), (256, 512),(512, INF),用於將不同大小的bbox分配到不同的FPN層進行預測
(2) 設置center_sampling_ratio=1.5,用於確定對於任意一個輸出層距離bbox中心多遠的區域屬於正樣本,該值越大,擴張比例越大,正樣本區域越大看起來好像沒問題,後來我通過正樣本可視化分析發現我上面說法是錯誤的。正確的應該是:
(1) 對於任何一個gt bbox,首先映射到每一個輸出層,利用center_sampling_ratio值計算出該gt bbox在每一層的正樣本區域以及對應的left/top/right/bottom的target
(2) 對於每個輸出層的正樣本區域,遍歷每個point位置,計算其max(left/top/right/bottom的target)值是否在指定範圍內,不再範圍內的認為是背景上面兩個寫法的順序變了,結果也差別很大,最大區別是第二種寫法可以使得某一個gt bbox在多個預測層都是正樣本,而第一種做法先把gt bbox映射到預測層後面才進行中心採樣,第二種做法相比第一種做法可以增加很多正樣本區域。很明顯,第二種才是正確的。
通過正樣本可視化分析可以發現:
紅色點表示正樣本point,其中0-4表示stride=[8,16,32,64,128]的輸出特徵圖,代表從小到大物體的預測。
可以看出,其是完全基於回歸距離來定義正負樣本位置的,但是從語義角度來看,這種設計不夠make sense,因為紅色點不是對稱分布,特別是上標為2的小象內部不算正樣本,比較奇怪。從語義角度理解不合理,但是從回歸數值範圍理解是很合理的。
正樣本可視化代碼我已經新增到mmdetection-mini中了,只需要對應train_cfg裡面的debug設置為True即可可視化。
2.3 loss計算
在確定了每一層預測層的正負樣本後就可以計算loss了,對於分類分支(class_num,h',w'),其採用的是FocalLoss,參數和retinanet一樣。對於回歸分支(4,h',w'),其採用的是GIou loss,也是常規操作,回歸分支僅僅對正樣本進行監督。
在這種設置情況下,作者發現訓推理時候會出現一些奇怪的bbox,原因是對於回歸分支,正樣本區域的權重是一樣的,同等對待,導致那些雖然是正樣本但是離gt bbox中心比較遠的點對最終loss產生了比較大的影響,其實這個現象很容易想到,但是解決辦法有多種。
現象應該和(b)圖一致,在靠近物體中心的四周會依然會產生大量高分值的輸出。
作者解決辦法是引入額外的centerness分類分支(1,h',w')來抑制,該分支和bbox回歸分支共享權重,僅僅在bbox head最後並行一個centerness分支,其target的設置是離gt bbox中心點越近,該值越大,範圍是0-1。雖然這是一個回歸問題,但是作者採用的依然是ce loss。很多人反映該分支訓練時候loss下降的特別慢,其實這是非常正常的,這其實是整圖回歸問題,在每個點處都要回歸出對應值其實是一個密集預測問題,難度是很大的,loss下降慢非常正常。
需要特別注意的是centerness分支也是僅僅對正樣本point進行回歸。
通過上圖可以看出,在引入centerness分支後,將該預測值和分類分支結果相乘得到橫坐標分支圖,縱坐標是bbox和gt bbox的iou,可以明顯發現兩者的一致性得到了提高,對最終性能有很大影響。至於這個圖自己如何繪製,有個比較簡單的辦法:
(1) 利用框架進行測試,保存coco驗證集的預測json格式
(2) 在寫一份離線代碼讀取該預測json和標註的json,遍歷每張圖片的預測bbox和gt bbox
(3) 對於每張圖片的結果,遍歷預測bbox,然後和所有gt bbox計算最大iou,該值就是縱坐標,橫坐標就是預測分值(cls*centerness)
(4) 所有數據都處理完成就可以上述圖表
由於代碼比較簡單,我這裡就不寫了。
對於任何一個點,其centerness的target值通過如下公式計算:
tools/fcos_analyze/center_samper_demo.py仿真效果如下所示:
越靠近中心,min(l,r)和max(l,r)越接近1,也就是越大。
2.4 附加內容fcos代碼在第一版和最終版上面修改了很多訓練技巧,對最終mAP有比較大的影響,主要是:
(1) centerness 分支的位置
早先是和分類分支放一起,後來和回歸分支放一起
(2) 中心採樣策略
早先版本是沒有中心採樣策略的
(3) bbox預測範圍
早先是採用exp進行映射,後來改成了對預測值進行relu,然後利用scale因子縮放
(4) bbox loss權重
早先是所有正樣本都是同樣權重,後來將樣本點對應的centerness target作為權重,離GT中心越近,權重越大
(5) bbox Loss選擇
早先採用的是iou,後面有更優秀的giou,或許還可以嘗試ciou
(6) nms閾值選取
早先版本採用的是0.5,後面改為0.6這些trick,將整個coco數據集mAP從38.6提高到42.5,提升特別大,新版本的論文中有非常詳細的對比實驗報告,有興趣的可以自行閱讀。
再次貼一下github:
https://github.com/hhaAndroid/mmdetection-mini歡迎star
推薦閱讀
mmdetection最小復刻版(四):獨家yolo轉化內幕
mmdetection最小復刻版(三):數據分析神兵利器
mmdetection最小復刻版(二):RetinaNet和YoloV3分析
機器學習算法工程師
一個用心的公眾號