感受野的计算

一:回顾CNN

其中大家常见到的CNN运算原理应该就是下面这张图了

卷积本质就是kernel覆盖区域和kernel对应点乘,然后加和,最后每个单独维度的kernel的值加和,如果有bias再加上bias就得到这个filter的对应位置的feature map的值。这里揭开我之前的疑惑就是,同一个filter不同维度的filter'上的值是不同的,然后不同的filter也是不同值得。

 

二:感受野

感受野的定义:

The receptive field is defined as the region in the input space that a particular CNN’s feature is looking at (i.e. be affected by)

也就是某个层上的某个feature map上的某个点,对应到原输入input图像上的区域的size,然后一个感受野可以用该感受野再原图的center location坐标以及size来表征。

根据:A guide to receptive field arithmetic for Convolutional Neural Networks

得到的几个CNN中的计算公式:

n-in n-out分别表示前一层输入特征图大小和后面输出的特征图大小,p,k,s表示padding kernel_size,stride

j-out,j-in表示的是jump,我理解的其实就是累乘的stride

r-in,r-out表示前一层的感受野大小和后一层感受野的大小

start-in,start-out表示前一层的第一个feature点的中心坐标,后一层的第一个feature点的中心坐标

前面两个公式比较好理解,第一个就是由于padding stride存在,当kernel在input上滑动卷积操作时,加上两边padding大小减去kernel大小然后除于stride得到输出的feature map尺寸。

第二个就是累乘的strde之积。

然后就是第三个公式:

直观上理解就是前面一层的感受野,此时由于当前层的感受野只限于前面的计算,后面一层的感受野则在此基础上加上由于padding和stride导致在两边增加的感受野(padding)出来。所以以上公式都是从前往后计算,即依次从浅层网络往后面深层网络计算的。

然后是第四个公式:

直观上理解就是由于padding和kernel的覆盖的总区域导致中心坐标发生偏移,最终结果就是最开始的center location加上kernel覆盖的区域的中心(除以2)减去由于边界padding增加的区域的中心,单边padding所以是只有一个p。所以就得到这个最终公式。下面是该博客上的一个例子推演:

其实如果是从后往前推算感受野可能会好理解,但是如果是网络设计的复杂或者层数很深就不好手算,故此可以借助以上公式方便计算,此外还有该博主给出的脚本用于计算感受野:


# [filter size, stride, padding]
#Assume the two dimensions are the same
#Each kernel requires the following parameters:
# - k_i: kernel size
# - s_i: stride
# - p_i: padding (if padding is uneven, right padding will higher than left padding; "SAME" option in tensorflow)
# 
#Each layer i requires the following parameters to be fully represented: 
# - n_i: number of feature (data layer has n_1 = imagesize )
# - j_i: distance (projected to image pixel distance) between center of two adjacent features
# - r_i: receptive field of a feature in layer i
# - start_i: position of the first feature's receptive field in layer i (idx start from 0, negative means the center fall into padding)

import math
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], [1, 1, 0]]
layer_names = ['conv1','pool1','conv2','pool2','conv3','conv4','conv5','pool5','fc6-conv', 'fc7-conv']
imsize = 227

def outFromIn(conv, layerIn):
  n_in = layerIn[0]
  j_in = layerIn[1]
  r_in = layerIn[2]
  start_in = layerIn[3]
  k = conv[0]
  s = conv[1]
  p = conv[2]
  
  n_out = math.floor((n_in - k + 2*p)/s) + 1
  actualP = (n_out-1)*s - n_in + k 
  pR = math.ceil(actualP/2)
  pL = math.floor(actualP/2)
  
  j_out = j_in * s
  r_out = r_in + (k - 1)*j_in
  start_out = start_in + ((k-1)/2 - pL)*j_in
  return n_out, j_out, r_out, start_out
  
def printLayer(layer, layer_name):
  print(layer_name + ":")
  print("\t n features: %s \n \t jump: %s \n \t receptive size: %s \t start: %s " % (layer[0], layer[1], layer[2], layer[3]))
 
layerInfos = []
if __name__ == '__main__':
#first layer is the data layer (image) with n_0 = image size; j_0 = 1; r_0 = 1; and start_0 = 0.5
  print ("-------Net summary------")
  currentLayer = [imsize, 1, 1, 0.5]
  printLayer(currentLayer, "input image")
  for i in range(len(convnet)):
    currentLayer = outFromIn(convnet[i], currentLayer)
    layerInfos.append(currentLayer)
    printLayer(currentLayer, layer_names[i])
  print ("------------------------")
  layer_name = input ("Layer name where the feature in: ")
  layer_idx = layer_names.index(layer_name)
  idx_x = int(input ("index of the feature in x dimension (from 0)"))
  idx_y = int(input ("index of the feature in y dimension (from 0)"))
  
  n = layerInfos[layer_idx][0]
  j = layerInfos[layer_idx][1]
  r = layerInfos[layer_idx][2]
  start = layerInfos[layer_idx][3]
  assert(idx_x < n)
  assert(idx_y < n)
  
  print ("receptive field: (%s, %s)" % (r, r))
  print ("center: (%s, %s)" % (start+idx_x*j, start+idx_y*j))

后来我又发现其实tf提供了这么一个工具用于计算感受野这些参数:

https://github.com/tensorflow/tensorflow/tree/master/tensorflow/contrib/receptive_field

这些参数虽说对算法模型设计帮助看起来无关紧要,但是其实是很重要的,这些就是CNN的本质,也是了解底层实现的原理。

最后如果是想从后往前计算感受野的大小,可以使用如下公式:

R(i) = (R(i+1) - 1)*stride + k_size,R(i),R(i+1),stride,k_size分别表示第i层感受野,第i+1层感受野,第i层的stride和第i层的kernel size

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值