目標檢測小網絡


目標檢測小網絡

一. Anchor-based

1.1 網絡結構

當前一般采用

  • backbone:shufflenetV2、mobilenetV2/3(V3比較難調試)、Ghostnet、SandGlass
  • FPN:PAN、BiFPN,這部分對檢測提升很大(實際使用得進行裁剪壓縮,原始版本不適合小模型),追求極致性能的網絡可以去除(比如人臉檢測超小模型,這類目標特征明顯且背景不是很復雜的情況)
  • Head:RFB、SSH,如果不加FPN結構,Head必須做一番手腳,如果存在FPN,簡單使用幾個卷積即可
  • 輸出:(這部分一般包含在Head內部,最近遇到這個問題單獨提出來),可以使用cls+reg共享一個卷積,也可以單獨卷積(每層之間也可以共享或單獨),小模型一般每層不共享(共享效果差,有朋友已實測),cls和reg之間隨意選擇(共享最好做一些trick,最好增加FPN,不共享可以增加RFB等操作)

小模塊一般采用:

  • 模塊卷積一般使用Depthwise結構,小卷積一般使用 \(1\times1\) 卷積(很少使用 \(3\times3\) 卷積)
  • backbone的輸出通道盡可能的大(筆者嘗試 \(chennels=[24, 48, 96]\) 的輸出效果很差),最基礎的保證backbone的輸出大於FPN的輸出
  • 小模型一般在 \(stride=[8,16,32,64]\) 進行采樣,\(stride=4\) 沒見過有人在小模型上使用
  • 關於BatchNormalize操作,小網絡一般使用BN即可,大網絡使用GN效果較好(也可以直接使用torch自帶的sync-BN)

loss的使用:

  • 正負樣本采用 \(3:1\) ,loss直接采用SSD,剛開始可以使用此方法,交叉熵的loss一般在1.0以下(具體多少適合你的網絡看結果),如果在0.9附近無法下降,實測效果較差
  • Focal-Loss、GHM、TopK,筆者測試在單目標檢測中使用較好,誤檢很少但難以訓練
  • 目前使用GIOU、DIOU作為回歸較多
  • FocalLoss+GIOU實測效果較好

1.2 數據和anchor

  • 使用mmdetection進行訓練,一定要注意目標的大小,匹配過小(未匹配等)會導致出現Nan出現(查找問題比較困難,之前還以為LR等問題)

  • 數據增強不是越多越好,比如人臉檢測不需要上下Flip、人臉檢測不需要遮擋問題(情況極少),數據增強過多會導致小模型不收斂,建議先進行基礎操作(color-transform + random-crop + resize),之后根據提升進行特定數據增強。

  • 數據和網絡盡量保持等比例,筆者數據 \(1280\times720\) ,網絡輸入 \(256\times256\) ,padding之后再進行輸入明顯效果好

  • 小目標過多,為了不丟棄數據,在增強策略先放大然后進行crop,或者直接crop目標區域(盡量不讓目標縮小再進網絡),筆者測試效果較好

  • anchor在每一層上一定大於stride(原始的RetinaNet中anchor比stride大4倍),小模型一般超過 \(\times1.25\) 以上,大模型 \(\times2\) 以上

  • 之前有朋友建議兩層之間的anchor大小有交集(開源項目沒見過這種操作),實際測試提高了召回率同時也增加了誤檢,看個人取舍

  • 關於感受野和anchor的關系,這部分前兩年說的較多,現在論文基本沒見過了

1.3 一系列問題

  • loss出現Nan
    • 首先排查數據問題,一般是未匹配(目標太小,超出邊界等)
    • 盡量使用預訓練模型(如果沒有,先訓練小批量數據得到預訓練模型,之后再大數據上再訓練)
    • 減少學習率和batchsize等操作
  • 小目標檢測較差
    • 查看anchor最小檢測的尺寸(當正樣本IOU=0.5,意味着最小檢測尺寸是最小anchor的一般半,但實際上我們粗略認為最小目標等於最小anchor,因為考慮到anchor和目標匹配的問題(比如兩個anchor之間存在一個目標))
    • 增大stride,上面我們說到小模型一般stride不小於8,過小的stride對網絡來說負擔很大(只能增大網絡獲得trade-off)
    • 多尺度訓練,在實際的輸入上下浮動即可(筆者將當前概率設為0.6,上下兩個尺度設為0.2),實現方式兩種:mmdet里面直接padding,yolov4直接resize操作,筆者僅使用了后者
    • mosic數據增強
    • 網絡結構,比如SSH針對小人臉檢測較好
    • anchor匹配的時候給予小目標更大權重(筆者猜測,未嘗試)
  • 誤檢較多
    • 語義無關的情況,可以扣mask打背景(比如杯子、蘋果等檢測),當然人臉檢測肯定不行
    • 增加樣本,針對性的訓練。可以增加負樣本或ignore。親測有效
    • 多尺度訓練,參見上文,實測有效
    • 加大網絡增加FPN、backbone。親測會提高召回率,誤檢效果不明顯
    • 改變loss,針對困難樣本的訓練
    • 如果誤檢較少,可以后端接一個分類器(很小的計算量)

1.4 具體案例

  • MobilenetV3+SSD+FPN
  • libface、utralface(實測對柔性檢測較差,對人臉檢測很好)
  • Yolov3/4/5剪枝
  • efficientnet縮小版

這里以目前最流行的人臉檢測器之一為例:

Model model file size(MB)
libfacedetection v1(caffe) 2.58
libfacedetection v2(caffe) 3.34
Official Retinaface-Mobilenet-0.25 (Mxnet) 1.68
version-slim 1.04
version-RFB 1.11
Model Easy Set Medium Set Hard Set
libfacedetection v1(caffe) 0.65 0.5 0.233
libfacedetection v2(caffe) 0.714 0.585 0.306
Retinaface-Mobilenet-0.25 (Mxnet) 0.745 0.553 0.232
version-slim 0.77 0.671 0.395
version-RFB 0.787 0.698 0.438

從上面兩個官方給出的表格,速度快的同時精度也高

  • backbone使用shufflenet改良版本,其中增加RFB用於擴大感受野
  • 中間層使用SSD的multi-feature
  • head使用層不共享且cls與reg之間也不共享的卷DW卷積
  • feature在\(stride=[8,16,32,64]\)上進行檢測,anchor設置\([[10, 16, 24], [32, 48], [64, 96], [128, 192, 256]]\),由於人臉基本呈現方形,所以這里anchor都是\(1:1\)
  • loss直接使用SSD策略,交叉熵+smoothL1+正負樣本按比例挖掘
  • 數據增強使用正常操作(color+random-crop等),未使用最新的mosic等
  • train策略包含很多種,一般采用SGD+Warmup+Cosin

二. Anchor-free

2.1 具體案例

注釋: anchor-free之前流行的都是大網絡,比如centerNet、FCOS、ATSS等,因為小網絡很難訓練centerness(網絡有人測試),基本沒見到小網絡效果好的版本。自從GFL-V1論文出來以后,使得loss訓練較為容易,然后NanoDet問世了。

這里直接以目前最流程的anchor-free小網絡為例

Model Resolution COCO mAP Latency(ARM 4xCore) FLOPS Params Model Size(ncnn bin)
NanoDet-m 320*320 20.6 10.23ms 0.72B 0.95M 1.8mb
NanoDet-m 416*416 21.7 16.44ms 1.2B 0.95M 1.8mb
YoloV3-Tiny 416*416 16.6 37.6ms 5.62B 8.86M 33.7mb
YoloV4-Tiny 416*416 21.7 32.81ms 6.96B 6.06M 23.0mb
  • backbone使用shuffenetV2(原封不動)
  • FPN使用PAN簡化版,直接雙線性插值之后add即可
  • head層之間不共享,cls與reg之間進行共享卷積
  • loss使用GFL-V1版本
  • 數據增強常規操作(未使用random-crop,未使用mosaic)
  • train直接使用SGD+Warmup+LinerStep

2.2 改進策略

  • 數據增強

原版NanoDet僅使用簡單操作,針對自己數據集進行特定數據增強,實測效果很大

  • 訓練多尺度

有效改善誤檢和漏檢(建議使用預訓練模型,直接使用容易崩)

  • 最近GFL-V2出來了,嘗試將trick加入Nanodet之中

筆者稍微修改了一下trick,目的兩點:1)盡量不修改Nanodet結構。2)更容易訓練。3)效果盡可能好。

直接使用GFL-V2很難訓練,而且誤檢率較高,NanoDet作者給出的原因是共享的cls+reg對分布不敏感,不適用共享的卷積效果可以提升。筆者猜測原因:1)共享卷積已經學到部分分布信息。2)小網絡最后階段直接使用分布去指導質量分數很難收斂。

未加入GFL-V2,實測效果未提升反而下降

# 層定義
self.reg_conf = nn.ModuleList([nn.Sequential(nn.Conv2d(4 * 8, 32, 1),
                                                     nn.LeakyReLU(negative_slope=0.1, inplace=True),
                                                     nn.Conv2d(32, 1, 1), nn.Sigmoid()) for _ in self.anchor_strides])
self.reg_cls_com = nn.ModuleList([nn.Conv2d(2, 1, 1) for _ in self.anchor_strides]) 

# 前向傳播
def forward_single(self, x, cls_convs, reg_convs, gfl_cls, gfl_reg, reg_conf, reg_cls_com):
        cls_feat = x
        reg_feat = x
        for cls_conv in cls_convs:
            cls_feat = cls_conv(cls_feat)
        for reg_conv in reg_convs:
            reg_feat = reg_conv(reg_feat)
        if self.share_cls_reg:
            feat = gfl_cls(cls_feat)
            cls_score, bbox_pred = torch.split(feat, [self.cls_out_channels, 4 * (self.reg_max + 1)], dim=1)
        else:
            cls_score = gfl_cls(cls_feat)
            bbox_pred = gfl_reg(reg_feat)

        B,C,H,W = bbox_pred.shape
        prob = F.softmax(bbox_pred.reshape(B, 4, self.reg_max+1, H*W), dim=2)
        quality_score = reg_conf(prob.reshape(B, C, H, W))
        cls_score = cls_score * quality_score
        
        #cls_score = torch.cat([cls_score, quality_score],dim=1)
        #cls_score = reg_cls_com(cls_score)

        if torch.onnx.is_in_onnx_export():
            cls_score = torch.sigmoid(cls_score).reshape(1, self.num_classes, -1).permute(0, 2, 1)
            bbox_pred = bbox_pred.reshape(1, (self.reg_max+1)*4, -1).permute(0, 2, 1)
        return cls_score, bbox_pred
       
  • 改變網絡結構
  • [ ] backbone優化(GhostNet、如果使用mobilenetV2可以增加CSPNet進行通道壓縮)
  • [ ] FPN優化(DUpsample進行上采樣)
  • [x] head優化(cls和reg不共享卷積,如果使用共享卷積可以在最后增加1*1卷積進行優化)
  • [x] 使用類似二階段網絡,將當前網絡作為一階段網絡,二階段網絡使用一階段的結果輸入,直接分類(背景,目標1,目標2....),如果追求更精確可以在二階段加回歸(這種情況得有負值,因為一階段輸出會有截斷情況,即使擴大檢測范圍也不可避免截斷)
  • 注意點
  • 小網絡的backbone至關重要遠遠大於后面neck和head比重。實測:基於NanoDet模型,shufflenetV2做主干提取S=4,8,16,32,64,其中在maxpooling后面接兩個block模塊然后再提取S=4層,PAN上采樣使用反卷積,下采樣使用maxpooling,中間穿插1*1卷積。計算量達到440MB,但是和原始NanoDet320MB對比效果差很多,后面試驗發現backbone信息丟失過多,后面如果增加計算量都無法補充。提取層數過多,backbone盡量別別使用maxpooling。

當然都是建立在盡量減少計算量的基礎上進行優化,做到trade-off


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM