如何计算感受野大小?

How to calculate receptive field size?

我正在阅读有关使用 CNN(卷积神经网络)进行对象检测的论文。

Rich feature hierarchies for accurate object detection and semantic segmentation

这里有一段关于感受野的引述:

The pool5 feature map is 6x6x256 = 9216 dimensional. Ignoring boundary effects, each pool5 unit has a receptive field of 195x195 pixels in the original 227x227 pixel input. A central pool5 unit has a nearly global view,
while one near the edge has a smaller, clipped support.

我的问题是:

  1. 感受野的定义是什么?
  2. 他们如何计算感受野的大小和位置?
  3. 我们如何使用caffe/pycaffe计算感受野的边界矩形?

1) 影响最后一个卷积输出的是像素区域的大小。

2) 对于每个卷积和池化操作,计算输出的大小。现在找到导致输出大小为 1x1 的输入大小。那就是感受野的大小

3) 您不需要使用库来完成它。对于每个 2x2 池,输出大小沿每个维度减少一半。对于步幅卷积,您还可以将每个维度的大小除以步幅。您可能需要削减一些维度,具体取决于您是否对卷积使用填充。最简单的情况是使用 padding = floor(kernel size/2),这样一个卷积就不会对输出大小有任何额外的改变。

这里是 python 脚本,除了计算步幅和输出大小外,还计算 RF 大小。

    # [filter size, stride, padding]

convnet =[[11,4,0],[3,2,0],[5,1,2],[3,2,0],[3,1,1],[3,1,1],[3,1,1],[3,2,0],[6,1,0]]
layer_name = ['conv1','pool1','conv2','pool2','conv3','conv4','conv5','pool5','fc6-conv']
imsize = 227


def outFromIn(isz, layernum = 9, net = convnet):
    if layernum>len(net): layernum=len(net)

    totstride = 1
    insize = isz
    #for layerparams in net:
    for layer in range(layernum):
        fsize, stride, pad = net[layer]
        outsize = (insize - fsize + 2*pad) / stride + 1
        insize = outsize
        totstride = totstride * stride

    RFsize = isz - (outsize - 1) * totstride

    return outsize, totstride, RFsize

if __name__ == '__main__':

    print "layer output sizes given image = %dx%d" % (imsize, imsize)
    for i in range(len(convnet)):
        p = outFromIn(imsize,i+1)
        print "Layer Name = %s, Output size = %3d, Stride = % 3d, RF size = %3d" % (layer_name[i], p[0], p[1], p[2])

如上所述,可能正确计算了 RF:

#Compute input size that leads to a 1x1 output size, among other things   

# [filter size, stride, padding]

convnet =[[11,4,0],[3,2,0],[5,1,2],[3,2,0],[3,1,1],[3,1,1],[3,1,1],[3,2,0],[6,1,0]]
layer_name = ['conv1','pool1','conv2','pool2','conv3','conv4','conv5','pool5','fc6-conv']
imsize = 227

def outFromIn(isz, layernum = 9, net = convnet):
    if layernum>len(net): layernum=len(net)

    totstride = 1
    insize = isz
    #for layerparams in net:
    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( layernum = 9, net = convnet):
    if layernum>len(net): layernum=len(net)
    outsize = 1
    #for layerparams in net:
    for layer in reversed(range(layernum)):
        fsize, stride, pad = net[layer]
        outsize = ((outsize -1)* stride) + fsize
    RFsize = outsize
    return RFsize

if __name__ == '__main__':

    print "layer output sizes given image = %dx%d" % (imsize, imsize)
    for i in range(len(convnet)):
        p = outFromIn(imsize,i+1)
        rf = inFromOut(i+1)
        print "Layer Name = %s, Output size = %3d, Stride = % 3d, RF size = %3d" % (layer_name[i], p[0], p[1], rf)

这是另一种直接计算感受野的方法。 Whosebug不支持数学公式,更易读的版本请参考Calculating Receptive Field of CNN

第$k$层的感受野(RF)$l_k$是:

$$ l_k = l_{k-1} + ((f_k - 1) * \prod_{i=1}^{k-1}s_i) $$

其中$l_{k-1}$是第$k-1$层的感受野,$f_k$是filter size (高度或宽度,但假设它们在这里相同),$s_i$ 是步幅 第 $i$ 层。

上面的公式是自下而上计算感受野(from layer 1).直观上,$k$ 层中的 RF 覆盖了 $(f_k - 1) * s_{k-1}$ 个像素 与层 $k-1$ 相关。但是,增量需要转换为 第一层,所以增量是一个阶乘——$k-1$ 层的步幅是 下层的步幅呈指数增长。

希望这对您有所帮助。

2019 年 12 月 11 日更新:

TF 库已移至 https://github.com/google-research/receptive_field

另请参阅 Distill 论文 "Computing Receptive Fields of Convolutional Neural Networks":https://distill.pub/2019/computing-receptive-fields/

旧:

Tensorflow 现在支持感受野计算,只需使用 tf.contrib.receptive_field

详情见https://github.com/tensorflow/tensorflow/tree/master/tensorflow/contrib/receptive_field

假设我们的网络架构包含多个卷积层。对于每个卷积层,我们定义一个平方核大小和一个扩张率。另外,假设步幅为1。因此,您可以通过以下python代码段计算网络的感受野:

K=[3,3]   # Kernel Size
R=[1,2]  # Dilation Rate

RF=1
d=1 # Depth
for k,r in zip(K,R):
    support=k+(k-1)*(r-1) # r-dilated conv. adds r-1 zeros among coefficients
    RF=support+(RF-1)
    print('depth=%d, K=%d, R=%d, kernel support=%d'%(d,k,r,support))
    d=d+1
print('Receptive Field: %d'%RF)

例如,让我们计算著名的 DnCNN(去噪卷积神经网络)[1] 的感受野 (RF)。 使用上面的代码段和以下输入来计算该网络的 RF。 (你会得到 RF=35)。

# In DnCNN-S, the network has 17 convolution layers.
K=[3]*17  # Kernel Size
R=[1]*17  # Dilation Rate

[1]张凯等。 "Beyond a gaussian denoiser: Residual learning of deep cnn for image denoising." IEEE 图像处理汇刊 26.7 (2017):3142-3155。