本文共 3904 字,大约阅读时间需要 13 分钟。
CNN中的感受野是CNN中的一个很重要的概念,关于其解释网上有许多版本,如
大意都是指CNN中处在某一层的神经元(或者说特征图上的某一点)在输入图像中所对应的感受区域大小,这一概念其实正是从生物学上的感受野类比过来的。
在网上主要找到了两种计算感受野的方法,一种是,另一种是,前者理解起来比较快,方便手算;后者理解起来可能要花点时间但比较系统且可以顺次计算出各层的感受野,方便编程实现。
计算方法如下图所示:
图中已经解释的比较清楚,第一个式子就是常用的根据输入尺寸计算卷积输出特征图尺寸的公式,相信熟悉CNN的童鞋一定不陌生;第三个式子可以这样理解,对于 r i n r_{in} rin所在层第一个k*k卷积区域(其对应的感受野也即下一层的一个神经元对应的感受野 r o u t r_{out} rout),卷积区域的第一个位置对应的感受野就是 r i n r_{in} rin,剩下的(横向或纵向)k-1个位置中每个位置产生的感受野增量就是上一层的特征间隔 j i n j_{in} jin;最后一个式子则是在第三个式子的基础上把padding的影响考虑进来了,根据 s t a r t o u t = r o u t − 2 ∗ p ∗ j i n 2 start_{out}=\frac{r_{out}-2*p*j_{in}}{2} startout=2rout−2∗p∗jin(不难从下图中得出),再将第三个式子带入即可得到 s t a r t o u t = r i n / 2 + ( k − 1 2 − p ) ∗ j i n start_{out}=r_{in}/2+(\frac{k-1}{2}-p)*j_{in} startout=rin/2+(2k−1−p)∗jin,对于Layer0而言, s t a r t i n = r i n / 2 start_{in}=r_{in}/2 startin=rin/2,即第四个式子。另外,由上面的第二个和第三个式子可以推出关于感受野计算的一个简化公式:
式中 l k l_k lk表示第k层对应的感受野尺寸,注意 f k − 1 f_{k}-1 fk−1乘以的因子是前k-1层的步长累乘的结果(当前层的步长不参与当前层感受野的计算)自底向上的感受野计算方式的python3代码如下
# coding:utf-8net_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']}}def calc_receptive_field(network, input_size): """Calculate receptive field according to name of network and input_size. Assume input image is square""" if network is None or input_size is None: return None assert network in net_struct.keys(), "unknown net" layer_sizes, feat_gaps, RF_sizes = [], [], [] net_list = net_struct[network]['net'] name_list = net_struct[network]['name'] assert len(net_list) == len(name_list), "unmatched between length of layers and number of names" n_in = input_size j_in = 1 RF_in = 1 for i in range(len(net_list)): kernel, stride, pad = net_list[i] n_out = (n_in + 2*pad - kernel)//stride + 1 j_out = stride * j_in RF_out = RF_in + (kernel-1)*j_in layer_sizes.append(n_out) feat_gaps.append(j_in) n_in, j_in, RF_in = n_out, j_out, RF_out print("Layer Name:{}, Output size:{}, Stride:{}, RF size:{}".format(name_list[i], n_out, j_out, RF_out)) result = zip(name_list, layer_sizes, feat_gaps, RF_sizes) return resultif __name__ == "__main__": input_size = 224 print("input size: 224*224") for network in net_struct.keys(): print("-----------------network name: %s--------------" % network) calc_receptive_field(network, input_size)
程序输出结果:
最后推荐一个网页版的,输入各卷积层参数配置即可计算出最后的感受野大小。 参考资料: