天天看點

記錄一些經典檢測算法的代碼細節Faster R-CNN (w/ FPN)SSD(經典的one-stage結構)RetinaNet(Focal Loss)CornerNet(無回歸且1個類别)CenterNetFCOSAnchor free總結分布式訓練

經典檢測算法代碼細節[無YOLO]

  • Faster R-CNN (w/ FPN)
  • SSD(經典的one-stage結構)
  • RetinaNet(Focal Loss)
  • CornerNet(無回歸且1個類别)
  • CenterNet
  • FCOS
  • Anchor free總結
  • 分布式訓練

Faster R-CNN (w/ FPN)

流程:帶FPN的backbone->rpn(生成anchor,rpnhead,賦予label)->->->->roi_head(檢測頭)

  1. FPN:傳統的特征圖像金字塔把圖像swap成不同尺寸,在對每種尺寸的圖檔分别檢測;SSD采用的是金字塔特征(每一層卷積層抽取特征);FPN則是在SSD的金字塔特征基礎上進行不同特征層的融合,具體實施為高一層(空間小,通道多)通過上采樣(插值算法)擴大空間,低一層(空間大、通道少)通過1x1卷積擴大通道,最終像素級求和。這種操作是是疊代的,不是僅僅互相兩層影響,由最高層一層一層疊代到最底層。
  2. FPN:FPN提取完特征之後,每一層特征都經過了3x3大小,通道不變的卷積核進行調整。,最高層額外在這個卷積核之後加入maxpooling(1x1,stride=2)下采樣提取額外的一層特征。這一層隻用于RPN部分,即所有層産生的proposals的特征映射到所有層(除這一層)上。
  3. RPN:RPN由FPN結構生成,P2(32,{1:2,2:1,1:1}), P3(64,{1:2,2:1,1:1}),P4(128,{1:2,2:1,1:1}),P5(256,{1:2,2:1,1:1}),P6額外(512,{1:2,2:1,1:1})。
  4. 有多個特征層,是否需要每個特征層取使用一個預測層呢?答案上no,FPN論文有實驗驗證,這就是共享Head的由來吧!共享Head沒有BN層,不然測試效果會極差,因為共享Head本質上處理多個檢測任務,BN中的參數在測試時固定,是以學習到五(5個特征層)不像參數在測試時儲存下來了。而GN卻不會有這種問題,因為GN的參數在測試時也是變化的(根據每張圖像)。
  5. P2-P6使用同一個RPN子產品,P2-P5使用同一個Fast RCNN子產品。P2-P6生成的RPN具體映射到那一層(P2、P3、P4、P5、P6)上進行預測(Fast RCNN)呢?通過一個公式得到的,w,h為每一層生成的RPN映射回原圖的長寬。 k = ⌊ k 0 + l o g 2 ( w h ) 1 / 2 / 224 ⌋ k=\lfloor{k_0}+log_2(wh)^{1/2}/224 \rfloor k=⌊k0​+log2​(wh)1/2/224⌋
  6. 資料加載與資料預處理(測試與訓練一樣,不像跟蹤,跟蹤在測試階段batch數為1,但最起碼跟蹤也要保持歸一化等操作的一緻性):dataset傳回的不是一個資訊,而是image,target兩個資訊,是以需要使用zip打包(collate_fn=utils.collate_fn)或者用字典将image和target資訊合并。不同大小的圖檔統一縮放成相同大小的tensor,是tensor,而不是圖像大小,在各種尺寸的圖像上填0組成相同大小的tensor的,規則如下: 歸一化處理(減均值、除方差) ->擷取所有圖像的長寬資訊,定義最小為800,最大為1000,先根據最小邊長與圖檔最小邊長的縮放比例來縮放圖檔,若有的變長大于1000,則将縮放比例定義為1000和圖檔最大變長之比,訓練時,bounding box也要縮放。 ->打包成batch,統一tensor的大小,并且把最大長寬統一到最接近32倍數的一個值,是以可以看到,圖檔最大為1000,但是輸入的tensor最大為1024。
  7. 生成anchors的過程:利用set_cell_anchors來生5*3(5層特征P2-P6,3種比例)個基準anchors(中心為0,0) ;給基準anchors施加stride位移得到dense anchors(可以不以stride作為位移,但位移的次數要等于每一層的特征圖大小,可見,越底層的特征層,anchors越多。因為stride等于每個像素感受野的差別);這兩步驟對所有圖像是相同的,接下來,周遊每張圖像,将anchors乘stride映射回圖檔上,每一層的anchors和每一張圖像上的anchors用list拼接(即嵌套list)。
  8. RPNHead:對P2-P6每一層通過3x3,padding=1的卷積核,不改變通道,再分别通過改變通道的cls和reg分支來得到每個像素的資訊(1個像素負責3個anchors,即通道數為3或者3*4),并将reg輸出賦予anchors得到proposals。
  9. 過濾proposals:周遊每張圖像,将越界的proposals調整到邊界->排除cls分數小于某個門檻值的proposals(源代碼為0.0),即這一步不起作用->nms處理,分數門檻值為0.5->取 nms處理後的前些proposals(訓練2000,測試1000)-> 最終将每個圖像的proposals與其分數值拼接在清單裡。
  10. 如果是訓練的話:需要為每個anchors制定labels,包括cls和reg輸出的labels。
  11. Roi_Head訓練:采樣訓練樣本并制定labels,cls的label值為-1,0,1,2,3,classes_num,-1丢棄,0為負樣本(IoU<0.5),1正樣本(IoU>0.5),選擇的正負樣本總量為512,負樣本比例是0.25;這個步驟是一樣的,接下來,周遊每張圖檔(每張圖檔512個訓練樣本),計算每個正anchor的reg的lables。Roi_align提取每個proposals的特征,提取方式上述FPN已詳解記錄。檢測頭,通過faltten再送入共享全連接配接層(或許是不能使用BN,這才是用全連接配接)->再送入cls和reg的全連接配接層,給每個類别都預測4個回歸參數,而在RPN中,給每個anchor預測框即可!!!。如果是訓練,就到此為止!
  12. Roi_Head測試:直接将rpn得到的1000個proposals用于檢測。通過檢測頭再經過後處理。後處理如下:(1)根據proposal以及預測的回歸參數計算出最終bbox坐标

    (2)對預測類别結果進行softmax處理

    (3)裁剪預測的boxes資訊,将越界的坐标調整到圖檔邊界上

    (4)移除所有背景資訊

    (5)移除低機率目标

    (6)移除小尺寸目标

    (7)執行nms處理,并按scores進行排序

    (8)根據scores排序傳回前topk個目标的分數、boxes、目标類。

  13. 處理傳回的結果:将bboxes傳回原圖像尺寸。

SSD(經典的one-stage結構)

在某些層結構提取特征進行目标檢測(FPN的前作)。

  1. anchors的大小、比例為(21;1:1 ,1:2, 2:1,1:1[(2145)**0.5]) (45; 1:1 , 1:2, 2:1, 1:3,3:1, 1:1 [(4599)**0.5])、(99;1:1 ,1:2, 2:1,1:3,3:1,1:1[(99153)**0.5])、(153;1:1 ,1:2, 2:1,1:3,3:1,1:1[(153207)**0.5])、(207;1:1 ,1:2, 2:1,1:1[(207261)**0.5])、(261;1:1 ,1:2, 2:1,1:1[(261315)**0.5])
  2. 對于每個anchor的回歸,至預測4個參數,不關注類别資訊,而Faster R-CNN的二階段則生成4*num_classed個參數,為每個類預測一個框。
  3. cls和reg的輸出主要是通過3x3的卷積層實作的,而不是全連接配接層。
  4. 每個特征層上的相關工作用list打包,再用zip打包,得到打包的輸出再用contiguous将資料連續存儲,通常在進行切割操作之後,拼接時用這個函數。
  5. 生成anchors(8732個)的過程是根據原圖像大小映射到feature maps,再映射回圖像大小的anchors,這與Faster R-CNN不同。這是由于Faster R-CNN是得到anchors後統一輸入FPN預測,而SSD是在每個特征層上預測。使用itertools.product函數生成每個anchors的中心,而不是通過基準anahor再通過位移,這與Faster R-CNN也不同。
  6. 計算損失時,先計算所有anchors的損失,得到損失向量(reduction=‘none’),再抽取正樣負樣本。
  7. SSD如何擷取負樣本(Hard negative mining):挑選損失值較大的anchors,是以這就是SSD先計算損失向量再挑選正負樣本的原因。
  8. 計算類别cls損失和定位reg損失時求均值,都除N(正樣本個數)。
  9. 正負樣本的比對(cls的labels)是在資料預處理時完成。每個anchors對應對大的GT的IoU(IoU排序->哪個GT)->每個GT對應對大的anchor的IoU(anchors排序->哪個anchor),通過這兩條準則,給每個anchors找到其對應的GT(哪怕IoU小),以及每個GT至少得有1個anchors(IoU設定為2,大于0.5的數)。最後通過IoU門檻值0.5過濾。
  10. NMS有個細節,給每個類别的框附加1個偏移量(這個偏移量就是labels*所有proposals中的最大坐标),使得每個類别的proposals不會重疊。另一種方法是給每個類别作NMS。

RetinaNet(Focal Loss)

focal loss提出後,one-stage檢測器首次在理論實驗上超越two-stage檢測器。

  1. 沿用了FPN網絡,但去除了P2(P2大,耗時),添加了P7。FPN中的P6是通過池化得到,這裡是通過3x3卷積層,P7在P6之後再使用3x3卷積層生成。
  2. anchors的生成,FPN3組,RetinaNet9組,加入了不同尺度{1,2**(1/3),2**(2/3)}
  3. 沒有使用Faster R-CNN中的全連接配接層來預測輸出,而是每一層使用了共享Head(無BN),共享Head包括分類和回歸分支。都是先通過4個通道不變,3x3的卷積層,再調整次元輸出分類(Focal loss的使用不包含背景,這是跟蹤算法沒用focalloss的原因,而采用權重損失)和回歸對應的通道數(類别不可知的預測:4*anchors_num個通道),這也是跟蹤算法沒用focalloss的原因。
  4. Focal Loss針對one-stage正負樣本不比對,因為two-stage在第二階段這個問題不明顯,它将類别分類兩步進行處理,第一步:區分前景,篩掉了大量easy samples;第二步:區分具體類别,這一步也可以使用SSD中的線上Hard negative mining,。
  5. Focal Loss提出意義:easy samples(标簽為1時,預測輸出>>0.5;标簽為0時,預測輸出<<0.5)的loss值也挺大的,是以導緻模型無法更多地關注hard samples。---------->是以,針對正負樣本不均衡,計算損失時給樣本增權重重;針對難易樣本,增加一個因子使得模型更關注難區分的樣本,即難區分樣本損失大。

以下anchor free detection隻分析訓練部分

CornerNet(無回歸且1個類别)

算法結構比較複雜。

  1. 網絡梳理:輸入(batch,3,511,511)圖檔,輸出4元素清單,元素分别為:tl_heat(batch,1,128,128), br_heat(batch,1,128,128), tl_tag(batch,1,128), br_tag(batch,1,128),1 denotes number of classes。
  1. kp子產品:通過self.up1(residual)得到高分辨率特征,通過self.up2得到不同的高分辨率特征,之後再相加torch.Size([batch, 256, 128, 128])。
  2. corner pooling通過編碼顯性的先驗知識(e.g. 從右向左,從下往上确定左上角)來更好地确定角落位置。 tl_conv,br_conv流程一樣。以tl_conv為例,流程圖中channel指輸出通道:
  1. 标簽labels制定:每張圖檔傳回5種資訊:tl_heatmap, br_heatmap, tl_tags, br_tags, tag_masks。

    tl_heatmap(batch,128,128):周遊每張圖檔中的boxes,擷取第一個box的左上角,通過高寬和高斯門檻值設定半徑,通過**draw_gaussian函數(無return,通過np.maximum輸出?)**輸出以左上角為中心,半徑内設定高斯标簽;第二個box同理,然而,得到的标簽是否會覆寫之前的heatmap,通過求最大處理。

    tl_tags(batch,128,):擷取所有boxes左上角的位置資訊,從左到右,從上到下,在128*128的映射圖上擷取,例如129位于第2行第1列。再把這個資訊傳入tl_tags作為embedding向量,有幾個boxes傳入幾個,是以這裡tl_tags設定為128大小(128個boxes),這個label在gather特征時使用,變相參與loss計算。

    br_tags:方式與tl_tags一樣,隻不過存的不是位置embedding,而是1。

  2. 訓練:包括ace loss包括focal loss和pull push losss(ae loss)。

    focal loss:計算标簽heatmap與輸出heat(batch,1,128,128)的損失,heat通過sigmod,再使用focal loss(實際上為heat輸出的每個特征像素作二分類)

    pull loss:給角點分組,統一box的左上、右下角點輸出向量盡量小。

    push loss:不同的boxes的角點輸出盡量大一些。

    記錄一些經典檢測算法的代碼細節Faster R-CNN (w/ FPN)SSD(經典的one-stage結構)RetinaNet(Focal Loss)CornerNet(無回歸且1個類别)CenterNetFCOSAnchor free總結分布式訓練

CenterNet

這個檢測算法整體非常簡潔。

  1. 網絡推理結構
  1. 可以用沙漏網絡來代替resnet和可形變卷及層。可形變卷積層結構:
  1. 損失包括三部分:hmap_loss中心點、reg_loss中心偏移(x,y)、w_h_loss高寬

    hmap_loss:輸出的hmap經過sigmod。再微調CornerNet的focal loss使用。

    reg_loss:使用l1 loss

    w_h_loss:使用l1 loss直接學習高寬,為什麼1個像素點的特征也能學到這麼複雜的任務,因為這個點的感受野非常大。

  2. labels的制定:重點關注圖檔以及bboxes的仿射變換

FCOS

網絡結構與RetianNet類似,不再叙述,本質上也是anchor-based,把點視為anchor。是以關鍵在如何定義訓練樣本。

  1. 通過三個mask操作,選出正樣本。第一個mask選出偏移大于0的像素點(每個像素有4個偏移);第二個mask規定在FPN某一層的偏移範圍[[-1,64],[64,128],[128,256],[256,512],[512,999999]];第三個mask選出制定半徑内的像素點。
  2. 在預處理圖像時,不僅要統一image的大小,還要統一每張圖檔中boxes的個數,這與Faster R-CNN不同

Anchor free總結

  1. CenterNet、CornerNet以點的方式存儲gt boxes的labels,在用gather特征,類似姿态估計;而Fcos更像是anchor-based,隻不過anchor為點。

分布式訓練

讀取一個batch資料->拿回參數->計算梯度(同步SGD,每塊GPU同步計算)->每塊GPU的梯度相加->發出梯度->更新梯度

  1. 資料如何在不同GPU之間配置設定:原始資料(5個)->shuffle->補充資料(加1[5個原始資料的第一個]為6個資料)->配置設定資料(GPU1:1,3,5;GPU2:2,4,6)
  2. 梯度如何在不同GPU之間通信:NVIDIA的GPU通信後端使用nccl。梯度求均值時,調整學習率為lr*num_gpus;梯度求和可忽略學習率調整。
  3. BatchNormalization如何在不同GPU之間同步(會降低并行速度,banchsize較大時同步BN沒什麼用):BN層有4個參數,均值和方差是通過動量的方法前向統計而來,均值和方差的偏移是訓練得到。
  4. pin_memory:直接将資料加載到GPU,避免了與CPU通信的某些耗時(多級緩存)過程。

繼續閱讀