卷积神经网络CNN入门
本文主要是记录一下对卷积神经网络
(CNN
)的学习,这是在机器学习,具体来说是深度学习中的第一步,认真学习、好好记录。
CNN曾经被AlphaGo
的开发团队用于其开发,从而也一度引起业内人士的青睐,不过有些时候会出现对CNN的盲目崇拜,比如你面试的时候,对面可能会问:你做过机器学习吗?你为什么没有采用CNN算法呢?
这可能让人无法回答,其实CNN虽然相对于深度神经网络
(DNN
)更加简便,效能也很好,不过CNN只是用于图像处理,或者具有与图像处理要素相当的一些问题的处理中,就比如AlphaGo
的下围棋,其实就相当于把棋盘当做了图像来进行处理,因此,如果你需要利用神经网络实现某些项目,一定要考虑好它具备与图像处理相当的要素没有。
这里提供一个比较好的学习视频,是李宏毅老师的教学视频。好了,下面进入学习记录了。
卷积神经网络概述
卷积神经网络,是深度学习算法应用最成功的领域之一,卷积神经网络包括一维卷积神经网络,二维卷积神经网络以及三维卷积神经网络。一维卷积神经网络主要用于序列类
的数据处理,二维卷积神经网络常应用于图像类文本
的识别,三维卷积神经网络主要应用于医学图像
以及视频类
数据识别。
其结构模型如下:
与常规神经网络不同,卷积神经网络的各层中的神经元是3维
排列的:宽度
、高度
和深度
。其中的宽度和高度是很好理解的,因为本身卷积就是一个二维模板,但是在卷积神经网络中的深度指的是激活数据体的第三个维度,而不是整个网络的深度,整个网络的深度指的是网络的层数。
卷积神经网络层次
卷积神经网络主要由这几类层构成:输入层
、卷积层
,ReLU层
、池化(Pooling)层
(有时也称降采样、下采样层)和全连接层
(全连接层和常规神经网络中的一样)。通过将这些层叠加起来,就可以构建一个完整的卷积神经网络,如下图:
各层的主要作用如下:
- 输入层:用于数据的输入;
- 卷积层:使用卷积核进行特征提取和特征映射;
- 池化层:进行下采样,对特征图稀疏处理,减少数据运算量;
- 激励层:由于卷积也是一种线性运算,因此需要增加非线性映射;
- 全连接层:通常在CNN的尾部进行重新拟合,减少特征信息的损失。
输入层
该层除了输入数据,还会对数据进行一些预处理,包括:去均值
、归一化
、PCA
、白化
PCA:是指通过抛弃携带信息量较少的维度,保留主要的特征信J自,来对数据进行降维处理,思路上是使用少数几个有代表性、互不相关的特征来代替原先的..k量的、存在一定相关性的特征,从而加速机器学习进程。PCA可用于特征提取,数据压缩,去噪声,降维等操作。
白化:目的是去掉数据之间的相关联度和令方差均一化,由于图像中相邻像素之间具有很强的相关性,所以用于训练时很多输入是冗余的。这时候去相关的操作就可以采用自化操作,从而使得:
- 1、减少特征之间的相关性
- 2、特征具有相同的方差
卷积层
卷积层是构建卷积神经网络的核心层,因此该层也是学习的重点。它产生了网络中大部分的计算量,注意是计算量而不是参数量。
卷积层的作用
主要有三点:
滤波器的作用或者说是卷积的作用
卷积层的参数是有一些可学习的
滤波器
(Filter
)集合构成的。每个滤波器在空间上(宽度和高度)都比较小,但是深度和输入数据一致。
直观地来说,网络会让滤波器学习到当它看到某些类型的视觉特征时就激活,具体的视觉特征可能是某些方位上的边界,或者在第一层上某些颜色的斑点,甚至可以是网络更高层上的蜂巢状、车轮状的图案。可以看做神经元的一个输出
神经元只观察输入数据中的一小部分,并且和空间上左右两边的所有神经元共享参数(因为这些数字都是使用同一个滤波器得到的结果)。
减少参数数量
这个由于卷积具有“
权值共享
”这样的特性,可以降低参数数量,达到降低计算开销,防止由于参数过多而造成过拟合。
感受野
在处理图像这样的高维度输入时,让每个神经元都与前一层中的所有神经元进行全连接是不现实的。而是让每个神经元只与输入数据的一个局部区域连接,该连接的空间大小叫做神经元的感受野(receptive field)
,它的尺寸是一个超参数(滤波器的空间尺寸)。注意:在深度方向上,这个连接的大小总是和输入的深度相等。
神经元的空间排列
感受野讲解了卷积层中每个神经元与输入数据体之间的连接方式,但是尚未讨论输出数据体中神经元的数量,以及它们的排列方式。3个超参数控制着输出数据体的尺寸:深度
(depth
),步长
(stride
)和零填充
(zero-padding
):
- 深度:一个超参数,和使用的滤波器的数量一致,而每个滤波器在输入数据中寻找一些不同的东西,即图像的某些特征。
- 步长:在滑动滤波器的时候,必须指定步长。当步长为1,滤波器每次移动1个像素;当步长为2,滤波器滑动时每次移动2个像素,依次类推。这个操作会让输出数据体在空间上变小。
- 零填充:有时候将输入数据体用
0
在边缘处进行填充是很方便的。零填充的尺寸是一个超参数。它具有一个良好性质,即可以控制输出数据体的空间尺寸(最常用的是用来保持输入数据体在空间上的尺寸,使得输入和输出的宽高都相等)。
输出数据体在空间上的尺寸 W2*H2*D2
可以通过输入数据体尺寸W1*H1*D1
、卷积层中神经元的感受野尺寸F
、步长S
、滤波器数量K
和零填充的数量P
计算输出出来:
注意这些空间排列的超参数之间是相互限制的。举例说来,当输入尺寸W=10
,不使用零填充P=0
,滤波器尺寸 F=3
,此时步长S=2
是行不通,因为(W-F+2P)/S+1=(10-3+0)/2+1=4.5
,结果不是整数,这就是说神经元不能整齐对称地滑过输入数据体。
因此,这些超参数的设定就被认为是无效的,一个卷积神经网络库可能会报出一个错误,通过修改零填充值、修改输入数据体尺寸,或者其他什么措施来让设置合理。合理地设置网络的尺寸让所有的维度都能正常工作,是相当让人头痛的事,而使用零填充和遵守其他一些设计策略将会有效解决这个问题。
权值共享
在卷积层中权值共享
是用来控制参数的数量。假如在一个卷积核中,每一个感受野采用的都是不同的权重值(卷积核的值不同),那么这样的网络中参数数量将是十分巨大的。
权值共享是基于这样的一个合理的假设:如果一个特征在计算某个空间位置(x1,y1)(x1,y1)
的时候有用,那么它在计算另一个不同位置(x2,y2)(x2,y2)
的时候也有用。基于这个假设,可以显著地减少参数数量。
换言之,就是将深度维度上一个单独的2维切片看做深度切片
(depth slice
),比如一个数据体尺寸为[55x55x96]
的就有96个深度切片,每个尺寸为[55x55]
,其中在每个深度切片上的结果都使用同样的权重和偏差获得的。
在这样的参数共享下,假如一个例子中的第一个卷积层有96个卷积核,那么就有96个不同的权重集了,一个权重集对应一个深度切片,如果卷积核的大小是11x11
的,图像是RGB3通道的,那么就共有96x11x11x3=34,848
个不同的权重,总共有34,944
个参数(因为要+96个偏差),并且在每个深度切片中的55x55
的结果使用的都是同样的参数。
在反向传播的时候,都要计算每个神经元对它的权重的梯度,但是需要把同一个深度切片上的所有神经元对权重的梯度累加,这样就得到了对共享权重的梯度。这样,每个切片只更新一个权重集。原理如下图:
如上两图所示,左侧的神经元是将每一个感受野展开为一列之后串联起来(就是展开排成一列,同一层神经元之间不连接)。右侧的Deep1i
是深度为1
的神经元的第i
个,Deep2i
是深度为2
的神经元的第i
个,同一个深度的神经元的权值都是相同的,黄色的都是相同的(上面4个与下面4个的参数相同),蓝色也都是相同的。
所以现在回过头来看上面说的卷积神经网络的反向传播公式对梯度进行累加求和也是基于这点考虑(同一深度的不同神经元共用一组参数,所以累加);而每个切片只更新一个权重集的原因也是这样的,因为从图中可以看到,不同深度的神经元不会公用相同的权重,所以只能更新一个权重集。
注意:如果在一个深度切片中的所有权重都使用同一个权重向量,那么卷积层的前向传播在每个深度切片中可以看做是在计算神经元权重和输入数据体的卷积(这就是“
卷积层
”名字由来)。这也是为什么总是将这些权重集合称为滤波器(或卷积核
(kernel
)),因为它们和输入进行了卷积。
注意:有时候参数共享假设可能没有意义,特别是当卷积神经网络的输入图像是一些明确的中心结构时候。这时候我们就应该期望在图片的不同位置学习到完全不同的特征(而一个卷积核滑动地与图像做卷积都是在学习相同的特征)。一个具体的例子就是输入图像是人脸,人脸一般都处于图片中心,而我们期望在不同的位置学习到不同的特征,比如眼睛特征或者头发特征可能(也应该)会在图片的不同位置被学习。在这个例子中,通常就放松参数共享的限制,将层称为局部连接层(
Locally-Connected Layer
)。
卷积层的超参数及选择
由于参数共享,每个滤波器包含F*F*D1
个权重,卷积层一共有F*F*D1*K
个权重和K
个偏移。在输出数据体中,第d
个深度切片(空间尺寸是W2*H2
),用第d
个滤波器和输入数据进行有效卷积运算的结果(使用步长S
),最后在加上第d
个偏差。
对这些超参数,常见的设置是F=3
,S=1
,P=1
,F=3
,S=1
,P=1
。
卷积层演示
因为3D数据难以可视化,所以所有的数据(输入数据体是蓝色
,权重数据体是红色
,输出数据体是绿色
)都采取将深度切片按照列的方式排列展现。输入数据体的尺寸是W1=5
,H1=5
,D1=3
,W1=5
,H1=5
,D1=3
,卷积层参数K=2
,F=3
,S=2
,P=1
,K=2
,F=3
,S=2
,P=1
。就是说,有2
个滤波器,滤波器的尺寸是3*33*3
,它们的步长是2
。因此,输出数据体的空间尺寸是(5−3+2)/2+1=3(5−3+2)/2+1=3
。注意输入数据体使用了零填充P=1
,所以输入数据体外边缘一圈都是0
。
下面的例子在绿色的输出激活数据上循环演示,展示了其中每个元素都是先通过蓝色的输入数据和红色的滤波器逐元素相乘,然后求其总和,最后加上偏差得来。
卷积操作形式
卷积操作的形式比较多,这里主要介绍三点:矩阵乘法实现卷积
、1*1卷积
、扩张卷积
。
- 用矩阵乘法实现卷积
卷积运算本质上就是在滤波器和输入数据的局部区域间做点积。卷积层的常用实现方式就是利用这一点,将卷积层的前向传播变成一个巨大的矩阵乘法。
(1) 输入图像的局部区域被
im2coim2col
操作拉伸为列。比如输入是[227x227x3]
,要与尺寸为11x11x3
的滤波器以步长为4
进行卷积,就依次取输入中的[11x11x3]
数据块,然后将其拉伸为长度为11x11x3=363
的列向量。重复进行这一过程,因为步长为4
,所以经过卷积后的宽和高均为(227-11)/4+1=55
,共有55x55=3,025
个神经元。因为每一个神经元实际上都是对应有363
的列向量构成的感受野,即一共要从输入上取出3025
个363
维的列向量。所以经过im2col
操作得到的输出矩阵的尺寸是[363x3025]
,其中每列是拉伸的感受野。注意因为感受野之间有重叠,所以输入数据体中的数字在不同的列中可能有重复。(2) 卷积层的权重也同样被拉伸成行。举例:如果有96个尺寸为
[11x11x3]
的滤波器,就生成一个矩阵,尺寸为[96x363]
。(3) 现在卷积的结果和进行一个大矩阵乘法
np.dot(Wrow,Xcol)np.dot(Wrow,Xcol)
是等价的了,能得到每个滤波器和每个感受野间的点积。在我们的例子中,这个操作的输出是[96x3025]
,给出了每个滤波器在每个位置的点积输出。注意其中的np.dotnp.dot
计算的是矩阵乘法而不是点积。(4) 结果最后必须被重新变为合理的输出尺寸
[55x55x96]
。
这个方法的缺点就是占用内存太多,因为在输入数据体中的某些值在XcolXcol
中被复制了多次;优点在于矩阵乘法有非常多的高效底层实现方式。
- 1*1卷积
具有信号处理专业知识的人刚开始看见这个1*1卷积
的时候可能会比较困惑,因为信号是2
维的,所以1*1卷积
就没有意义。但是,在卷积神经网络中不是这样,因为这里是对3
个维度进行操作,滤波器和输入数据体的深度是一样的。比如,如果输入是[32x32x3]
,那么1*1卷积
就是在高效地进行3
维点积
(因为输入深度是3个通道);另外的一种想法是将这种卷积的结果看作是全连接层的一种实现方式,后面讲到全连接层会提到。
- 扩张卷积
我们前面提到的滤波器都是连续的,但是,让滤波器中元素之间有间隙也是可以的,这就叫做扩张,如图:
在某些设置中,扩张卷积与正常卷积结合起来非常有用,因为这可以在很少的层数内更快地汇集输入图片的大尺度特征。比如,如果上下重叠2
个3*3
的卷积层,那么第二个卷积层的神经元的感受野是输入数据体中5*5
的区域(可以称这些神经元的有效感受野是5*5)。如果我们对卷积进行扩张,那么这个有效感受野就会迅速增长。
至此,对卷积层的学习告一段落,下一层是池化层。
ReLU层
也称线性整流层(Rectified Linear Units layer, ReLU layer
),使用线性整流函数(Rectified Linear Units, ReLU
)f(x)=max(0,x)
作为这一层神经的激励函数(Activation function
)。它可以增强判定函数和整个神经网络的非线性特性,而本身并不会改变卷积层。
事实上,其他的一些函数也可以用于增强网络的非线性特性,如双曲正切函数 f(x)=tanh(x)
、f(x)=|tanh(x)|
,或者Sigmoid函数f(x)=(1+e^(-x))^(-1)
。相比其它函数来说,ReLU函数更受青睐,这是因为它可以将神经网络的训练速度提升数倍,而并不会对模型的泛化准确度造成显著影响。
池化层
通常在连续的卷积层之间会周期性地插入一个池化层
(Pooling
),它的作用是逐渐降低数据体的空间尺寸,这样的话就能减少网络中参数的数量,使得计算资源耗费变少,也能有效控制过拟合。如下图:
池化层使用MAX
操作,对输入数据体的每一个深度切片独立进行操作,改变它的空间尺寸。最常见的形式是使用尺寸2*2
的滤波器,以步长为2
来对每个深度切片进行降采样,将其中75%
的激活信息都丢掉。每个MAX
操作是从4
个数字中取最大值(也就是在深度切片中某个2*2
的区域),深度保持不变。
池化层的计算:输入数据体尺寸W1*H1*D1
,有两个超参数:空间大小F
和步长S
;输出数据体的尺寸W2*H2*D2
,其中:
这里面与之前的卷积的尺寸计算的区别主要在于两点:首先在池化的过程中基本不会进行另补充;其次池化前后深度不变。
普通池化(General Pooling):除了常用的最大池化,池化单元还可以使用其他的函数,比如平均池化
(average pooling
)或L-2范式池化
(L2-norm pooling
)。平均池化历史上比较常用,但是现在已经很少使用了。因为实践证明,最大池化的效果比平均池化要好。
反向传播:回顾一下反向传播的内容,其中max(x,y)
函数的反向传播可以简单理解为将梯度只沿最大的数回传。因此,在向前传播经过汇聚层的时候,通常会把池中最大元素的索引记录下来(有时这个也叫作道岔
(switches
)),这样在反向传播的时候梯度的路由就很高效。
不使用池化层:有些时候,被当做图像处理的问题其实也并不完全等同于图像处理,比如AlphaGo的下围棋:棋盘是不能摘除一部分位置进行缩小的,因此也就不能进行池化,事实上AlphaGo也并没有采用池化层。
通过在卷积层中使用更大的步长来降低数据体的尺寸。有发现认为有时候,在训练一个良好的生成模型时,弃用汇聚层也是很重要的。比如变化自编码器
(VAEs:variational autoencoders
)和生成性对抗网络
(GANs:generative adversarial networks
)。未来的卷积网络结构中,池化层的发展还真不能确定。
全连接层
全连接层(简称FC
)将每一个结点都与上一层的所有结点相连,用来把前边提取到的特征综合起来。全连接层可以整合卷积层或者池化层中具有类别区分性的局部信息。
为了提升 CNN 网络性能,全连接层每个神经元的激励函数一般采用ReLU函数。最后一层全连接层的输出值被传递给一个输出,可以采用softmax
逻辑回归(softmax regression
)进行分类,该层也可称为softmax
层(softmax laye
)。
将卷积层转化为全连接层
对于任一个卷积层,都存在一个能实现和它一样的前向传播函数的全连接层。该全连接层的权重是一个巨大的矩阵,除了某些特定块(感受野),其余部分都是0
;而在非0
部分中,大部分元素都是相等的(权值共享)。
如果把全连接层转化成卷积层,以输出层的Deep11
为例,与它有关的输入神经元只有上面四个,所以在权重矩阵中与它相乘的元素,除了它所对应的4
个,剩下的均为0
,这也就解释了为什么权重矩阵中有为零的部分。
另外要把“将全连接层转化成卷积层
”和“用矩阵乘法实现卷积
”区别开,这两者是不同的,后者本身还是在计算卷积,只不过将其展开为矩阵相乘的形式,并不是”将全连接层转化成卷积层”,所以除非权重中本身有零,否则用矩阵乘法实现卷积的过程中不会出现值为0
的权重。
将全连接层转化为卷积层
任何全连接层都可以被转化为卷积层。比如,一个K=409
6的全连接层,输入数据体的尺寸是 7*7*5127*7*512
,这个全连接层可以被等效地看做一个F=7
,P=0
,S=1
,K=4096
,F=7
,P=0
,S=1
,K=4096
的卷积层。换句话说,就是将滤波器的尺寸设置为和输入数据体的尺寸设为一致的。因为只有一个单独的深度列覆盖并滑过输入数据体,所以输出将变成1*1*40961*1*4096
,这个结果就和使用初始的那个全连接层一样了。
这个实际上也很好理解,因为,对于其中的一个卷积滤波器,这个滤波器的的深度为512
,也就是说,虽然这个卷积滤波器的输出只有1
个,但是它的权重有7*7*5127*7*512
,相当于卷积滤波器的输出为一个神经元,这个神经元与上一层的所有神经元相连接,而这样与前一层所有神经元相连接的神经元一共有4096
个,这不就是一个全连接网络。
在上述的两种变换中,将全连接层转化为卷积层
在实际运用中更加有用。假设一个卷积神经网络的输入是224*224*3
的图像,一系列的卷积层和汇聚层将图像数据变为尺寸为7*7*512
的激活数据体(在AlexNet中就是这样,通过使用5个汇聚层来对输入数据进行空间上的降采样,每次尺寸下降一半,所以最终空间尺寸为224/2/2/2/2/2=7)。从这里可以看到,AlexNet使用了两个尺寸为4096
的全连接层,最后一个有1000
个神经元的全连接层用于计算分类评分。我们可以将这3个全连接转化为3个卷积层:
(1) 针对第一个连接区域是
[7x7x512]
的全连接层,令其滤波器尺寸为F=7
,这样输出数据体就为[1x1x4096]
了。(2) 针对第二个全连接层,令其滤波器尺寸为
F=1
,这样输出数据体为[1x1x4096]
。(3) 对最后一个全连接层也做类似的,令其
F=1
,最终输出为[1x1x1000]
。
这样做的目的是让卷积网络在一张更大的输入图片上滑动,得到多个输出,这样的转化可以让我们在单个向前传播的过程中完成上述的操作。
至此,对CNN各层的学习暂时告一段落,最起码有了一定的了解。下面将对各层之间的结构进行学习。
卷积神经网络结构特点
卷积神经网络通常是由三种层构成:卷积层,池化层(除非特别说明,一般就是最大值池化)和全连接层。ReLU层通常在卷积层之后,它逐元素地进行激活函数操作,常常将它与卷积层看作是同一层。
层的排列规律
卷积神经网络最常见的形式就是将一些卷积层和ReLU层放在一起,其后紧跟池化层,然后重复如此直到图像在空间上被缩小到一个足够小的尺寸,在某个地方过渡成成全连接层也较为常见。最后的全连接层得到输出,比如分类评分等。换句话说,最常见的卷积神经网络结构如下:
其中*
指的是重复次数,POOL?
指的是一个可选的池化层。其中N>=0
,通常N<=3
,M>=0
,K>=0
,K<3
。例如,下面是一些常见的网络结构规律:
- INPUT -> FC :实现一个线性分类器,此处
N = M = K = 0
; - INPUT -> CONV -> RELU -> FC:单层的卷积神经网络;
- INPUT -> [CONV -> RELU -> POOL]*2 -> FC -> RELU -> FC:此处在每个汇聚层之间有一个卷积层,这种网络就是简单的多层的卷积神经网络;
- INPUT -> [CONV -> RELU -> CONV -> RELU -> POOL]*3 -> [FC -> RELU]*2 -> FC:此处每个汇聚层前有两个卷积层,这个思路适用于更大更深的网络,因为在执行具有破坏性的汇聚操作前,多重的卷积层可以从输入数据中学习到更多的复杂特征。
卷积层的大小选择
几个小滤波器卷积层的组合比一个大滤波器卷积层好。假设你一层一层地重叠了3
个3*3
的卷积层(层与层之间有ReLU激活函数)。在这个排列下,第一个卷积层中的每个神经元都对输入数据体有一个3*3
的视野。第二个卷积层上的神经元对第一个卷积层有一个3*3
的视野,也就是对输入数据体有5*5
的视野。同样,在第三个卷积层上的神经元对第二个卷积层有3*3
的视野,也就是对输入数据体有7*7
的视野。
假设不采用这3
个3*3
的卷积层,而是使用一个单独的有7*7
的感受野的卷积层,那么所有神经元的感受野也是7*7
。多个卷积层与非线性的激活层交替的结构,比单一卷积层的结构更能提取出深层的更好的特征。但是也会有一些缺点,假设所有的数据有C
个通道,那么单独的7*7
卷积层将会包含C*(7*7*C)=49C^2
个参数,而3
个3*3
的卷积层的组合仅有3*(C*(3*3*C))=27C^2
个参数
直观说来,最好选择带有小滤波器的卷积层组合,而不是用一个带有大的滤波器的卷积层。前者可以表达出输入数据中更多个强力特征,使用的参数也更少。唯一的不足是,在进行反向传播时,中间的卷积层可能会导致占用更多的内存。
层的尺寸设置
各层常见的尺寸设置如下:
- 输入层:应该能被
2
整除很多次。常用数字包括32
,64
,96
或224
(比如ImageNet卷积神经网络),384
和512
。 - 卷积层:应该使用小尺寸滤波器(比如
3*3
或最多5*5
),使用步长S=1
。还有一点非常重要,就是对输入数据进行零填充
,这样卷积层就不会改变输入数据在空间维度上的尺寸。比如,当F=3
,那就使用P=1
来保持输入尺寸。当F=5
,P=2
,一般对于任意F
,当P=(F-1)/2
的时候能保持输入尺寸。如果必须使用更大的滤波器尺寸(比如7*7
之类),通常只用在第一个面对原始图像的卷积层上。 - 池化层:负责对输入数据的空间维度进行降采样。最常用的设置是用
2*2
感受野(即F=2
)的最大值池化,步长为S=2
。注意这一操作将会把输入数据中75%
的激活数据丢弃(因为对宽度和高度都进行了2
的下采样)。
另一个不那么常用的设置是使用3*3
的感受野,步长为2
。最大值池化的感受野尺寸很少有超过3
的,因为池化操作过度,易造成数据信息丢失,这通常会导致算法性能变差。
至此,对卷积神经网络的学习到此告一段落,很多深层次的地方其实还没搞懂,在以后的实践中再加深学习吧。
- Post Title: 卷积神经网络CNN入门
- Post Author: ggb0n
- Post Link: http://ggb0n.cool/2020/04/09/卷积神经网络CNN学习记录/
- Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.
1.TCTF2020部分题解
2.第五空间pwn题练习
3.堆溢出-Tcache_Attack
4.堆溢出-Housese_Of_XXX
5.堆溢出基础
6.入坑二进制