深度學習筆記(二十二)EfficientDet


論文:EfficientDet: Scalable and Efficient Object Detection

關聯:EfficientNet: Rethinking Model Scaling for Convolutional Neural Networks

TensorFlow 實現

PyTorch 實現

EfficientDet 是目前最優秀的檢測器,backbone 是基於 depthwise separable convolution 和 SE 模塊利用 AutoML 搜索出來的,EfficientDet 出彩的地方在於設計了高效的 FPN 結構,即 BiFPN。

摘要

模型的效率在計算機視覺中變得越發重要。本文系統地研究了神經網絡結構在目標檢測中的設計選擇,並提出了提高檢測效率的幾個關鍵優化方案。首先,本文提出了一種加權的雙向特征金字塔網絡(BiFPN),它可以方便、快速地融合多尺度特征;其次,文中提出了一種混合縮放方法,可以同時對所有主干、特征網絡和 box/class 預測網絡的分辨率、深度和寬度進行均勻縮放。基於這些優化和 EfficientNet backbone, 文章開發了一類新的目標檢測,稱為 EfficientDet,該類檢測算法比現有的檢測算法都要高效。

以下部分為了圖省事,參考的是 PyTorch 復現版本的代碼,畢竟 TF1 調試不友好

 

 

以 d0 為例,計算量為:

backbone+fpn+box params/flops = 3.880067M, 2.535978423B

EfficientNet

檢測網絡的 backbone 采用的是 EfficientNet 系列,這一系列的網絡是由 Auto ML 搜索出來的網絡。網絡的主要成分為 Depthwise Separable ConvSENet 結構,這一系列網絡結構從 B0 到 B7,有以下這些參數控制差別:

# Coefficients:   width,depth,res,dropout
'efficientnet-b0': (1.0, 1.0, 224, 0.2),
'efficientnet-b1': (1.0, 1.1, 240, 0.2),
'efficientnet-b2': (1.1, 1.2, 260, 0.3),
'efficientnet-b3': (1.2, 1.4, 300, 0.3),
'efficientnet-b4': (1.4, 1.8, 380, 0.4),
'efficientnet-b5': (1.6, 2.2, 456, 0.4),
'efficientnet-b6': (1.8, 2.6, 528, 0.5),
'efficientnet-b7': (2.0, 3.1, 600, 0.5)

1. 由 width 參數控制除 MBConvBlock 外兩個卷積層(Stage1 和 Stage9)的輸出通道數:

def round_filters(filters, global_params):
    """ Calculate and round number of filters based on depth multiplier. """
    multiplier = global_params.width_coefficient
    if not multiplier:
        return filters
    divisor = global_params.depth_divisor # default = 8
    min_depth = global_params.min_depth
    filters *= multiplier
    min_depth = min_depth or divisor       # min_depth default is None
    new_filters = max(min_depth, int(filters + divisor / 2) // divisor * divisor)
    if new_filters < 0.9 * filters:  # prevent rounding by more than 10%
        new_filters += divisor
    return int(new_filters)
View Code

2. 由 depth 控制 Block 重復次數(向上取整):

def round_repeats(repeats, global_params):
    """ Round number of filters based on depth multiplier. """
    multiplier = global_params.depth_coefficient
    if not multiplier:
        return repeats
    return int(math.ceil(multiplier * repeats))
View Code

3. res 控制圖像輸入尺度,dropout 控制最后 AVG_POOL 和 FC 之間的 DropOut 層的 dropout_rate

下面是基礎的 EfficientNet-B0,MBConv 后面的數字代表深度可分離卷積中 expand 系數,Resolution 代表輸入特征圖分辨率, Channel 代表輸出通道數,Layers 代表 Block 重復次數

# Stem Stage1
x = self._swish(self._bn0(self._conv_stem(inputs)))

# Blocks Stage2-8
for idx, block in enumerate(self._blocks):
    drop_connect_rate = self._global_params.drop_connect_rate
    if drop_connect_rate:
        drop_connect_rate *= float(idx) / len(self._blocks)
    x = block(x, drop_connect_rate=drop_connect_rate)
# Head Stage9_1
x = self._swish(self._bn1(self._conv_head(x)))

# Pooling and final linear layer Stage9_2
x = self._avg_pooling(x)
x = x.view(bs, -1)
x = self._dropout(x)
x = self._fc(x)

 

默認每個深度可分離卷積中,DW 和 PW 之間會插入 SEblock, 同時默認開啟 skip connection

區別於常規的 CNNs,  如果 skip connection、stride=1 且 block 的輸入和輸出通道數相同則在 skip connection 之前、深度可分離卷積之后加上 dropout 操作!下面是 MBConvBlock 的部分實現:

# Expansion and Depthwise Convolution
x = inputs
if self._block_args.expand_ratio != 1:
    x = self._expand_conv(inputs)
    x = self._bn0(x)
    x = self._swish(x)

x = self._depthwise_conv(x)
x = self._bn1(x)
x = self._swish(x)

# Squeeze and Excitation
if self.has_se:
    x_squeezed = F.adaptive_avg_pool2d(x, 1)
    x_squeezed = self._se_reduce(x_squeezed)
    x_squeezed = self._swish(x_squeezed)
    x_squeezed = self._se_expand(x_squeezed)
    x = torch.sigmoid(x_squeezed) * x

x = self._project_conv(x)
x = self._bn2(x)

# Skip connection and drop connect
input_filters, output_filters = self._block_args.input_filters, self._block_args.output_filters
if self.id_skip and self._block_args.stride == 1 and input_filters == output_filters:
    if drop_connect_rate:
        x = drop_connect(x, p=drop_connect_rate, training=self.training)
    x = x + inputs  # skip connection

BiFPN

同 YOLOV3 等檢測框架一樣,EfficientDet 也加入了 FPN 元素來加強輸出 feature map 的特征表示。區別在於 BiFPN 的連接更加復雜,同時可以作為一個基礎層重復多次!

如上圖所示:(a) 是傳統的 FPN 結構,只有自頂向下的連接;(b) PANet 包括自頂向下和自底向上兩條路徑連接;(c) NAS-FPN 則是通過 NAS 搜出來的連連看結構。

作者觀察到 PANet 的效果比  FPN 和 NAS-FPN 效果好,但就是計算量大了點。於是,作者從 PANet 出發,先移除掉了兩個節點,然后在同一 level 的輸入和輸出節點之間,連上了一條線,有點 skip-connection 的味道。最后作者認為這樣的連接可以作為一個基礎 Block 而重復使用。

這里首先會將所有的輸出 feature map 統一到相同 channel 來進行后續 Block 處理,這個 channel 數和 Block 重復次數也會根據不同的子版本而變化。

BiFPN 相比 FPN 能漲 4 個點,而且參數量反而是下降的:

Anchor

\begin{equation}
\label{Anchor}
\begin{split}
& anchor\_scale = 4.0 \\
& strides = [8, 16, 32, 64, 128] \\
& scales = [2^0, 2^{\frac{1}{3}}, 2^{\frac{2}{3}}] \\
& ratios = [(1.0, 1.0), (1.4, 0.7), (0.7, 1.4)] \\
& base\_anchor\_size = anchor\_scale * stride * scale  \\
& anchor\_size\_w = base\_anchor_size * ratio[0]  \\
& anchor\_size\_h = base\_anchor_size * ratio[1]
\end{split}
\end{equation}

每個尺度上共設計 9 個 Anchor,具體的,固定每個 feature map 上的 anchor _scale 為 4.0,將其映射到輸出圖片上並加入 scale 后就確定了 Anchor 的尺度 base_anchor_size 了。隨后以每個大小的 Base Anchor 為基准,設計三種寬高比的 Anchor。

Regressor & Classifier

回歸:每個預測的 feature map 會經過 N 次深度可分離卷積(DSC+BN+SW)后,再接上一個用於預測回歸結果的深度可分離卷積(PW 的輸出 channel = num_anchors * 4)。

分類:同上,區別只是最后的 PW 層輸出 channel = num_anchors * num_classes。

Anchor+Regression

YOLOV3 編解碼倒是很像,區別在於中心點偏移沒有經過 sigmoid 函數:

記先驗框($c_x, c_y, p_w, p_h$),其中 $c_x$ 和 $c_y$ 分別表示 Anchor 中心點距離圖像左上角的距離,$p_w$ 和 $p_h$ 則分別表示先驗框的寬和高。

記網絡回歸輸出為($t_x, t_y, t_w, t_h$),其中$t_x$ 和 $t_y$ 用以偏移先驗框的中心到檢測框,$t_w$ 和 $t_h$ 則用來縮放先驗框到檢測框大小,

那么檢測框($b_x, b_y, b_w, b_h$)可以用以下表達式表示:

\begin{equation}
\label{a}
\begin{split}
& b_x =  t_x + c_x \\
& b_y =  t_y + c_y \\
& b_w =  p_w e^{t_w} \\
& b_h =  p_h e^{t_h} \\
\end{split}
\end{equation}

LOSS

分類 loss 采用 focal loss,區別在於正負樣本之間有個 margion,IoU < 0.4 的 Anchor 標記為負樣本,IoU >= 0.5 的 Anchor 標記為正樣本。

回歸目標

\begin{equation}
\label{target}
\begin{split}
& \delta_x = (g_x - c_x) / p_w \\
& \delta_y = (g_y - c_y) / p_h \\
& \delta_w = log(g_w / p_w) \\
& \delta_h = log(g_h / p_h) \\
\end{split}
\end{equation}

回歸 loss 則類似於 SmoothL1 loss:

\begin{equation}
\label{Smooth_L1}
smooth_{L_1}(x) = \begin{cases}
\ 0.5 * 9.0 * x^2 & if \ \lvert{x}\rvert < 1.0/9.0 \\
\ \lvert{x}\rvert - 0.5/9.0 & \ otherwise
\end{cases}
\end{equation}

Compound Scaling

Model Scaling 指的是人們經常根據資源的限制,對模型進行調整。比如說為了把 backbone 部分 scale up,得到更大的模型,就會考慮把層數加深, Res50 -> Res101這種,或者說比如把輸入圖的分辨率拉大。

EfficientNet 在 Model Scaling 的時候考慮了網絡的 width, depth, and resolution 三要素。而 EfficientDet 進一步擴展, 把 EfficientNet 拿來做 backbone 的同時,neck 部分,BiFPN 的 channel 數量重復的 layer 數量也可以控制;此外還有 head 部分的層數,以及 輸入圖片的分辨率,這些組成了 EfficientDet 的 scaling config 。

self.backbone_compound_coef = [0, 1, 2, 3, 4, 5, 6, 6]
self.fpn_num_filters = [64, 88, 112, 160, 224, 288, 384, 384]
self.fpn_cell_repeats = [3, 4, 5, 6, 7, 7, 8, 8]
self.input_sizes = [512, 640, 768, 896, 1024, 1280, 1280, 1536]
self.box_class_repeats = [3, 3, 3, 4, 4, 4, 5, 5]

Q

1. Backbone 裁剪網絡是否大幅影響模型性能

2. 卷積層后的 Dropout 層是否需要

3. 控制計算量的前提下 BiFPN 重復幾次最合適

4. Anchor 設計可以更有針對性

5. 正負樣本划分存在 Match Unbalance 問題

6. 匹配階段為負樣本的有可能回歸到正樣本,參考 HAMBox

7. 回歸 Loss 可以嘗試替換成 CIoU Loss

8. 沒有多尺度訓練

9. 數據增廣太單一,只有隨機翻轉


免責聲明!

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



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