『TensorFlow』卷积层、池化层详解 - 叠加态的猫 - 博客园两种网络层实现的数学细节。https://www.cnblogs.com/hellcat/p/7850048.html
一、CNN概述
CNN一个牛逼的地方就在于通过感受野和权值共享减少了神经网络需要训练的参数的个数,所谓权值共享就是同一个Feature Map中神经元权值共享,该Feature Map中的所有神经元使用同一组权值。因此:
a. 参数个数: 与神经元的个数无关,只与卷积核的大小及Feature Map的个数相关。
b. 共有多少个连接个数:就与神经元的个数相关了,神经元的个数也就是特征图的大小。
卷积神经网络依旧是层级网络, 但层的功能和形式做了变化。层级结构可参照下图
1.层级结构
其层级结构包括:数据输入层/ Input layer,卷积计算层 / CONV layer, ReLU 激励层 / ReLU layer,池化层 / Pooling layer,全连接层 / FC layer
(1)数据输入层/ Input layer
有3种常见的图像数据处理方式
去均值:把输入数据各个维度都中心化到0,也就是算出所有样本的平均值,再让所有样本减去这个均值
归一化:幅度归一化到同样的范围,比如把样本数据压缩到0-1
PCA/白化:用PCA降维,白化是对数据每个特征轴上的幅度归一化
CNN在图像上的处理往往只有去均值。
(2)卷积计算层/ CONV layer
它不再是全连接了,而是局部关联。每个神经元看做一个filter。通过对窗口(receptive field)做滑动操作,filter对局部数据计算
这里还有3个概念:
深度/depth,在这幅图中指的3,通常为图片的rgb3个颜色通道。
步长/stride:即窗口每次滑动多远
填充值/zero-padding:为了使滑动窗口正好滑动到边界,需要在周围填充0,padding等于几,就填充几圈
下面给一个卷积的具体例子:
这个例子的depth为2,因为只有两个filter,每个颜色通道的上都有一个3*3的滑动窗口,这个窗口里的值与filter里的w对应相乘,每个通道上都会得到一个值,把这3个值加起来就得出了Output Volume层值,Filter w0对输入输入卷积后得到output volume层的第一个矩阵,filter w2得到第二个。
另外,卷积层有一个特别重要的特点就是参数共享机制,即每个神经元连接数据窗的权重是固定的,可以这样理解参数共享机制:
固定每个神经元连接权重,可以看做模板,每个神经元只关注一个特性。
它带来的好处就是需要估算的权重个数减少,例如AlexNet网络从1亿个需要调节的参数减少到3.5w个。
(3)激励层 (ReLU)
把卷积层输出结果做非线性映射
常见的激励函数有:Sigmoid,Tanh(双曲正切),ReLU,Leaky ReLU,ELU,Maxout
Sigmoid:最开始使用的,现在已经基本不用了,因为当x比较大时,它的输出值都比较接近于1,它的梯度是接近于0,而我们是要要利用梯度取做优化的,这将导致无法完成权重的优化
ReLU:比价常用的激励函数,它有收敛快,求梯度简单,较脆弱这些特点,较脆弱的原因是,当x的值小于0后,它任然会出现梯度为0 的结果。
Leaky ReLU
不会“饱和”/挂掉,计算也很快
指数线性单元ELU:所有ReLU有的优点都有,不会挂,输出均值趋于0,因为指数存在,计算量略大
Maxout:计算是线性的,不会饱和不会挂,多了好些参数,两条直线拼接
实际经验:
1)不要用sigmoid
2)首先试RELU,因为快,但要小心点
3)如果2失效,请用Leaky ReLU或者Maxout
4)某些情况下tanh倒是有不错的结果,但是很少
(4)池化层 / Pooling layer
它的位置一般是夹在连续的卷积层中间,作用是压缩数据和参数的量,减小过拟合
(5)全连接层 / FC layer
两层之间所有神经元都有权重连接,通常全连接层在卷积神经网络尾部。业界人解释放一个FC layer的主要目的是最大可能的利用现在经过窗口滑动和池化后保留下的少量的信息还原原来的输入信息
(6)CNN的一般结构可归结为:
1)INPUT 2) [[CONV -> RELU]*N -> POOL?]*M 3) [FC -> RELU]*K 或FC
2.典型的CNN
LeNet,这是最早用于数字识别的CNN;AlexNet,2012 ILSVRC比赛远超第2名的CNN,比LeNet更深,用多层小卷积层叠加替换单大卷积层;ZF Net,2013 ILSVRC比赛冠军;GoogLeNet,2014 ILSVRC比赛冠军;VGGNet,2014 ILSVRC比赛中的模型,图像识别略差于GoogLeNet,但是在很多图像转化学习问题(比如object detection)上效果奇好
3.fine-tuning
(1)何谓 fine-tuning:使用已用于其他目标,预训练好模型的权重或者部分权重,作为初始值开始训练
(2)为什么要fine-tuning:首先自己从头训练卷积神经网络容易出现问题,其次fine-tuning能很快收敛到一个较理想的状态
(3)怎么做:一般复用相同层的权重,新定义层取随机权重初始值,但要注意调大新定义层的的学习率,调小复用层学习率
三、CNN的常用框架
1.Caffe:源于Berkeley的主流CV工具包,支持C++,python,matlab,Model Zoo中有大量预训练好的模型供使用
2.TensorFlow:Google的深度学习框架,TensorBoard可视化很方便,数据和模型并行化好,速度快
3.Torch:Facebook用的卷积神经网络工具包,通过时域卷积的本地接口,使用非常直观,定义新网络层简单.
四、典型应用
1.图像识别与检索
2.人脸识别
3.性别/年龄/情绪识别
4.物体检测
五、CNN训练注意事项
1.用Mini-batch SGD对神经网络做训练的过程如下:
不断循环 :
① 采样一个 batch 数据( ( 比如 32 张 )
②前向计算得到损失 loss
③ 反向传播计算梯度( 一个 batch)
④ 用这部分梯度迭代更新权重参数
2.去均值
去均值一般有两种方式:第一种是在每个像素点都算出3个颜色通道上的平均值,然后对应减去,如AlexNet。 第二种是在整个样本上就只得到一组数,不分像素点了,如VGGNet。
3.权重初始化
1)用均值为0的高斯函数,随机取一些点去初始化W。这种初始化方法对于层次不深的神经网络 OK,深层网络容易带来整个网络( 激活传递)的不对称性
2)当激励函数是sigmoid函数时,输入层神经元个数为input,输出层为output,则输出层的W可为input和output之间的一个数/input的平方根,公式如下
3)当激励函数变成当今最为流行ReLu函数时,以上方式又失效了,除以(input/2)的平方根是可以的
关于Batch Normalization:通常在全连接层后 , 激励层前做如下操作
它的作用是自动的约束输出不会发散,导致导致整个网络的训练死掉,具体的官方的好处有如下四点:
1)梯度传递(计算 )更为顺畅
2)学习率设高一点也没关系
3)对于初始值的依赖减少了!!
4)其实这里也可以看做一种正则化 , 减少了对dropout的需求。
4.Dropout
这是一种防止过拟合的一种正则化方式,以前的正则化方式是在loss中加上所有的W,但在神经网络中不可行,因为w的数量太大了,不仅会使loss值很大,也会浪费很多时间在计算w的和上。简单的理解dropout就是别一次开启所有学习单元
一个最简单的实现方式可参照如下代码所示:
dropout一般在训练阶段使用,在测试或者预测时并不会去dropout,工业上的做法是在输入的X上乘以P得到X的期望,或者输入不做变化而是对所有的有dropout层都做X/p
dropout能防止过拟合的的理解方式:
理解一:别让你的神经网络记住那么多东西
理解二:每次都关掉一部分感知器 , 得到一个新模型 , 最后做融合
二、padding的理解
在用tensorflow写CNN的时候,调用卷积核api的时候,会有填padding方式的参数,找到源码中的函数定义如下(max pooling也是一样):
def conv2d(input, filter, strides, padding, use_cudnn_on_gpu=None, data_format=None, name=None)
源码中对于padding参数的说明如下:
padding: A string
from: "SAME", "VALID"
.
The type of padding algorithm to use.
说了padding可以用“SAME”和“VALID”两种方式,但是对于这两种方式具体是什么并没有多加说明。
这里用Stack Overflow中的一份代码来简单说明一下,代码如下:
x = tf.constant([[1., 2., 3.],
[4., 5., 6.]])
x = tf.reshape(x, [1, 2, 3, 1]) # give a shape accepted by tf.nn.max_pool
valid_pad = tf.nn.max_pool(x, [1, 2, 2, 1], [1, 2, 2, 1], padding='VALID')
same_pad = tf.nn.max_pool(x, [1, 2, 2, 1], [1, 2, 2, 1], padding='SAME')
print(valid_pad.get_shape())
print(same_pad.get_shape())
最后输出的结果为:
(1, 1, 1, 1)
(1, 1, 2, 1)
可以看出“SAME”的填充方式是比“VALID”的填充方式多了一列。
让我们来看看变量x是一个2x3的矩阵,max pooling窗口为2x2,两个维度的strides=2。
第一次由于窗口可以覆盖(橙色区域做max pool操作),没什么问题,如下:
接下来就是“SAME”和“VALID”的区别所在,由于步长为2,当向右滑动两步之后“VALID”发现余下的窗口不到2x2所以就把第三列直接去了,而“SAME”并不会把多出的一列丢弃,但是只有一列了不够2x2怎么办?填充!
如上图所示,“SAME”会增加第四列以保证可以达到2x2,但为了不影响原来的图像像素信息,一般以0来填充。(这里使用表格的形式展示,markdown不太好控制格式,明白意思就行),这就不难理解不同的padding方式输出的形状会有所不同了。
在CNN用在文本中时,一般卷积层设置卷积核的大小为n×k,其中k为输入向量的维度(即[n,k,input_channel_num,output_channel_num]),这时候我们就需要选择“VALID”填充方式,这时候窗口仅仅是沿着一个维度扫描而不是两个维度。可以理解为统计语言模型当中的N-gram。
我们设计网络结构时需要设置输入输出的shape,源码nn_ops.py中的convolution函数和pool函数给出的计算公式如下:
If padding == "SAME":
output_spatial_shape[i] = ceil(input_spatial_shape[i] / strides[i])
If padding == "VALID":
output_spatial_shape[i] =
ceil((input_spatial_shape[i] -
(spatial_filter_shape[i]-1) * dilation_rate[i])
/ strides[i]).
dilation_rate为一个可选的参数,默认为1,这里我们可以先不管它。
三、各层参数和链接数计算
举例:
下面以最经典的LeNet-5例子来逐层分析各层的参数及连接个数。
C1层(卷积层):是一个卷积层,由6个特征图Feature Map构成。特征图中每个神经元与5*5的邻域(滤波器)相连。
1. 特征图大小:特征图的大小28*28,这样能防止输入的连接掉到边界之外(32-5+1=28)。
feature map边长大小的具体计算参见:http://blog.csdn.net/qq_15192373/article/details/78393520
2. 参数个数:C1有156个可训练参数 (每个滤波器5*5=25个unit参数和一个bias参数,一共6个滤波器,共(5*5+1)*6=156个参数)
3. 链接个数/FLOPS个数::(5*5+1)*6 *(28*28)=122,304个。左边是滤波器在输入层滑过的神经元个数,右边是C1层每个feature map的神经元个数,左右对应关系是1对28*28,相乘即为连接数。(每个链接对应1次计算,由wa+b可知,每个参数参与1次计算,所以1个单位的偏置b也算进去)
S2层(下采样层):是一个下采样层,有6个14*14的特征图。特征图中的每个单元与C1中相对应特征图的2*2邻域相连接。
1. 特征图大小:每个单元的2*2感受野并不重叠,因此S2中每个特征图的大小是C1中特征图大小的1/4(行和列各1/2)
2. 参数个数:S2层有 12个 (6*(1+1)=12) 可训练参数。S2层 每个滤波器路过的4个邻域 的4个输入相加,乘以1个可训练参数w,再加上1个可训练偏置b(即一个滤波器对应两个参数)。(对于子采样层,每一个特征映射图的的可变参数需要考虑你使用的采样方式而定,如文中的采样方式,每一个特征映射图的可变参数数量为2个,有的采样方式不需要参数)
3. 链接个数/FLOPS个数:5880个连接,( (2*2+1)*6 *14*14=5880) 。左边是滤波器在C1层滑过的神经元个数,右边是S2层每个feature map的神经元个数,相乘即为连接数。
C3层(卷积层):是一个卷积层,它同样通过5x5的卷积核去卷积层S2,然后得到的特征map就只有10x10个神经元,但是它有16种不同的卷积核,所以就存在16个特征map了。
1. 特征图大小:C3中每个特征图由S2中所有6个或者几个特征map组合(几个滤波器计算的神经元的值 相加 合并成一个神经元)而成。
为什么不把S2中的每个特征图连接到每个C3的特征图呢?原因有2点。
第1,不完全的连接机制将连接的数量保持在合理的范围内。
第2,也是最重要的,其破坏了网络的对称性。由于不同的特征图有不同的输入,所以迫使他们抽取不同的特征(希望是互补的)。(14-5+1=10)
2. 参数个数:例如,存在的一个方式是:
C3的前6个特征图:相当于需要6组滤波器,每组以S2中 3个相邻 特征图子集 为输入,共享一个偏置。(C3每个特征图 由 S2中3个特征图分别用不同滤波器 再加和得到)
C3的接下来6个特征图:相当于需要6组滤波器,每组以S2中 4个相邻 特征图子集 为输入,共享一个偏置。(1对4)
C3的接下来3个特征图:相当于需要3组滤波器,每组以S2中 4个不相邻 特征图子集 为输入,共享一个偏置。(1对4)
C3的最后1个特征图:相当于需要1组滤波器,每组将S2中所有 特征图 为输入,共享一个偏置。(1对6)
这样C3层有1516个可训练参数。计算:6*(3*25+1)+6*(4*25+1)+3*(4*25+1)+(25*6+1)=1516。此处,C3个特征图由 S2中n个卷积结果合并,然后共享1个b,组合计算得到。
3. 链接个数/FLOPS个数:1516* 10*10=151600个连接。左边是滤波器滑过的S2层神经元个数,右边是C3层特征图大小。
S4层(下采样层):是一个下采样层,由16个5*5大小的特征图构成。特征图中的每个单元与C3中相应特征图的2*2邻域相连接,跟C1和S2之间的连接一样。
1. 特征图大小:5*5大小。每个单元的2*2感受野并不重叠,因此S2中每个特征图的大小是C1中特征图大小的1/4(行和列各1/2)
2. 参数个数:S4层有32个可训练参数。(每个特征图1个因子w和1个偏置b,16*(1+1)=32)
3. 链接个数/FLOPS个数:16* (2*2+1) *5*5=2000个连接。左边是滤波器在C3层滑过的神经元个数,右边是S4层神经元个数,相乘即为连接数。
C5层(卷积层或第一个全连接层):是一个卷积层,有120个特征图(或者说神经元)。每个单元与S4层的全部16个单元的5*5邻域(与S4层特征图一样大)相连。(120组滤波器, 每组16个滤波器,分别与 S4层16个特征图 进行卷积, 每组得到C5层的一个神经元/特征图)
1. 特征图大小:由于S4层特征图的大小也为5*5(同滤波器一样),故C5特征图的大小为1*1。(5-5+1=1), 这构成了S4和C5之间的全连接。之所以仍将C5标示为卷积层而非全相联层,是因为如果LeNet-5的输入变大,而其他的保持不变,那么此时特征图的维数就会比1*1大。
2. 参数个数:120* (16*5*5+1)=48120个。滤波器个数120*16个,所以w有120*16*5*5个,同组16个滤波器共用一个b,所以有120个b。
3. 链接个数/FLOPS个数:48120*1*1, 左边是滤波器滑过的神经元个数,右边是C5层特征图大小(其实现在已经变成了单个神经元,大小1*1),相乘即为连接数,此处也即FLOPS个数。
F6层(全连接层):虽然是全连接层,得到F6层每个神经元 由 每组120个1*1的滤波器对C5层卷积,一共84组滤波器,得到84个神经元。
1. 特征图大小:有84个单元(之所以选这个数字的原因来自于输出层的设计),与C5层全相连。
2. 参数个数:有 84* (120*(1*1)+1)=10164 个可训练参数。如同经典神经网络,F6层计算输入向量(120)和权重向量(1*1)之间的点积,再加上一个偏置(+1)。然后将其传递给sigmoid函数产生单元i的一个状态。
3. 链接个数/FLOPS个数:10164* 1*1,左边是滤波器在C5层滑过的神经元个数,右边是F6层特征图大小。1个链接对应1次计算。
输出层:由欧式径向基函数(Euclidean Radial Basis Function)单元组成,每类一个单元,每个有84个输入。
相关参考:
http://blog.csdn.net/dcxhun3/article/details/46878999
TensorFlow中CNN的两种padding方式“SAME”和“VALID”_wuzqchom的博客-CSDN博客_padding same
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)