之前有段时间偏向于工程实现,用CNN做classification,Detection,Segmentation都已经玩过,且在项目上有过应用,过程中也看了很多理论知识,但较浅。现在有时间再回头过下理论知识吧。
一.感受野的计算
点击打开链接该博客中做了完备数学公式的推导,在此引用一下:
假设第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
分析可有如下逻辑:
假设目前有一张原始图像(224*224*1),中间会经过两次卷积(conv1,conv2),那么conv2后的数据(即第3层)对输入层的的感受野可推导如下(假设conv1的kernel size为3*3, stride为1;conv2的kernel size为3*3,stride 为1):
先计算conv2(第2层)对conv1(第1层)的感受野:
则上面公式中i=2,j=2;
F(2,1) = conv2的kernel_size + (F(2,2)-1)*(conv2的stride)
所以F(2,1)= 3 + (1-1)*1 = 3
接着求F(2,0),即conv2对输入图像的感受野,则上面公式中i=2,j=1,
F(2,0) = conv的kernel_size + (F(2,1)- 1) * (conv1的stride)
即F(2,0) = 3 + (3 -1)*2 = 7
所以conv3对输入图像的感受野为7个像素。
博客点击打开链接中也对感受野做了介绍,并推出了主流网络vgg16和Alexnet各层的感受野,下面我也带着推导部分层来手动验证下,做这个工作需要对vgg16和Alexnet的网络结构了解(对各层的卷积和池化的kernel size以及stride了解)
使用博客点击打开链接中的vgg16模型图:
博客点击打开链接对Alexnet的各层结构做了详细介绍。
来验证博客点击打开链接中的推导值(同时含有vgg16及Alexnet):
首先来验证Alexnet的pool2层对原始图像的感受野计算。pool2是第四层。
则F(4,3)= 第四层的kernel_size + (F(4,4)-1)*第四层的stride。了解到Alexnet的结构,这里kernel_size=3.
所以F(4,3) = 3;
F(4,2) = 第三层的kernrl_size + (F(4,3) - 1)*第三层的stride
所以F(4,2) = 5 + (3 - 1)* 1 = 7
F(4,1) = 第二层的kernel_size + (F(4,2) - 1)*第二层的stride
所以F(4,1) = 3+ (7 - 1)* 2 = 15
F(4,0) = 第一层的kernel_size + (F(4,1) - 1)*第一层的stride
所以F(4, 0) = 11 + (15 - 1)* 4 = 67
Alexnet验证正确
接下来看Vgg16的conv1_2对输入层的感受野求解,conv1_2为第二层
F(2,1) = 第二层的kernel_size + (F(2,2) -1)*第二层的stride
F(2,1) = 3
F(2,0) = 第一层的kernel_size + (F(2,1) - 1)* 第一层的stride
F(2,0) = 3 + (3 - 1)*2 = 5。
Vgg16验证正确
二.各层的输出和输入图像宽度和高度大小的关系
outsize = (insize - fsize + 2*pad)/stride + 1,前者会向下求整
这里outsizes是输出图像的宽度和高度大小,insize是输入图像的宽度和高度大小,fsize表示该层的卷积核大小,stride表示卷积核的移动步长。
补充说明:
Tensorflow实战Google深度学习框架书里提到
全0填充时:
out = in/stride
这里out指被滤波器卷积后的图像大小,in指滤波前的图像大小,stride指步长 ,向上取整
不使用全0填充时:
out = (in - filter + 1)/stride
这里out指被滤波器卷积后的图像大小,in指滤波前的图像大小,stride指步长, filter指滤波器的大小,向上取整
首先来验证Alexnet的pool2的输出图像的outsize.(不按照书上)
conv1_outsize = (224 - 11 + 2*0)/4 + 1 -> 54
pool1_outsize = (54 - 3 + 2*0)/2 + 1 -> 26
conv2_outsize = (26 - 5 + 2*2)/1 + 1 -> 26
pool2_outsize = (26 - 3 + 2*0)/2 + 1 -> 12
验证正确
按照书上来的话,如果是全0填充,则:
conv1_outsize = 224/4 = 56
pool1_outsize = 56/2 = 28
可知应该不是全0填充的,就不继续推导了,假设不全使用0填充,此时
conv1_outsize = (224 - 11 + 1)/4 = 53.5 ~~54
pool1_outsize = (54 - 3 + 1) / 2 = 26
conv2_outsize = (26 - 5 + 1) / 1 = 22
和上面的不对应,改为使用全0填
conv2_outsize = 26 / 1 = 26
pool2_outsize = (26 - 3 + 1)/2 = 12 (不全0填)
conv3_outsize = (12 - 3 + 1)/1 = 10
conv3_outsize = 12 / 1= 12(全0填)
这边验证出:pad标记为0, 为不使用全0填充
pad标记为1,2等,为使用前全0填
看vgg16的conv2_1的输出size
conv1_1 = (224 - 3 + 2*1)/1 + 1 ->224
conv1_2 = (224 - 3 + 2*1)/1 + 1 ->224
pool1 = (224 - 2 + 2*0)/2 + 1 -> 112
conv2_1 = (112 - 3 + 2*1) + 1 -> 112
验证正确!
按照书上的推导:
conv1_1 = 224/1 = 224
conv1_2 = 224/1 = 224
pool1 = (224 - 2 + 1)/2 = 111.5 ~~112
这边验证出:pad标记为0, 为不使用全0填充
pad标记为1,2等,为使用前全0填
最后验证下zf-5网络
conv1 = (224 - 7 + 2*3)/2 = 111.5 ~~ 112
pool1 = (112 - 3 + 2*1)/2 = 55.5 ~~56
如果按照书上的推导:
全0填充:
conv1 = 224 / 2 = 112
不使用全0填充:
pool1 = (224 - 7 + 1)/2 = 59
所以这里也验证出:
这边验证出:pad标记为0, 为不使用全0填充
pad标记为1,2等,为使用前全0填