高效的從Backbone CNN提取特徵的方法:FPN

FPN(Features Pyramid Networks) 特徵金字塔網絡是從backbone CNN中提取特徵用於後續目標檢測和語義分割等問題。一個top-down結構結合lateral連接來建立所有不同尺度特徵的high-level語義特徵。

背景

在這裏插入圖片描述
(a)使用原始圖像去建立特徵金字塔,特徵相互獨立地在不同尺度上的圖像進行計算,所以非常慢,使得此方法不能用於實際的應用。
(b)近期的detection系統選擇僅僅使用單尺度的特徵來進行快速的detection,即backbone的最後輸出特徵。
(c)一個可選的方法是重用ConvNet計算的金字塔層次特徵。
(d)FPN比(b)和(c)都要快但是更準確。
上圖中,越厚的邊緣代表語義更強的特徵。

深度Convnet通過逐層計算feature hierarchy,因爲sub-sampling使得feature hierarchy本身具有金字塔的形狀,但是不同分辨率的feature-maps具有因深度引起的較大語義差距。高像素feature-map具有low-level特徵,會影響目標識別的特徵能力。SSD就使用了圖 ( c ) 的方法,爲了避免使用low-level特徵,SSD從高層開始進行金字塔的建立(e.g.conv4_3 of VGGNets)並且添加了幾個新層。這種方法錯過了高分辨率feature-map的使用,作者認爲這些高分辨率feature-map對於檢測small目標是重要的。

FPN的目標是特徵金字塔的所有尺度特徵都具有很強的語義性。爲了完成這個目的,我們依靠一個架構來結合低分辨率,語義強的特徵與高分辨率,語義弱的特徵,通過一個top-down通路和lateral鏈接的方式,(d)所示。
在這裏插入圖片描述
相似的結構也採用了top-down和跳連,如圖2的Top,但是他們的目標是產生一個單一的high-level和fine resolution的feature map來預測。FPN對每一層的特徵都進行獨立地預測。

解釋一下fine resolution
參考鏈接:https://www.cnblogs.com/kk17/p/9807571.html
CNN 抽取 semantic feature 導致的 coarse representation雖然具有強的語義性,但和 detect small objects 需要 fine resolution 之間的矛盾,small objects 因爲 small,很難在 coarse representation 還有很好的表示,很可能就被忽略了。Backbone的stride太大導致最終的表示是coarse resolution表示,小目標容易被忽略。

FPN方法

在這裏插入圖片描述
給定一個單尺度的圖像,輸出的不同level features的size是成比例的。這個處理過程是和backbone無關的,本文以ResNet爲例。如上的構建涉及3個部分:top-down通路,bottom-up通路。lateral連接。

bottom-up通路

就是圖中左側一個ConvNet前向計算的過程,產生了不同尺度的feature map。就ResNet來說,每一個階段最後一個殘差塊的輸出爲:{C2,C3,C4,C5}\{C_{2},C_{3},C_{4},C_{5}\}分別對應的Conv2,Conv3,Conv4,Conv5。作者沒有包括Conv1的feature map,由於佔用內存太大。

top-down通路和lateral連接

top-down通路是右側自上而下的通路,該通路逐漸升採樣spatially coarser但是語義stronger的特徵。這些特徵會通過lateral連接將bottom-up的特徵傳來進行merge。bottom-up傳來的特徵是low-level的語義,但是其激活能夠更準確地定位因爲被subsample的次數更少。

虛擬框展示了block來產生top-down特徵。對於一個coarser-resolution feature map,首先升採樣到2倍(使用了最近鄰插值for simplicity)。之中升採樣的特徵需要被merge到對應的bottom-up特徵(經歷一個1x1卷積層來減少channel)通過element-wise addition。這個過程需要不斷迭代直到finest resolution map產生。

迭代開始前,使用一個1x1卷積層在C5C_{5}來產生coarsest resolution map。使用一個3x3卷積對於merge之後的特徵圖來減少升採樣的重疊效應。最終的特徵集{P2,P3,P4,P5}\{P_{2},P_{3},P_{4},P_{5}\}對應{C2,C3,C4,C5}\{C_{2},C_{3},C_{4},C_{5}\}的spatial size。

因爲每一層的特徵是使用shared 分類器和迴歸器,作者固定每個feature map的channel維度d=256d=256,因此其他的多餘的卷積也是256-channel輸出。這些層都沒有非線性,實驗上有微小的影響。

代碼:

使用代碼來解析上述的過程:

# Bottom-up
self.RCNN_layer0 = nn.Sequential(resnet.conv1, resnet.bn1, resnet.relu, resnet.maxpool)
self.RCNN_layer1 = nn.Sequential(resnet.layer1)
self.RCNN_layer2 = nn.Sequential(resnet.layer2)
self.RCNN_layer3 = nn.Sequential(resnet.layer3)
self.RCNN_layer4 = nn.Sequential(resnet.layer4)
c1 = self.RCNN_layer0(im_data)
c2 = self.RCNN_layer1(c1)
c3 = self.RCNN_layer2(c2)
c4 = self.RCNN_layer3(c3)
c5 = self.RCNN_layer4(c4)


# Top layer
self.RCNN_toplayer = nn.Conv2d(2048, 256, kernel_size=1, stride=1, padding=0)  # reduce channel

# Smooth layers
self.RCNN_smooth1 = nn.Conv2d(256, 256, kernel_size=3, stride=1, padding=1)
self.RCNN_smooth2 = nn.Conv2d(256, 256, kernel_size=3, stride=1, padding=1)
self.RCNN_smooth3 = nn.Conv2d(256, 256, kernel_size=3, stride=1, padding=1)

# Lateral layers
self.RCNN_latlayer1 = nn.Conv2d(1024, 256, kernel_size=1, stride=1, padding=0)
self.RCNN_latlayer2 = nn.Conv2d( 512, 256, kernel_size=1, stride=1, padding=0)
self.RCNN_latlayer3 = nn.Conv2d( 256, 256, kernel_size=1, stride=1, padding=0)

def _upsample_add(self, x, y):
    _,_,H,W = y.size()
    return F.upsample(x, size=(H,W), mode='bilinear') + y
    
# Top-down
p5 = self.RCNN_toplayer(c5)
p4 = self._upsample_add(p5, self.RCNN_latlayer1(c4))
p4 = self.RCNN_smooth1(p4)
p3 = self._upsample_add(p4, self.RCNN_latlayer2(c3))
p3 = self.RCNN_smooth2(p3)
p2 = self._upsample_add(p3, self.RCNN_latlayer3(c2))
p2 = self.RCNN_smooth3(p2)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章