Receptive field 可中译为“感受野”,是卷积神经网络中非常重要的概念之一。

我个人最早看到这个词的描述是在 2012 年 Krizhevsky 的 paper 中就有提到过,当时是各种不明白的,事实上各种网络教学课程也都并没有仔细的讲清楚“感受野”是怎么一回事,有什么用等等。直到我某天看了 UiO 的博士生 Dang Ha The Hien写了一篇非常流传甚广的博文:A guide to receptive field arithmetic for Convolutional Neural Networks,才大彻大悟,世界变得好了,人生都变得有意义了,正如博主自己谈到的写作动机:

This post fills in the gap by introducing a new way to visualize feature maps in a CNN that exposes the receptive field information, accompanied by a complete receptive field calculation that can be used for any CNN architecture.

此文算是上述博文的一个精要版笔记,再加上个人的理解与计算过程。

FYI:读者已经熟悉 CNN 的基本概念,特别是卷积和池化操作。一个非常好的细致概述相关计算细节的 paper 是:A guide to convolution arithmetic for deep learning


感受野可视化

我们知晓某一层的卷积核大小对应于在上一层输出的“图像”上的“视野”大小。比如,某层有 3x3 的卷积核,那就是一个 3x3 大小的滑动窗口在该层的输入“图像”上去扫描,我们就可以谈相对于上一层,说该层下特征图(feature map)当中任一特征点(feature)的“感受野”大小只有 3x3.(打引号说明术语引用不够严谨)。

  • 先看个感受野的较严格定义:

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

一个特征点的感受野可以用其所在的中心点位置(center location)大小(size)来描述。然而,某卷积特征点所对应的感受野上并不是所有像素都是同等重要的,就好比人的眼睛所在的有限视野范围内,总有要 focus 的焦点。对于感受野来说,距离中心点越近的像素肯定对未来输出特征图的贡献就越大。换句话说,一个特征点在输入图像(Input) 上所关注的特定区域(也就是其对应的感受野)会在该区域的中心处聚焦,并以指数变化向周边扩展(need more explanation

废话不多说,我们直接先算起来。


首先假定我们所考虑的 CNN 架构对称的,并且输入图像也是方形的。这样的话,我们就忽略掉不同长宽所造成的维度不同。

Way1 对应为通常的一种理解感受野的方式。在下方左侧的上图中,是在 5x5 的图像(蓝色)上做一个 3x3 卷积核的卷积计算操作,步长为2,padding 为1,所以输出为 3x3 的特征图(绿色)。那么该特征图上的每个特征(1x1)对应的感受野,就是 3x3。在下方左侧的下图中,是在上述基础上再加了一个完全一样的卷积层。对于经过第二层卷积后其上的一个特征(如红色圈)在上一层特征图上“感受”到 3x3 大小,该 3x3 大小的每个特征再映射回到图像上,就会发现由 7x7 个像素点与之关联,有所贡献。于是,就可以说第二层卷积后的特征其感受野大小是 7x7(需要自己画个图,好好数一数)。Way2 (下方右侧的图像)是另一种理解的方式,主要的区别仅仅是将两层特征图上的特征不进行“合成”,而是保留其在前一层因“步长”而产生的影响。

Way2 的理解方式其实更具有一般性,我们可以无需考虑输入图像的大小对感受野进行计算。如下图:

虽然,图上绘制了输入 9x9 的图像(蓝色),但是它的大小情况是无关紧要的,因为我们现在只关注某“无限”大小图像某一像素点为中心的一块区域进行卷积操作。首先,经过一个 3x3 的卷积层(padding=1,stride=2)后,可以得到特征输出(深绿色)部分。其中深绿色的特征分别表示卷积核扫过输入图像时,卷积核中心点所在的相对位置。此时,每个深绿色特征的感受野是 3x3 (浅绿)。这很好理解,每一个绿色特征值的贡献来源是其周围一个 3x3 面积。再叠加一个 3x3 的卷积层(padding=1,stride=2)后,输出得到 3x3 的特征输出(橙色)。此时的中心点的感受野所对应的是黄色区域 7x7,代表的是输入图像在中心点橙色特征所做的贡献。

这就是为何在 《VERY DEEP CONVOLUTIONAL NETWORKS FOR LARGE-SCALE IMAGE RECOGNITION》 文章中提到:

It is easy to see that a stack of two 3 × 3 conv. layers (without spatial pooling in between) has an effective receptive field of 5 × 5; three such layers have a 7 × 7 effective receptive field.

也就是说两层 3x3 的卷积层直接堆叠后(无池化)可以算的有感受野是 5x5,三层堆叠后的感受野就是 7x7。


感受野计算

直观的感受了感受野之后,究竟该如何定量计算嗯?只要依据 Way2 图像的理解,我们对每一层的特征“顺藤摸瓜”即可。

我们已经发觉到,某一层特征上的感受野大小依赖的要素有:每一层的卷积核大小 k,padding 大小 p,stride s。在推导某层的感受野时,还需要考虑到该层之前各层上特征的的感受野大小 r,以及各层相邻特征之间的距离 j(jump)。

所以对于某一卷积层(卷积核大小 k,padding 大小 p,stride s)上某特征的感受野大小公式为:

  • 第一行计算的是,相邻特征之间的距离(jump)。各层里的特征之间的距离显然是严重依赖于 stride ,并且逐层累积。值得注意的是,输入图像的作为起始像素特征,它的特征距离(jump) 为1。
  • 第二行计算的就是某层的特征的感受大小它依赖于上一层的特征的感受野大小 和特征之间的距离 ,以及该层的卷积核大小 k。输入图像的每个像素作为特征的感受野就是其自身,为1。
  • 第三行公式计算的是特征感受野的几何半径。对于处于特征图边缘处的特征来说,这类特征的感受野并不会完整的对应到原输入图像上的区域,都会小一些。初始特征的感受野几何半径为 0.5。
 下面,我们继续拿可视化时用的例子,看看具体是怎么计算和对应的:

上图中除了公式和说明部分外,有两行分别代表的是第一层卷积和第二层卷积。在每行中,应从左往右观察卷积核计算和操作。

第一层比较简单,最后输出 3x3 绿色的特征图,每个特征有阴影框大小来表示每个特征对应的感受野大小 3x3。其中   表示的 0.5 几何半径,我已经用红色标识出来,对应于阴影面积覆盖到的绿色面积的几何半径。

第二层,由于有一个单位的 padding,所以 3x3 卷积核是按照蓝色箭头标记作为的起始方向开始,在所有的绿色位置上挪动的。最后算得特征的感受野大小为 7x7,亦对应于阴影框和阴影区域部分。其中  是 0.5 也已经用红色标记了出来。


Python Script

这个代码其实很好写,我就直接挪用 Dang Ha The Hien 的 python 脚本了:

# [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 = raw_input ("Layer name where the feature in: ")
layer_idx = layer_names.index(layer_name)
idx_x = int(raw_input ("index of the feature in x dimension (from 0)"))
idx_y = int(raw_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))

在 AlexNet 网络上的效果如下:

关于感受野 (Receptive field) 你该知道的事的更多相关文章

  1. 十件你需要知道的事,关于openstack-trove(翻译)

    开源数据库即服务OpenStack Trove应该知道的10件事情 作者:Ken Rugg,Tesora首席执行官 Ken Rugg是Tesora的创始人,CEO和董事会成员. Ken的大部分职业都是 ...

  2. C# 范型约束 new() 你必须要知道的事

    C# 范型约束 new() 你必须要知道的事 注意:本文不会讲范型如何使用,关于范型的概念和范型约束的使用请移步谷歌. 本文要讲的是关于范型约束无参构造函数 new 的一些底层细节和注意事项.写这篇文 ...

  3. 神经网络中的感受野(Receptive Field)

    在机器视觉领域的深度神经网络中有一个概念叫做感受野,用来表示网络内部的不同位置的神经元对原图像的感受范围的大小.神经元之所以无法对原始图像的所有信息进行感知,是因为在这些网络结构中普遍使用卷积层和po ...

  4. 关于Unicode,字符集,字符编码,每个程序员都应该知道的事

    关于Unicode,字符集,字符编码,每个程序员都应该知道的事 作者:Jack47 李笑来的文章如何判断一个人是否聪明?中提到: 必要.清晰.且准确的概念,是一切思考的基石.所谓思考,很大程度上,就是 ...

  5. 学习IOS需要知道的事

    什么是iOS iOS是一款由苹果公司开发的操作系统(OS是Operating System的简称),就像平时在电脑上用的Windows XP.Windows 7,都是操作系统 那什么是操作系统呢?操作 ...

  6. 漫谈ElasticSearch关于ES性能调优几件必须知道的事

    lasticSearch是现在技术前沿的大数据引擎,常见的组合有ES+Logstash+Kibana作为一套成熟的日志系统,其中Logstash是ETL工具,Kibana是数据分析展示平台.ES让人惊 ...

  7. IL命令初学者要知道的事

    在一个中间语言程序中,如果某一行以“.”开始,代表这是一个传输给汇编工具的指令:而不是以“.”开始的行是中间语言的代码.上图中.method是方法定义指令,定义了Main方法,参数在“()”中,IL代 ...

  8. 重写equals方法需要知道的事

    重写equals方法 相信在每个人都有过重写过java的equals的方法的经历.这篇博文就从以下几个方面说明重写equals方法的原由,与君共进步. 一 为什么要重写equals方法 首先我们了解e ...

  9. 苹果强制使用HTTPS传输了怎么办?——关于HTTPS,APP开发者必须知道的事

    WeTest 导读 2017年1月1日起,苹果公司将强制使用HTTPS协议传输.本文通过对HTTPS基础原理和通信过程内容的讲解,介绍APP开发者在这个背景下的应对办法. 几周前,我们在<htt ...

随机推荐

  1. Sql case when 示例

    SELECT       单元编号,年,月,   项目编号='11111',   sum(case  when 项目编号= 'ZB010201' then [当月额] else 0 end ) 医疗收 ...

  2. maven项目自动创建src/main/resources等四个资源文件夹

    如何使maven项目自动创建这四个文件夹:src/main/resources.src/main/java.src/test/java.src/test/resources 网传甚广的在Config ...

  3. 关于Python的Mixin模式

    转自:http://www.bjhee.com/python-mixin.html 像C或C++这类语言都支持多重继承,一个子类可以有多个父类,这样的设计常被人诟病.因为继承应该是个”is-a”关系. ...

  4. TensorFlow的介绍和安装

    TensorFlow概要 由google Brain开源,设计初衷是加速机器学习的研究,2015年11月在GitHub上开源,2016年4月分布式版本,2017年发布了1.0版本,趋于稳定. Tens ...

  5. Java笔记Spring(五)

    C:\apache-tomcat-8.0.36\bin\catalina.bat run [2018-05-23 02:30:31,657] Artifact demo-springmvc:war e ...

  6. 微信小程序隐藏滚动条

    全局wxss中添加以下样式,可以隐藏所有的滚动条: 包括使用scroll-view组件或者使用overflow-y:scroll;而出现的滚动条: 无论竖向横向滚动条都可隐藏: ::-webkit-s ...

  7. when to use reinterpret_cast

    写的太好了.. When you convert for example int(12) to unsigned float (12.0f) your processor needs to invok ...

  8. VS 编译通过后 链接提示 无法使用的外部符号

    1. 检查是否已经链接了需要的.lib静态库,如果是自己定义的头文件,检查cpp文件是否添加到了VS工程里 2. 头文件尽量不要包含其他头文件,容易造成包含混乱,如头文件里使用了自定义的类名,最好只用 ...

  9. visual studio 2013 几个测试工具(Nunit 3、xUnit)

    一.Nunit 3 1.在解决方案里添加一个类库——引用——右键(如下图)) 3.搜索nunit 并安装(如图) 3.注意引入命名空间并给测试类和测试方法添加特性(如图) 4.如果测试通过则为绿色(如 ...

  10. 我与OO (1)

    前言 “真正的勇士敢于面对惨淡的人生,敢于面对淋漓的鲜血” 我是谨慎拜读了鲁迅先生的名言,怀着崇敬的精神去接触这门课程的. 而当我真的经历了这门课程以后,我才发现,刘和珍君这样的觉悟,我们普通人,果然 ...