1、總體結構
對於多個候選區域依次進入特徵提取網絡進行圖像特徵提取,SPPNet採用直接將全圖輸入到卷積神經網絡中提取圖像特徵,然後在圖像特徵圖上找到候選區域的圖像特徵。此外,採用Spatial Pyramid Pooling將不同尺寸的候選區域特徵轉化為固定尺寸的值,避免了RCNN採用的縮放操作。
總結來說,SPPNet的貢獻可分為共享卷積和Spatial Pyramid Pooling兩方面,下文將從這兩個方面對SPPNet實現方法進行介紹,並逐行閱讀Spatial Pyramid Pooling的Pytorch源碼。
2、共享卷積
SPPNet依舊採用Selective Search來獲取圖像的候選區域,這一點和RCNN一致。但是RCNN是將獲得的2000個候選區域依次輸入到卷積神經網絡中去提取圖像特徵,而SPPNet是先將整張圖像輸入到卷積神經網絡中獲取特徵圖,隨後在這該特徵圖上獲取候選區域的特徵,相當於前者是過2000次卷積神經網絡來提取特徵,後者只過了一次,所以SPPNet提出的共享卷積可以加快運算速度。
在具體的實現上,SPPNet直接將原圖輸入到卷積神經網絡中直接圖像特徵,然後利用區域和特徵圖之間的映射關係得到候選區域的特徵,如下圖所示:
3、Spatial Pyramid Pooling
對於任意大小的特徵圖(如B×C×H×W),Spatial Pyramid Pooling首先分別將特徵圖劃分為若干數量的子塊,然後對這些子塊計算最大池化,將計算結果進行拼接即可得到固定大小的輸出。
下圖採用了三個分支,分別將特徵圖劃分1×1、2×2、4×4大小的子塊(顯然,在不同的劃分模式下子塊大小是不一致的,輸入特徵圖尺寸不一致時子塊大小也不一樣),然後對每個子塊進行最大池化,即將不同大小的子塊都轉化為一個值,將池化之後的結果進行拼接即可得到一個大小固定為21維的輸出。如此一來,無論輸入特徵圖的尺寸發生如何變化,Spatial Pyramid Pooling均可將其轉化為固定大小的尺寸進行輸出。
4、Spatial Pyramid Pooling源碼解析
本部分以Pytorch 版本的源碼為例進行分析(地址:https://github.com/mmmmmmiracle/SPPNet/blob/master/sppnet.py)。
def spatial_pyramid_pool(previous_conv, num_sample, previous_conv_size, out_pool_size):
"""
previous_conv: a tensor vector of previous convolution layer
num_sample: an int number of image in the batch
previous_conv_size: an int vector [height, width] of the matrix features size of previous convolution layer
out_pool_size: a int vector of expected output size of max pooling layer
returns: a tensor vector with shape [1 x n] is the concentration of multi-level pooling
"""
for i in range(len(out_pool_size)):
# out_pool_size是一個數組,例如[1,2,4],表示需要將原先的特徵圖分別劃分為1×1、2×2、4×4三種.
h, w = previous_conv_size
# h,w表示原先特徵圖的長和寬
h_wid = math.ceil(h / out_pool_size[i])
w_wid = math.ceil(w / out_pool_size[i])
# 計算每一個子塊的長和寬,這裡需要進行取整
h_str = math.floor(h / out_pool_size[i])
w_str = math.floor(w / out_pool_size[i])
# 計算池化的步長
max_pool = nn.MaxPool2d(kernel_size=(h_wid, w_wid), stride=(h_str, w_str))
x = max_pool(previous_conv)
# 對每個子塊進行最大池化
if i == 0:
spp = x.view(num_sample, -1)
else:
spp = torch.cat((spp, x.view(num_sample, -1)), 1)
# 拼接各個子塊的輸出結果
return spp