CNN(卷积神经网络)详解

Why CNN

首先回答这样一个问题,为何咱们要学CNN,或者说CNN为何在不少领域收获成功?仍是先拿MNIST来当例子说。MNIST数据结构不清楚的话自行百度。。html

我本身实验用两个hidden layer的DNN(全链接深度神经网络)在MNIST上也能取得不错的成绩(98.29%)。下面是一个三个hidden layer的网络结构图
盗图1
盗图1web

全链接深度神经网络,顾名思义,每一个神经元都与相邻层的神经元链接。在这个实验中,每一个数字的image是28*28,也就是784(=28*28)个数值,每一个数值对应一个像素值,值的大小反应像素点的强度。这就意味着咱们网络的输入层有784个神经元。输出层呢?因为咱们是预测0-9这几个数字,输出层固然就是10个神经元了。至于隐藏层节点的个数咱们能够自行选定,本实验中选的是500.算法

咱们想一想为何DNN在训练后可以正确地分类?那确定是它学到了东西,学到什么东西呢?它学到了图片中的某些空间结构,不一样数字它们的空间结构确定是不同的,而这样的空间结构就是由像素点与像素点之间的关系造成。咱们再仔细看DNN输入层和第一个隐藏层,发现它对咱们输入的784个像素点是同等对待的,也就是说它此时并无考虑像素点与像素点之间的关系。有没有一个好点的模型可以考虑到这点呢?那就是CNN网络

CNN有三个几本思想,局部感觉野(local receptive fields) 权值共享(shared weights) 池化(pooling) 数据结构

局部感觉野(local receptive fields)

刚刚咱们在DNN中是把输入层784个神经元排成了一条长线,这里咱们还原图片原来的样子(28*28),以下图架构

强迫症的同窗就不要数每行每列几个了,我已经数过了是28了。。偷笑
盗图2
盗图2app

DNN中,咱们会把输入层的每一个神经元都与第一个隐藏层的每一个神经元链接(看看盗图1)。而在CNN中咱们这样作的,第一个隐藏层的神经元只与局部区域输入层的神经元相连。下图就是第一个隐藏层的某个神经元与局部区域输入层的神经元相连的状况。
盗图3
盗图3less

这里的局部区域就是局部感觉野,它像一个架在输入层上的窗口。你能够认为某一个隐藏层的神经元学习分析了它”视野范围“(局部感觉野)里的特征。图中一个隐藏层的神经元有5*5个权值参数与之对应。ide

咱们移动这样一个窗口使它可以扫描整张图,每次移动它都会有一个不一样的节点与之对应。咱们从输入层左上角开始,以下
盗图4
盗图4svg

而后,咱们一个像素往右滑动一个像素,以下
盗图5
盗图5

以此类推能够造成第一个隐藏层,注意咱们的图片是28*28的,窗口是5*5的,能够获得一个24*24(24=28-5+1)个神经元的隐藏层

这里咱们的窗口指滑动了一个像素,一般说成一步(stride),也能够滑动多步,这里的stride也是一个超参,训练是能够根据效果调整,一样,窗口大小也是一个超参。

权值共享(Shared weights and biases)
上一节中提到一个隐藏层的神经元有5*5个权值参数与之对应。这里要补充下,这24*24个隐藏层的神经元它们的权值和偏移值是共享的 用公式描述下

σ ( b + l = 0 4 m = 0 4 w l , m a j + l , k + m )

σ表明的是激活函数,如sigmoid函数等,b就是偏移值,w就是5*5个共享权值矩阵,咱们用矩阵a表示输入层的神经元,ax,y表示第x+1行第y+1列那个神经元(注意,这里的下标默认都是从0开始计的,a0,0表示第一行第一列那个神经元)因此经过矩阵w线性mapping后再加上偏移值就获得公式中括号里的式子,表示的是隐藏层中第j+1行k+1列那个神经元的输入。有点晕的话就参照上面的图,图4就是j=k=0的状况,图5是j=0,k=1. 最后加上激活函数就表示该隐藏神经元的输出了。这部分原理和DNN是同样的,若是把w改为28*28的矩阵就变成了全链接,就是DNN了。

咱们能不能简化一下这个公式呢

a 1 = σ ( b + w a 0 )

a1表示隐藏层的输出,a0表示隐藏层的输入,而∗就表示卷积操做(convolution operation) 这也正是卷积神经网络名字的由来。

因为权值共享,窗口移来移去仍是同一个窗口,也就意味着第一个隐藏层全部的神经元从输入层探测(detect)到的是同一种特征(feature),只是从输入层的不一样位置探测到(图片的中间,左上角,右下角等等),必须强调下,一个窗口只能学到一种特征!另外,窗口还有其余叫法:卷积核(kernal),过滤器(filter)。咱们在作图像识别时光学习一个特征确定是不够的,咱们想要学习更多的特征,就须要更多的窗口。若是用三个窗口的话以下图
盗图6
盗图6

窗口与窗口间的w和b是不共享的,三个窗口就表示有三个w矩阵和三个偏移值b,结果是从整张图片的各个位置学到三种不一样的特征。到这里确定有人会问,你说学到特征了,怎么证实学到了呀?如今咱们用20个窗口来学习MNIST里的图片特征,咱们只看20个窗口里的权值矩阵w,若是把这20个w画成20张黑白图,每张图片都是5*5(一个权值表明一个像素点),以下图所示
盗图7
盗图7

盯着其中的一张看,白色区域表示权值比较小,说明窗口的这部分对输入层的神经元不敏感(responds less),相反黑色部分表示权值比较大,说明窗口的这部分对输入层的神经元敏感(responds more).每张图片都有明显的黑白区域,这也可以说明CNN确实学到一些和空间结构相关的特征。究竟学的是什么特征呢?这个很难回答清楚,此处暂不深究,更好理解的话能够参考 Visualizing and Understanding Convolutional Networks

权值共享还有一个很大的好处,就是能够大大减小模型参数的个数。咱们的例子中,一个窗口参数个数是26(5*5+1),20个窗口就是520个参数,若是换成全链接的话就是785(28*28+1)个参数,比CNN多了265个参数。可能你以为265嘛,对计算机来讲彻底不算什么。若是咱们是30个隐藏层的DNN的话(深度学习里很常见的),须要23550(785*30)个参数,是CNN的45倍多。。固然咱们也不能光光去比较它们参数的个数,毕竟两个模型本质原理上就相差甚远,可是直觉上咱们能够感觉到,CNN能够依靠更少的参数来得到和DNN相同的效果,更少的参数就意味着更快的训练速度,这但是谁都想要的。

池化(Pooling)

CNN还有一个重要思想就是池化,池化层一般接在卷积层后面。池化这个词听着就颇有学问,其实引入它的目的就是为了简化卷积层的输出。通俗地理解,池化层也在卷积层上架了一个窗口,但这个窗口比卷积层的窗口简单许多,不须要w,b这些参数,它只是对窗口范围内的神经元作简单的操做,如求和,求最大值,把求得的值做为池化层神经元的输入值,以下图,这是一个2*2的窗口
盗图8
盗图8

值得注意的是,咱们此时的窗口每次移动两步,采用的是求最大值的方法,全部称之为max-pooling,刚刚卷积层含有24*24个神经元,通过池化后到池化层就是12*12个神经元。一般卷积层的窗口是多个的,池化层的窗口也是多个的。简单来讲,卷积层用一个窗口去对输入层作卷积操做,池化层也用一个窗口去对卷积层作池化操做。可是注意这两个操做的本质区别。下面来看一个用三个卷积窗口和跟随其后的池化窗口长啥样。
盗图9
盗图9

怎么理解max-pooling呢?因为通过了卷积操做,模型从输入层学到的特征反映在卷积层上,max-pooling作的事就是去检测这个特征是否在窗口覆盖范围的区域内。这也致使了,它会丢失这种特征所在的精准位置信息,所幸的是池化层能够保留相对位置信息。然后者相比而言比前者更重要。不理解上面的话也不要紧,可是须要记住池化层一个最大的好处:通过池化后,大大减小了咱们学到的特征值,也就大大减小了后面网络层的参数(上图能够看出池化层的神经元数明显少于卷积层神经元数)。

max-pooling技术只是池化技术的一种,还有一种比较经常使用的是L2-pooling,与max-pooling惟一的区别就是在池化窗口扫过的区域里作的操做不是求最大值,而是全部神经元平方后求和再开根号,这和咱们L2正则对权值参数的操做是同样的。实际操做中,这两种方式都是比较经常使用的。池化操做方式的选择也是咱们调参工做的一部分,咱们能够根据validation data集来调节,选择更好的池化操做。

总的来看

介绍完CNN的三个几本思想概念后咱们把它串起来看下。
盗图10
盗图10
从左往右依次是输入层,卷积层,池化层,输出层。输入层到卷积层,卷积层到池化层已经详细介绍过了。池化层到输出层是全链接,这和DNN是同样的。
总体上把我CNN的网络架构,其实和DNN很类似,都是一层一层组合起来的,层与层之间的行为也是有对应的权值w和偏移值b决定的,而且它们的目的也是一致的:经过training data来学习网络结构中的w和b,从而能把输入的图片正确分类。很简单吧。

到此为之,CNN的基本原理大体介绍完毕了,若是只需对CNN作大体了解的话上面的内容我想应该足够了。下面主要介绍下其数学原理了。

CNN的BP算法

DNN的BP算法

介绍CNN的BP算法以前仍是先看下DNN的吧,二者有不少类似的地方。
这里我假设你们都理解了DNN的网络结构了,首先引入一些数学符号。

a l :第l层神经元的输出
z l :第l层神经元的输入
W l :从l-1层mapping到l层权值矩阵
b l :与上面参数对应的偏移值
x :train data的输入
y :train data正确的label
这里设咱们的输出层为第L层,对应 a L ,采用均方差来度量损失,那么对应的损失函数就是

J ( W , b , x , y ) = 1 2 | | a L y | | 2 2 ( 1 )

好了,有了损失函数就开始用梯度降低法了。记住咱们的目的是为了求出每层的 W b
先来看下输出层的输出
a L = σ ( z L ) = σ ( W L a L 1 + b L ) ( 2 )

咱们把(2)代入(1)中获得
J ( W , b , x , y ) = 1 2 | | a L y | | 2 2 = 1 2 | | σ ( W L a L 1 + b L ) y | | 2 2 ( 3 )

咱们利用链式求导法,求得 W , b 的梯度
J ( W , b , x , y ) W L = J ( W , b , x , y ) z L z L W L = ( a L y ) σ ( z L ) ( a L 1 ) T ( 4 )
J ( W , b , x , y ) b L = J ( W , b , x , y ) z L z L b L = ( a L y ) σ ( z L ) ( 5 )

表示两个向量的内积
上面两个式子有个公共的部分那就是 J ( W , b , x , y ) z L 咱们能够单独把它拎出来看
δ L = J ( W , b , x , y ) z L = ( a L y ) σ ( z L ) ( 6 )

咱们这里引入了一个新的符号 δ l : J ( W , b , x , y ) z l ,这样的话咱们就能够简写下(4),(5)两个式子
J ( W , b , x , y ) W l = J ( W , b , x , y ) z l z l W l = δ l ( a l 1 ) T ( 7 )
J ( W , b , x , y ) b l = J ( W , b , x , y ) z l z l b l = δ l ( 8 )

简单多了吧,咱们只须要把 δ l 求出来, W , b 就OK了
仔细看下式子(6),咱们的数据按网络结构正向传播到输出层的时候 δ L 能够求出来,咱们能不能依靠 δ L 来求 δ L 1 , δ L 2 , δ L 3 呢?继续使用链式求导法能够得出:
δ l = J ( W , b , x , y ) z l = J ( W , b , x , y ) z l + 1 z l + 1 z l = δ l + 1 z l + 1 z l ( 9 )

可见,想经过 δ l + 1 来求 δ l 关键是看怎么求 z l + 1 z l ,好,咱们再来看 z l + 1 z l 的关系,由式子(2)能够得出
z l + 1 = W l + 1 a l + b l + 1 = W l + 1 σ ( z l ) + b l + 1 ( 10 )

那么:
z l + 1 z l = ( W l + 1 ) T σ ( z l ) ( 11 )

(11)代入(9)中获得
δ l = δ l + 1 z l + 1 z l = ( W l + 1 ) T δ l + 1 σ ( z l ) ( 12 )

咱们根据 δ L ,再根据上式的递推关系就能够求出每层的 δ l ,也就能够求出每层的 W , b 的梯度了。

下面是DNN BP算法伪代码(随机梯度降低法)

Max:最大迭代次数
α : learning rate
{ ( x 1 , y 1 ) , ( x 2 , y 2 ) , . . . , ( x m , y m ) } :输入的样本对

for iter = 1 to Max
for i = 1 to m:
set a 1 = x i
for l=2 to L,前向传播计算 a l = σ ( z l ) = σ ( W l a l 1 + b l )
for l=L to 2,反向传播计算 δ l = ( W l + 1 ) T δ l + 1 σ ( z l )
for l=2 to L,更新第l层的 W l , b l :
W l = W l α i = 1 m δ i , l ( a i , l 1 ) T
b l = b l α i = 1 m δ i , l
检查 W , b 的变化值,若是小于中止迭代的阀值,就跳出循环
输出各层的 W , b ,结束。

CNN的BP算法

如今再来看看CNN的BP算法,因为CNN能够分为卷积层,池化层和全链接层,全链接层和DNN同样,不须要再说了。主要看另外两层,首先看看卷积层。

在说CNN基本思想的时候咱们已经给出了卷积层正向传播的公式了:

a l = σ ( z l ) = σ ( a l 1 W l + b l ) ( 13 )

仔细看这个公式,和DNN正向传播的公式惟一的区别就有卷积操做 ,注意这不是乘号* 咱们还沿用和上述DNN相同的损失函数。公式(7),(8),(9)一样适用于卷积层,可是 z l z l 1 的关系变了:
z l = a l 1 W l + b l = σ ( z l 1 ) W l + b l ( 14 )

这里和DNN的区别就是卷积操做,对于含有卷积的式子求导,卷积核(W)被旋转180度,意思就是上下翻转,而后左右翻转,所以咱们的公式(12)变成了:
δ l 1 = δ l z l z l 1 = δ l r o t 180 ( W l ) σ ( z l 1 ) ( 15 )

对卷积求导原理感兴趣的同窗能够自行百度。
有了对卷积求导的知识就能够很轻易写出:
J ( W , b ) W l = J ( W , b ) z l z l W l = δ l r o t 180 ( a l 1 ) ( 16 )

刚刚说公式(8)一样适合卷积层其实不够严谨,对于b的梯度不能像DNN那样直接等于 δ l ,由于b是一个向量,而这里的由于通过了卷积求导后变成了一个三维的张量,因此能够近似地用一个偏差向量代替:
J ( W , b ) b l = u , v ( δ l ) u , v

到这里咱们已经解决了如何求卷积层上的 W , b 的梯度了,就剩池化层了。
池化层上 W , b 的梯度怎么求呢?不对,基本思想里说过池化层压根就没有 W , b 呀。那是否是能够直接忽略池化层呢?不行!CNN网络结构是一个总体,BP算法运行时,数据流确定会通过池化层,偏差在通过池化层后也会发生相应的变化。那求它的什么呢?想一想,卷积层在求 δ 时要依赖后一层的 δ ,卷积层的后一层不正是池化层吗?因此咱们要求出池化层的 δ

在基本思想里面讲到,前向传播时池化层通常的操做是MAX或Average等,咱们如今是要从压缩过的偏差 δ l 来还原一个较大区域的偏差。咱们首先把 δ l 全部的子矩阵大小还原成池化前的大小,若是用的是MAX操做的话,就把全部子矩阵各个池化局部的值放在以前作前向传播时获得最大值的位置。这个过程叫作unsample。
举个例子就好理解了,假设 δ l 第k个子矩阵是:

δ k l = ( 2 8 4 6 )

假设咱们池化窗口大小是2*2,则将其还原成原来大小就是:
( 0 0 0 0 0 2 8 0 0 4 6 0 0 0 0 0 )

这里假设咱们以前前向传播时记录的最大值的位置分别是左上,右下,右上,左下,unsample后就是:
( 2 0 0 0 0 0 0 8 0 4 0 0 0 0 6 0 )

因为咱们池化层是对卷积层的输出作池化的,因此在求 δ 时不一样于卷积层和全链接层,链式求导时先是对上一层的 a 求偏导,再乘上上一层的 a z 的偏导,公式以下:
δ k l 1 = J ( W , b ) a k l 1 a k l 1 z k l 1 = u p s a m p l e ( δ k l ) σ ( z k l 1 )

归纳下:
δ l 1 = u p s a m p l e ( δ l ) σ ( z l 1 )

这里不一样的池化操做也对应着不一样的unsample操做。
不由感叹下,无论状况多么复杂,数学公式最后的解老是那么简洁。

后续会更新CNN在中文句子分类上的应用原理和source coding

参考:
Neural Networks and Deep Learning
卷积神经网络(CNN)反向传播算法