論文:EfficientDet: Scalable and Efficient Object Detection
關聯:EfficientNet: Rethinking Model Scaling for Convolutional Neural Networks

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 Conv 和 SENet 結構,這一系列網絡結構從 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)
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))
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]

