學習RCNN系列論文時, 出現了感受野(receptive field)的名詞, 感受野的尺寸大小是如何計算的,在網上沒有搜到特別詳細的介紹, 為了加深印象,記錄下自己對這一感念的理解,希望對理解基於CNN的物體檢測過程有所幫助。
1 感受野的概念
在卷積神經網絡中,感受野的定義是 卷積神經網絡每一層輸出的特征圖(feature map)上的像素點在原始圖像上映射的區域大小。
RCNN論文中有一段描述,Alexnet網絡pool5輸出的特征圖上的像素在輸入圖像上有很大的感受野(have very large receptive fields (195 × 195 pixels))和步長(strides (32×32 pixels) ), 這兩個變量的數值是如何得出的呢?
2 感受野大小的計算
感受野計算時有下面的幾個情況需要說明:
(1)第一層卷積層的輸出特征圖像素的感受野的大小等於濾波器的大小
(2)深層卷積層的感受野大小和它之前所有層的濾波器大小和步長有關系
(3)計算感受野大小時,忽略了圖像邊緣的影響,即不考慮padding的大小,關於這個疑惑大家可以閱讀一下參考文章2的解答進行理解
這里的每一個卷積層還有一個strides的概念,這個strides是之前所有層stride的乘積。
即strides(i) = stride(1) * stride(2) * ...* stride(i-1)
關於感受野大小的計算采用top to down的方式, 即先計算最深層在前一層上的感受野,然后逐漸傳遞到第一層,使用的公式可以表示如下:
RF = 1 #待計算的feature map上的感受野大小
for layer in (top layer To down layer):
RF = ((RF -1)* stride) + fsize
stride 表示卷積的步長; fsize表示卷積層濾波器的大小
用python實現了計算Alexnet zf-5和VGG16網絡每層輸出feature map的感受野大小,實現代碼:

#!/usr/bin/env python net_struct = {'alexnet': {'net':[[11,4,0],[3,2,0],[5,1,2],[3,2,0],[3,1,1],[3,1,1],[3,1,1],[3,2,0]], 'name':['conv1','pool1','conv2','pool2','conv3','conv4','conv5','pool5']}, 'vgg16': {'net':[[3,1,1],[3,1,1],[2,2,0],[3,1,1],[3,1,1],[2,2,0],[3,1,1],[3,1,1],[3,1,1], [2,2,0],[3,1,1],[3,1,1],[3,1,1],[2,2,0],[3,1,1],[3,1,1],[3,1,1],[2,2,0]], 'name':['conv1_1','conv1_2','pool1','conv2_1','conv2_2','pool2','conv3_1','conv3_2', 'conv3_3', 'pool3','conv4_1','conv4_2','conv4_3','pool4','conv5_1','conv5_2','conv5_3','pool5']}, 'zf-5':{'net': [[7,2,3],[3,2,1],[5,2,2],[3,2,1],[3,1,1],[3,1,1],[3,1,1]], 'name': ['conv1','pool1','conv2','pool2','conv3','conv4','conv5']}} imsize = 224 def outFromIn(isz, net, layernum): totstride = 1 insize = isz for layer in range(layernum): fsize, stride, pad = net[layer] outsize = (insize - fsize + 2*pad) / stride + 1 insize = outsize totstride = totstride * stride return outsize, totstride def inFromOut(net, layernum): RF = 1 for layer in reversed(range(layernum)): fsize, stride, pad = net[layer] RF = ((RF -1)* stride) + fsize return RF if __name__ == '__main__': print "layer output sizes given image = %dx%d" % (imsize, imsize) for net in net_struct.keys(): print '************net structrue name is %s**************'% net for i in range(len(net_struct[net]['net'])): p = outFromIn(imsize,net_struct[net]['net'], i+1) rf = inFromOut(net_struct[net]['net'], i+1) print "Layer Name = %s, Output size = %3d, Stride = % 3d, RF size = %3d" % (net_struct[net]['name'][i], p[0], p[1], rf)
執行后的結果如下:
參考:
1 http://stackoverflow.com/questions/35582521/how-to-calculate-receptive-field-size
4 Convolutional Feature Maps: Elements of Efficient (and Accurate) CNN-based Object Detection
5 Spatial Pyramid Pooling in Deep Convolutional Networks for Visual Recognition
6 http://blog.cvmarcher.com/posts/2015/05/17/cnn-trick/