SPP,PPM、ASPP和FPN結構理解和總結


轉載:https://blog.csdn.net/muyijames/article/details/106996209

轉載:https://www.jianshu.com/p/f743bd9041b3

轉載:https://blog.csdn.net/m0_37798080/article/details/103163397

1 綜述

SPP論文鏈接:
Spatial Pyramid Pooling in Deep Convolutional Networks for Visual Recognition

pspNet論文鏈接:
Pyramid Scene Parsing Network

ASPP論文鏈接(此處已deeplab v3為例):
deeplab v3(2017年):Rethinking Atrous Convolution for Semantic Image Segmentation
deeplab v3+(2018年):Encoder-Decoder with Atrous Separable
Convolution for Semantic Image Segmentation

FPN論文連接:
Feature Pyramid Networks for Object Detection

1.1 SPP結構(Spatial Pyramid Pooling)

在何愷明2015年《Spatial Pyramid Pooling in Deep ConvolutionalNetworks for Visual Recognition》被提出,改論文主要改進兩點:

解決CNN需要固定輸入圖像的尺寸,導致不必要的精度損失的問題

因為帶有全連接層的網絡結構都需要固定輸入圖像的尺度,當然后期也有直接用conv層代替FC層的,比如SSD網絡直接用conv層來計算邊界框坐標和置信度的。

解決R-CNN對候選區域進行重復卷積計算,導致計算冗余的問題

因為R-CNN網絡中基於segment seletive輸出的2000個候選框都要重新計算feature map較為耗時,因此提出了候選區域到全圖的特征(feature map)之間的對應映射,這樣圖像只需計算一次前向傳播即可。
在之后的 fast R-CNN 和 faster R-CNN 都采用這種映射關系,為ROI pooling層。
但在mask R-CNN中,用ROI Align替代了ROI pooling層,其認為兩次量化的候選框與最開始的回歸候選框有一定偏差,影響檢測和分割准確度,ROI Align中不進行float量化,通過雙線性內插計算四個坐標點,然后進行max pooling。

1.2 PPM結構(Pyramid Pooling Module)

應用在語義分割中,PSPNet網絡結構如下:

在這里插入圖片描述
(1)Input Image:即自然場景下拍攝的包含不同目標的原始圖;
(2)Feature Map:即通過前面CNN獲得的特征圖,,這個CNN是預訓練的ResNet;
(3)Pyramid Pooling Module:上圖中方框POOL表示采用1x1、2x2、3x3和6x6四種不同尺寸的pooling操作得到多個尺寸的特征圖,並對這些尺寸的特征圖再次進行“1x1的Conv”來減少通道數。然后采用雙線性插值進行UPSAMPLE,即通過上采樣來獲得金字塔模塊前相同尺寸的特征圖,並在通道上進行拼接;
(4)Final Prediction:即最終預測結果;

PPM模塊代碼如下,結合代碼更直觀:

#下面是單個PPM模塊
def interp_block(prev_layer, level, feature_map_shape, input_shape):
    if input_shape == (473, 473):
        kernel_strides_map = {1: 60,
                              2: 30,
                              3: 20,
                              6: 10}
    elif input_shape == (713, 713):
        kernel_strides_map = {1: 90,
                              2: 45,
                              3: 30,
                              6: 15}
    else:
        print("Pooling parameters for input shape ",
              input_shape, " are not defined.")
        exit(1)

    names = [
        "conv5_3_pool" + str(level) + "_conv",
        "conv5_3_pool" + str(level) + "_conv_bn"
    ]
    kernel = (kernel_strides_map[level], kernel_strides_map[level])
    strides = (kernel_strides_map[level], kernel_strides_map[level])
    prev_layer = AveragePooling2D(kernel, strides=strides)(prev_layer)  #或者maxPooling;
    prev_layer = Conv2D(512, (1, 1), strides=(1, 1), name=names[0],
                        use_bias=False)(prev_layer)
    prev_layer = BN(name=names[1])(prev_layer)
    prev_layer = Activation('relu')(prev_layer)
    # prev_layer = Lambda(Interp, arguments={
    #                    'shape': feature_map_shape})(prev_layer)
    prev_layer = Interp(feature_map_shape)(prev_layer) #此處進行上采樣,resize到原來feature map的size;
    return prev_layer

#下面是1,2,3,6的PPM模塊
def build_pyramid_pooling_module(res, input_shape):
    """Build the Pyramid Pooling Module."""
    # ---PSPNet concat layers with Interpolation
    feature_map_size = tuple(int(ceil(input_dim / 8.0))
                             for input_dim in input_shape)  #原圖經過resnet后變為1/8;
    print("PSP module will interpolate to a final feature map size of %s" %
          (feature_map_size, ))

    interp_block1 = interp_block(res, 1, feature_map_size, input_shape)
    interp_block2 = interp_block(res, 2, feature_map_size, input_shape)
    interp_block3 = interp_block(res, 3, feature_map_size, input_shape)
    interp_block6 = interp_block(res, 6, feature_map_size, input_shape)

    # concat all these layers. resulted
    # shape=(1,feature_map_size_x,feature_map_size_y,4096)
    res = Concatenate()([res,
                         interp_block6,
                         interp_block3,
                         interp_block2,
                         interp_block1])
    return res

#pspnet網絡結構
def build_pspnet(nb_classes, resnet_layers, input_shape, activation='softmax'):
    """Build PSPNet."""
    print("Building a PSPNet based on ResNet %i expecting inputs of shape %s predicting %i classes" % (
        resnet_layers, input_shape, nb_classes))

    inp = Input((input_shape[0], input_shape[1], 3))
    res = ResNet(inp, layers=resnet_layers)
    psp = build_pyramid_pooling_module(res, input_shape)
#后面是經過3×3,1×1的卷積后一次上采樣到原圖
    x = Conv2D(512, (3, 3), strides=(1, 1), padding="same", name="conv5_4",
               use_bias=False)(psp)
    x = BN(name="conv5_4_bn")(x)
    x = Activation('relu')(x)
    x = Dropout(0.1)(x)

    x = Conv2D(nb_classes, (1, 1), strides=(1, 1), name="conv6")(x)
    # x = Lambda(Interp, arguments={'shape': (
    #    input_shape[0], input_shape[1])})(x)
    x = Interp([input_shape[0], input_shape[1]])(x)
    x = Activation('softmax')(x)

    model = Model(inputs=inp, outputs=x)

    # Solver
    sgd = SGD(lr=learning_rate, momentum=0.9, nesterov=True)
    model.compile(optimizer=sgd,
                  loss='categorical_crossentropy',
                  metrics=['accuracy'])
    return model

1.3 ASPP結構(Atrous Spatial Pyramid Pooling)

可以認為是SPP在語義分割中的應用,結合了空洞卷積可在不丟失分辨率(不進行下采樣)的情況下擴大卷積核的感受野。

回顧空洞卷積

Deep CNN 中普通卷積對於其他任務還有一些致命性的缺陷。較為著名的是 up-sampling 和 pooling layer 的設計。
主要問題有:

  • Up-sampling / pooling layer (e.g. bilinear interpolation) is deterministic. (參數不可學習)
  • 內部數據結構丟失;空間層級化信息丟失。
  • 小物體信息無法重建 (假設有四個pooling layer 則 任何小於 2^4 = 16 pixel 的物體信息將理論上無法重建。)
    在這樣問題的存在下,語義分割問題一直處在瓶頸期無法再明顯提高精度, 而 dilated convolution 的設計就良好的避免了這些問題。

在圖像分割領域,圖像輸入到CNN(典型的網絡比如FCN[3])中,FCN先像傳統的CNN那樣對圖像做卷積再pooling,降低圖像尺寸的同時增大感受野,但是由於圖像分割預測是pixel-wise的輸出,所以要將pooling后較小的圖像尺寸upsampling到原始的圖像尺寸進行預測(upsampling一般采用deconv反卷積操作,deconv可參見知乎答案如何理解深度學習中的deconvolution networks?),之前的pooling操作使得每個pixel預測都能看到較大感受野信息。因此圖像分割FCN中有兩個關鍵,一個是pooling減小圖像尺寸增大感受野,另一個是upsampling擴大圖像尺寸。在先減小再增大尺寸的過程中,肯定有一些信息損失掉了,那么能不能設計一種新的操作,不通過pooling也能有較大的感受野看到更多的信息呢?答案就是dilated conv。

此處已deeplab v3網絡為例,deeplab v3論文中的ASPP結構如下如所示

在這里插入圖片描述

  • 其中的1*1卷積,論文中的解釋是當 rate = feature map size 時,dilation conv 就變成了 1 ×1 conv,所以這個 1 × 1 conv相當於rate很大的空洞卷積。還加入了全局池化,再上采樣到原來的 feature map size,思想來源於PSPnet。為什么用 rate = [6, 12, 18] ?是論文實驗得到的,因為這個搭配比例的 mIOU 最高。
  • 在 backbone 的每一個block里面又參考了HDC的思想,設置了 [1,2,1] 的rate,所以每個conv的rate = Rate * rate。論文給出的 Multi-grid 部分結果如下所示。
    在這里插入圖片描述

這里提一下deeplab v3+ 結構,是對V3的一個加強,主要有兩個改進點

  • 對ASPP中的 upsample 進行了改進

下圖來自 deeplab v3+ 論文中,(a) 是deeplab v3的結構,( c) 是deeplab v3+的結構(Encode-Decode),v3+ 中將上采樣變成了2次 4× 的 upsample,相比於v3中直接進行 8× 的 upsample,具有更豐富的語義信息,所以對物體邊緣分割效果較好。

  • deeplab v3+中另一個改進點,將 modify xception 作為 backbone;(這個改進點與ASPP無關)

如此下圖所示,(1)加深了網絡結構層數,且利用了 Atrous Separable Convolution 來減少權重參數;(2)利用 stride = 2 的 depthwise conv 來替代max pooling 進行下采樣;(3)在每一個 depthwise conv 后都加了 BN 和 relu 層。作者在論文中做了各種嘗試組合,有興趣的可查看論文

 

Decoded module

  xception的輸出2048維特征接到ASPP上得到256維multi-scale context feature map(一般s=16),再4×上采樣,和backbone上的同分辨率的low-level feature map concat(一般是entry flow的第一個shortcut block的輸出,剛好s=4)。這個時候要讓low-level feature map在concat后的總特征圖中占比小,因為它的語義信息太少了,所以接了1*1的低維conv,這個地方可不是為了降低計算量,關於這個conv的channel選取,論文給出了對比試驗Table 1。concat后再接3*3 conv block,它的channel和block個數,論文中也進行了實驗驗證Table 2.最后再進行4×上采樣,達到原圖的分辨率。此外,作者還實驗了將aspp和decode中的卷積替換為depthwise conv,mIOU沒有明顯降低,flops大大降低了。

 

空洞卷積潛在問題 1:The Gridding Effect

假設我們僅僅多次疊加 dilation rate 2 的 3 x 3 kernel 的話,則會出現這個問題:

 

(a)圖對應3x3的1-dilated conv,和普通的卷積操作一樣,(b)圖對應3x3的2-dilated conv,實際的卷積kernel size還是3x3,但是空洞為1,也就是對於一個7x7的圖像patch,只有9個紅色的點和3x3的kernel發生卷積操作,其余的點略過。也可以理解為kernel的size為7x7,但是只有圖中的9個點的權重不為0,其余都為0。 可以看到雖然kernel size只有3x3,但是這個卷積的感受野已經增大到了7x7(如果考慮到這個2-dilated conv的前一層是一個1-dilated conv的話,那么每個紅點就是1-dilated的卷積輸出,所以感受野為3x3,所以1-dilated和2-dilated合起來就能達到7x7的conv),(c)圖是4-dilated conv操作,同理跟在兩個1-dilated和2-dilated conv的后面,能達到15x15的感受野。對比傳統的conv操作,3層3x3的卷積加起來,stride為1的話,只能達到(kernel-1)*layer+1=7的感受野,也就是和層數layer成線性關系,而dilated conv的感受野是指數級的增長。

dilated的好處是不做pooling損失信息的情況下,加大了感受野,讓每個卷積輸出都包含較大范圍的信息。

我們發現 kernel 並不連續,也就是並不是所有的 pixel 都用來計算了,因此這里將信息看做 checker-board 的方式會損失信息的連續性。這對 pixel-level dense prediction 的任務來說是致命的。

潛在問題 2:Long-ranged information might be not relevant.

我們從 dilated convolution 的設計背景來看就能推測出這樣的設計是用來獲取 long-ranged information。然而光采用大 dilation rate 的信息或許只對一些大物體分割有效果,而對小物體來說可能則有弊無利了。如何同時處理不同大小的物體的關系,則是設計好 dilated convolution 網絡的關鍵。

HDC(Hybrid Dilated Convolution)

針對以上幾個問題,圖森組的文章對其提出了較好的解決的方法。他們設計了一個稱之為 HDC 的設計結構。
它有幾個特性,可以從一定程度上解決上述問題。這里咱不討論。我們可以從一張圖來對比一下正常空洞卷積與HDC的效果:

 

 可以看到經過卷積之后,HDC能夠獲得更多的圖像信息,不會出現像正常空洞卷積一樣的小方塊;

 

1.4 FPN結構(Feature Pyramid Networks for Object Detection)

FPN通常用在 object detection 網絡中,通常低層的特征語義信息比較少,但是目標位置准確;高層的特征語義信息比較豐富,但是目標位置比較粗略。FPN 即是對兩者進行了融合,同時利用低層特征高分辨率和高層特征的高語義信息,通過融合這些不同層的特征達到預測的效果。並且預測是在每個融合后的特征層上單獨進行的,這和常規的特征融合方式不同。

FPN結構如下圖:
在這里插入圖片描述
自底向上的路徑:
具體而言,當 backbone 是 ResNet 時,我們使用每個階段的最后一個residual block輸出的特征激活輸出。 對於conv2,conv3,conv4 和 conv5 輸出,我們將這些最后residual block的輸出表示為 {C2,C3,C4,C5},並且它們相對於輸入圖像具有 {4, 8, 16, 32} 的步長。

自頂向下的路徑:
通過對在空間上更抽象但語義更強高層特征圖進行上采樣來幻化高分辨率的特征。在C5上附加一個1×1卷積層來生成低分辨率圖P5,隨后通過側向連接從底向上的路徑,使得高層特征得到增強。將低分辨率的特征圖做2倍上采樣,最終的特征映射集稱為{P2,P3,P4,P5},分別對應於{C2,C3,C4,C5},具有相同的尺寸。通過按元素相加,將上采樣映射與相應的自底而上映射合並。
最后,在每個合並的圖上附加一個3×3卷積來生成最終的特征映射,這是為了減少上采樣的混疊效應。

作者在將其應用到 RPN 和 fast / faster R-CNN 中,在論中有詳細的實驗數據,大家有興趣可自行查閱。

在YOLO v3中也采用了類似 FPN 的結構,但里面用了concat 進行特征融合

2 總結

空間金字塔思想在圖像處理中具有很重要的作用,
傳統圖像處理中:

  • 在SIFT 中利用高斯差分金字塔 (DOG) 保持尺度不變性;
  • 在配准 / 匹配算法中利用空間金字塔進行粗匹配和細匹配達到效率優化;

CNN 網絡中:

    • 在含有FC層網絡中利用 SPP 改進輸入需要固定尺度的問題;
    • 在語義分割中利用 ASPP 在不丟失信息時組合不同感受野的語義信息,提高分割精度;
    • 在 object detection 網絡中利用 FPN 改善小目標難檢測的問題;


免責聲明!

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



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