深度学习中的感受野计算

https://blog.csdn.net/kuaitoukid/article/details/46829355

https://blog.csdn.net/hungryof/article/details/50241351

概念

感受野(receive field)是指当前feature map中的一个原子点P与输入层中多少个原子个数相关的问题,假设输入层相关的点为(RF*RF)个(假设长宽两个方向上做同样尺度的操作),即输入层中RF*RF个原子值的变化会影响当前feature map中的指定原子P,而输入层中其他原子变化并不会影响这个指定原子P。显然局部感受野只跟kernel size有关,与stride无关。这也是卷积层的两个重要特性之一(局部感受野和参数共享)。显然RF>=1.

当然感受野也可以作为一个相对概念,即不一定是相对于输入层的感受野,你也可以定义任意层l1相对于另外任意层L2的相对域(只需要满足约束条件:L2不能再L1层的后面,档L1==L2时,RF==1).

问题:求任意层的输出Feature map上的感受野F(则感受原子点个数即为F*F).

首先我们来分析比较简单的一种情况:

    输入层通过第1层(type = conv,kernel_size =3,stride =1),再通过第2层(type = pool,kernel_size =2,stride =2),求第2层的输出feature map 在输入层的感受野F.

    显然,这是很简单的模型,第一层对于输入层的感受野为3(只跟kernel_size有关),第二层对第一层的感受野为2(只跟kernel_size有关),那第二层对输入层呢?动手画一画,就知道第2层对输入层的感受野为4*4。我们在分析的过程中,是不是现求第二层对第一层的感受野,然后结合第一层对输入层的感受野,算出第二层对输入层的感受野。这就等价一个递归关系。

现在来看一般情况

假设第i层上对第j层的局部感受野为F(i,j),显然i>=j.假定输入层为第0层。

则现在问题转化为求F(i,0)的问题。由上面分析可知F(i,i)=1,现只需要求出F(i,j) 与F(i,j-1)层的关系,即可通过F(i,i)求出F(i,0).

 通过简单情况和画图分析,可得出递归关系式,F(i,j-1) = kernel_size_j + (F(i,j)-1)*stride_j,kernel_size_j表示的第j层的kernel_size,stride_j表示第j层的stride.这个式子分为两部分,一部分是指kernel_size_j,即局部感受野,另一部分是stride,可理解为当前层在每多一个原子,上一层的感受野多增加stride个(在第一个局部感受野的基础上增加的)(只考虑一个方向的大小)。

则由递归关系和F(i,i) =1,可求出F(i,0).注意F(i,0)和F(i-1,0)并没有任何关系

感受野(receptive field)是怎样一个东西呢,从CNN可视化的角度来讲,就是输出featuremap某个节点的响应对应的输入图像的区域就是感受野。

比如我们第一层是一个3*3的卷积核,那么我们经过这个卷积核得到的featuremap中的每个节点都源自这个3*3的卷积核与原图像中3*3的区域做卷积,那么我们就称这个featuremap的节点感受野大小为3*3

如果再经过pooling层,假定卷积层的stride是1,pooling层大小2*2,stride是2,那么pooling层节点的感受野就是4*4

有几点需要注意的是,padding并不影响感受野,stride只影响下一层featuremap的感受野,size影响的是该层的感受野。

至于如何计算感受野,我的建议是top to down的方式。下面我拿一个例子来算算

 
若没有边缘填充,padding=VALID,计算公式如下:
O=ceil((W-K+1)/S)
若有边缘填充,padding=SAME,计算公式如下:
O=ceil(W/S)
O是输出尺寸,W是输入尺寸的长或宽,K是过滤器尺寸,P是边缘填充,S是步长,ceil()表示向上取整函数。


type    size   stride
conv1    3     1
pool1    2     2
conv2    3     1
pool2    2     2
conv3    3     1
conv4    3     1     
pool3    3     2


感受野计算公式:

(最后一层感受野大小 -1 )×stride + size(过滤器)
pool3感受野:
pool3  1
conv4  3
conv3  5
pool3  10
conv2  12
pool2  24
conv1  26

若没有边缘填充:

conv4  输出为2*2     输入: (w - 2 + 1)/2 = 2 -->  w=5  :5*5

conv3  输出为5*5     输入: (w - 3 + 1)/1 = 5 -->  w=7  :7*7

pool2  输出为7*7     输入:  (w - 2 + 1)/2 = 7 -->  w=15  :15*15

conv2  输出为15*15   输入:  (w - 3 + 1)/1 = 15 --> w=17 :17*17

pool1  输出为17*17   输入:  (w - 2 + 1)/2 = 17 --> w=35 :35*35
 
conv1  输出为35*35   输入:  (w - 3 + 1)/1 = 35 --> w=37 :37*37

若有边缘填充:

conv4  输出为2*2     输入: 2*2

conv3  输出为2*2     输入: 2*2

pool2  输出为2*2     输入: 4*4

conv2  输出为4*4   输入:   4*4

pool1  输出为4*4  输入:   8*8
 
conv1  输出为 8*8   输入:    8*8

为啥说感受野与ping无关   输入的图像小于感受野很疑惑  求解

计算感受野:

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))
 
 

猜你喜欢

转载自blog.csdn.net/qq_30638831/article/details/82693505