目標檢測:奇淫技巧

背景

國內的數據競賽真的缺乏交流,還是喜歡 kaggle 的 kernel 和討論區,真硬核!這裏分享一下我總結的一些目標檢測中會用到的 “奇淫技巧”,牽扯到代碼的我就直接拿 mmdetection[1] 來舉例了,修改起來比較簡單。這些都是跟隨鄭燁、樑爽、元梵、楊勝、亞光、徐大哥等大佬一起學習的一部分思路,感謝各位大佬不嫌棄小弟,帶我不斷起飛,哈哈哈。

1. 模型選擇

近一年多以來目標檢測領域沒有太大的動靜,即使最近一段時間的 Anchor Free 和神經網絡搜索框架比較熱,但都沒有太大的革新,當前檢測競賽圈的通用配置還是 Cascade-R-CNN + ResNeXt/ResNet 系列 + FPN+DCN 2,畢竟二階段爲王,詳情參考鏈接 cascade_rcnn_dconv_c3-c5_r50_fpn, 根據自己的需要進行修改,如果沒有修改源碼的能力,那就只能用官方給出的配置了。然後就是一些根據實驗結果的調整了,由於商湯沒有開源 SeNet 系列的訓練模型(避免誤會,這裏指的是商湯未在 mmd 中開源,並沒有 senet 的所屬關係),所以如果有卡的話可以自己搞。

2. 數據預處理

如果你 baseline 選的準,那麼基本上已經領先一大部分人了,但是如果在數據預處理過程中沒有搞好,那基本上就跟 TOP 系列無緣了,畢竟在數據處理上能夠領先的大佬,後面煉丹的技術也絕對不差。這裏分爲以下幾個部分聊一聊數據這方面的策略:

2.1 數據擴充

如果說待檢測目標具有旋轉不變性,那這裏就可以對目標做上下翻轉、左右反轉、90°*3 旋轉等操作;如果目標中存在模糊的情況,在擴充的時候也可以適當做一些高斯模糊什麼的;對於顏色抖動、銳度變化、隨機縮放等這些操作,我實驗的過程中也很難界定他們的效果,而且跟隊友做相同實驗時,所起的作用也不一樣,總結來說,有的時候真的是隨機上分。

更詳細的附帶代碼的數據擴充請移步:目標檢測系列二:數據增廣

2.2 mixup

mixup 的意思就是將兩張圖按照一定的比例混合在一起,詳情移步論文:Bag of Freebies for Training Object Detection Neural Networks[4]。這裏要說的是如何 mixup,如何選擇 mixup 的對象。
在工業類缺陷檢測或者違禁物品檢測中,常常會給出一些不含有待檢測目標的正常圖像,可以將含有目標的圖像和隨機選取的正常圖像進行 mixup(隨機意味着更多的組合~),這樣數據量又上來了。還有一種是徐大哥用的比較騷的操作,就是跟 coco 的數據集進行 mixup,真是服氣。。。

2.3 填鴨式

這個肯定別人也這麼叫過,但是我們隊一開始想到的時候,就這麼稱呼了。所謂填鴨式,就是將一些目標(也可以是誤撿的)扣出來,放到沒有目標的圖上去,增加圖像的魯棒性。比如我們在鋼筋識別的時候,有一些小石子和吊機容易被誤判成鋼筋,索性就選了一些圖,把這些僞目標填充合理的位置上,效果就是沒有再誤判過了。

在津南 2 違禁物品的檢測中,我們一開始也測試了 mixup 的策略,但是效果一般,後來改用 “填鴨式” 處理,效果要好很多,應該說前排基本都用了這個方法吧(猜測)。這裏有一個要注意的就是“隨機”,要是能夠讓目標在正常圖像中的隨機位置填充並且隨機旋轉和縮放,那就完美了,無奈這個沒有實現好,如果有大佬實現了,求指導。。。。

2.4 裁剪

根據具體任務,可以將待檢測目標進行裁剪。在 DF 新的交通標誌物的識別競賽中,圖像尺寸較大,3200x1800,但是目標特別小,最大也在 200x200 左右,這就導致訓練尺寸必須要不低於原始圖像尺寸,很依賴算力。後來在跟徐大哥、鄭燁討論後,決定在目標周圍裁剪出一個尺寸,然後進行訓練,效果不錯,且訓練週期明顯縮小。這個策略前排基本都在用,也沒什麼可隱藏的了。

想到其他的了,再更新。

3. 預訓練模型

數據處理完以後,基本上就是要衝擊前排了,這裏就是要考慮如何選用預訓練模型了,一般的檢測都是使用 ImageNet 預訓練的 backbone,這是基本配置,高級一點的就是針對數據集做一次預訓練,比如津南 2 的違禁物品檢測,可以將所有目標裁剪出來,然後訓練一個不錯的分類模型,這樣的初始化相比 ImageNet 就要好太多了。。。

再一點就是使用 coco 預訓練的完整檢測模型權重,這樣的效果就是模型收斂速度賊快,而且效果一般都比較好,也是大家最常用的方法,這裏給出 mmdetection 修改 coco 預訓練權重類別數的腳本:

# for cascade rcnn
import torch
num_classes = 21
model_coco = torch.load("cascade_rcnn_x101_32x4d_fpn_2x_20181218-28f73c4c.pth")

# weight
model_coco["state_dict"]["bbox_head.0.fc_cls.weight"].resize_(num_classes,1024)
model_coco["state_dict"]["bbox_head.1.fc_cls.weight"].resize_(num_classes,1024)
model_coco["state_dict"]["bbox_head.2.fc_cls.weight"].resize_(num_classes,1024)
# bias
model_coco["state_dict"]["bbox_head.0.fc_cls.bias"].resize_(num_classes)
model_coco["state_dict"]["bbox_head.1.fc_cls.bias"].resize_(num_classes)
model_coco["state_dict"]["bbox_head.2.fc_cls.bias"].resize_(num_classes)
#save new model
torch.save(model_coco,"coco_pretrained_weights_classes_%d.pth"%num_classes)

這裏再說一下 backbone 的選擇,因爲要考慮到要使用 coco 預訓練權重的原因,暫時採用的都是官方給出的模型以及對應的 backbone。我自己也在 mmdetection 中實現了 SeNet 系列的 backbone,無奈沒卡,沒法訓一個 coco 出來。。。。

4. 訓練技巧

深度煉丹名不虛傳。。。。。

4.1 warmup lr

翻譯一下就是對學習率進行預熱,最開始是在 ResNet 的論文中提到的一種方法,原始是先在前幾個 epoch 或 iter 或目標達到一個水準之前以小於預設值得 lr 進行訓練,然後再恢復 lr 到初始值。後來 Facebook 提出了改良版本,詳情請移步論文: Gradual warmup[5],這也是當前檢測和分割中必不可少的環節,mmdetection 中默認是啓用了的:

lr_config = dict(
    policy='step',
    warmup='linear',
    warmup_iters=500,
    warmup_ratio=1.0 / 3,
    step=[8, 11])

4.2 lr 如何計算

學習率的初始化設置一直是一個比較頭疼的問題,有的時候需要經常實驗才能得到一個比較好的值,我們在檢測任務中常用的計算方法是:lr = 0.02 / 8 x num_gpus x img_per_gpu / 2,一般情況都是這麼計算後設置。

4.3 多尺度訓練

檢測的兩大任務:分類和定位,定位需要模型能夠適應變化頻繁的尺度特徵,但是這恰恰是卷積神經網絡所不具備的,目前在網絡模型上通過 FPN 結構可以緩解一部分,另外就是多尺度訓練,在不同的 iter 下選擇不同的尺度進行訓練,注意尺寸最好時能夠被 32 整除,不過 mmdetection 會檢查這一點,幫你 pad 一下。

4.4 損失函數

4.4.1 Focal Loss

這是 CV 中根據實驗結果調整損失函數最先考慮的一個,論文: Focal Loss for Dense Object Detection, 主要是針對模型擬合困難的樣例或者樣本不均衡的樣例,在圖像分類中常用作最終的損失函數,直接進行優化,而在目標檢測中卻有兩個選擇,一個是在 RPN 層使用 FocalLoss,這樣可以緩解由於目標占比較少導致生成的 anchor 正負樣本比例失衡;另一種就是類似圖像分類一樣,在 bbox_head 中使用,mmdetection 中的相應配置 (如果要正確使用,需要做點改動,自行修改源碼吧,不難):

#1. rpn 處更改
rpn_head=dict(
    type='RPNHead',
    in_channels=256,
    feat_channels=256,
    anchor_scales=[8],
    anchor_ratios=[0.5, 1.0, 2.0],
    anchor_strides=[4, 8, 16, 32, 64],
    target_means=[.0, .0, .0, .0],
    target_stds=[1.0, 1.0, 1.0, 1.0],
    loss_cls=dict(
        type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0),
    loss_bbox=dict(type='SmoothL1Loss', beta=1.0 / 9.0, loss_weight=1.0))

#2. bbox_head 處更改
bbox_head=dict(
    type='SharedFCBBoxHead',
    num_fcs=2,
    in_channels=256,
    fc_out_channels=1024,
    roi_feat_size=7,
    num_classes=81,
    target_means=[0., 0., 0., 0.],
    target_stds=[0.1, 0.1, 0.2, 0.2],
    reg_class_agnostic=False,
    loss_cls=dict(
        type='CrossEntropyLoss', #在此處替換
        use_sigmoid=False,
        loss_weight=1.0),
    loss_bbox=dict(
        type='SmoothL1Loss', beta=1.0, loss_weight=1.0)))

4.4.2 GIou Loss

Generalized Intersection over Union: A Metric and A Loss for Bounding Box Regression,之前曠世提出了 Iou Loss 收斂性較差,GIOU 的詳細介紹,我之前一篇博客介紹了,這裏不再贅述,詳情移步:Generalized Intersection over Union, 這裏只提一下,這種損失函數對增加框的迴歸效果比較有效,如果你的任務要求 IOU > 0.8 或者更高,這個可以優先考慮。

4.4.3 其他損失函數

針對分類的損失函數可以試試如 GHM-C Loss,針對迴歸的損失函數可以試試如 GHM-R Loss。

4.5 OHEM

OHEM(online hard example mining),翻譯過來就是在線難例挖掘,就是對所有的 ROI 的損失進行評估,選擇損失較大的來優化網絡,詳情移步:OHEM 論文解讀

4.6 待更。。。

想到其他的了,再更新。。。

5. infer 技巧

5.1 TTA

檢測任務中使用比較多的就是多尺度預測,這個時間開銷有點高,但是效果也是不錯的。另外一個就是 CV 中最常用的 TTA 了(Test Time Augmentation),如果在訓練的過程中加入了旋轉和翻轉,那麼前向過程中也需要加上,即使訓練沒用的話,加上翻轉也會有提升。

5.2 Soft-NMS

Soft-NMS 改進了之前比較暴力的 NMS,當 IOU 超過某個閾值後,不再直接刪除該框,而是降低它的置信度 (得分),如果得分低到一個閾值,就會被排除;但是如果降低後仍然較高,就會被保留。實現細節移步:NMS 與 soft NMS

在 mmdetection 中的設置如下:

test_cfg = dict(
    rpn=dict(
        nms_across_levels=False,
        nms_pre=1000,
        nms_post=1000,
        max_num=1000,
        nms_thr=0.7,
        min_bbox_size=0),
    rcnn=dict(
        score_thr=0.05, nms=dict(type='soft_nms', iou_thr=0.5), max_per_img=100),
    keep_all_stages=False)

TODO

  • [ ] 其他未知

參考文獻

[1] : mmdetection
[2] : Cascade R-CNN: Delving into High Quality Object Detection
[3] : Deformable ConvNets v2: More Deformable, Better Results
[4] : Bag of Freebies for Training Object Detection Neural Networks
[5] : Gradual warmup

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章