CNN、RNN和LSTM
卷积神经网络(Convolutional Neural Network,CNN)
1984年,日本学者福岛基于感受区域概念提出了神经认知机。神经认知机可以看作是卷积神经网络的第一个实现网络,也是感受区域概念在人工神经网络领域的首次应用。神经认知机将一个视觉模式分解成许多feature,然后进入分层递阶式相连的feature maps进行处理,这样就可以将视觉系统模型化,使其能够在物体有位移或轻微变形的时候,也能完成识别。
我们以用于手写数字识别的LeNet-5为例来讲卷积神经网络。
卷积神经网络由卷积层(Convolutions Layer)、池化层(Pooling Layer)和全连接层构成。全连接层在最后,前面是若干卷积层和池化层,每个卷积层后面跟一个池化层,如此重复。
图中展示了LeNet-5网络的结构,一共七层,前面四层是卷积层和池化层(池化层又叫子采样层,也即Subsample Layer),后面三层是全连接层,最后一层输出层是高斯连接层,也是全连接层,共有10个节点,分别代表数字0到9,且如果节点i
的值为0,则网络识别的结果是数字i
。采用的是欧式径向基函数(ERBF)的网络连接方式。假设x是上一层的输入,y是ERBF的输出,则ERBF输出的计算方式是:
理解卷积神经网络的核心在于理解前面的卷积层和池化层。既然我们将卷积神经网络分为了卷积层、池化层和全连接层,则隐含了前面的卷积层和池化层并不是全连接的,那它们是怎么连接的呢?
介绍连接方式之前需要先介绍卷积层与池化层的性质:
卷积层:卷积层由若干张feature map(FM)构成。对输入数据应用卷积核(可以认为是对特定feature非常敏感的探测器),在输入数据上滚一遍我们的卷积核,就得到了一张FM,FM上记录了卷积核在原图不同区域的激活程度(与该卷积核的feature越契合,激活程度越高),也即去掉了读不懂的数据,留下了符合一定feature的数据。每个卷积核拥有一个feature,也就能产生一张对应feature的FM。
池化层:池化层的价值在于缩减输入数据的规模,FM上k*k
一共k^2
个激活值合并成为池化层上的一个激活值,合并的方法有很多种,比如最大值合并、平均值合并及随机合并,需要视情况而定,比如如果要确定『有没有』的问题,就要采用最大值合并,把最高的激活值保留下来。
然后最后一个池化层后面连接到一个或多个全连接层,全连接层的输出就是最后的输出。训练过程通过改进的反向传播实现,在反向传播的时候需要特别考虑到池化层合并激活值的方法,最大值合并、平均值合并等,均需要采用特定的处理方法,并以此来更新卷积核。
我们能人工定义的是卷积核的宽和高,还有卷积核的个数。卷积核对什么feature敏感,是先随机初始化,再经过BP算法慢慢训练出来的,卷积核的权重就是卷积神经网络主要需要学习的参数。
卷积神经网络通过『局部感知野』与『权值共享』大大减少了连接的个数,也即需要训练的参数的个数。
就拿原图像到第一层卷积层的连接举例,假设我们的图像是1000*1000的,则有10^6个隐层神经元,那么它们全连接的话,也就是每个隐层神经元都连接图像的每个像素点,就有10^12个连接,也即10^12个权值参数需要训练,这显然是不值得的。但是对于一个只识别特定feature的卷积核,需要大到覆盖整个图像的所有像素点吗?通常是不需要的,一个特定feature,尤其是第一层需要提取的feature,通常都相当基础,只占图像很小的一部分。所以我们设置一个较小的局部感受区域,比如10*10,也即每个神经元只需要和这10*10的局部图像相连接,所以10^6个神经元也就有10^8个连接。这就叫局部感知野。
那什么叫权值共享呢?在上面的局部连接中,10^6个神经元,每个神经元都对应100个参数,所以是10^8个参数,那如果每个神经元所对应的参数都是相同的,那需要训练的参数就只有100个了。
这后面隐含的道理在于,这100个参数就是一个卷积核,而卷积核是提取feature的方式,与其在图像上的位置无关,图像一个局部的统计特征与其他局部的统计特征是一样的,我们用在这个局部抽取feature的卷积核也可以用在图像上的其它任何地方。
而且这100个参数只是一种卷积核,只能提取一种feature,我们完全可以采用100个卷积核,提取100种feature,而所需要训练的参数也不过10^4,最开始我们训练10^12个参数,还只能提取一种特征。选取100个卷积核,我们就能得到100张FM,每张FM可以看做是一张图像的不同通道。
接下来我要具体介绍一下利用卷积核卷积生成FM的过程:
左侧绿色的是5*5的原图,左侧黄色的是3*3的卷积核覆盖的区域,右侧是3*3的FM。
原图就不说了,卷积核的权重是黄色区域每个格子右下角乘号后面的数字,也即卷积核就是我们前面用作示例的那个卷积核:
具体的卷积过程,就是将卷积核覆盖在原图上,从左上角开始,一次向右移动一个像素,卷积完一行,整个卷积核向下移动一个像素,再开始卷积。在卷积核覆盖的区域范围内,原图与卷积核对应位置的像素分别做乘法,再全部加和。
至于FM为什么是3*3的,因为(5-3)/1 + 1 = 3
。原图5*5,卷积核3*3,按这样的方法法卷积(卷积核卷积的滑动步长为1),得到的feature也就是3*3的。也即这一层隐层的神经元的个数是3*3,不过这只是一张FM,如果10张相同的FM,那神经元的个数就是10倍了。
如果定义了滑动步长为2,那就每次向右移动2个像素了,一行结束也是向下移动2个像素,当然FM大小的计算方法也要随之改变了,也即(5-3)/2 + 1 = 2
,公式为(原图宽高-卷积核宽高)/滑动步长 + 1
。
这样基本的卷积过程就讲完了,接下来讲池化过程。
池化过程看起来要简单的多,就是一个取局部平均值\最大值的过程(根据具体池化方法决定)。人们可以计算图像一个区域上的某个特定特征的平均值 (或最大值)。这些概要统计特征不仅具有低得多的维度 (相比使用所有提取得到的特征),同时还会改善结果(不容易过拟合)。
另外需要提的一点是,前面说10*10的卷积核需要训练的参数是100个,严格来讲是错误的,其实是101个,因为每个卷积核还有一个可训练偏置。
接下来以LeNet-5为例,从头捋一遍卷积神经网络的过程,重新放一遍LeNet-5的图:
输入层是32*32像素的图片,比数据集中最大的的字符(最大体积是20*20像素的字符,位于28*28像素区域的中心)大很多。这样做的原因是能使潜在的特征比如边缘的端点、拐角能够出现在最高层次的卷积核的接收域的中心。
然后第一层C1,6个卷积核,所以也就6张FM,卷积核是5*5的,至于为什么每张FM是28*28的,(32-5)/1 + 1 = 28
。那C1一共有多少个需要训练的参数呢?(5*5+1)*6 = 156
个可训练参数,每个卷积核26个可训练参数,6个卷积核,也就是156个可训练参数。那C1与原图一共有多少个连接呢?(28*28)*6*26 = 122304
个连接,28*28的FM,一共有6张,所以乘6,这6张FM里的每一个点,都是采用了6种卷积核的其中之一卷积出来的,不管是这6种卷积核里的哪种,都有26个参数,也即26个连接,所以要乘以26。当然每张FM里面的点用的是同样的卷积核,连接数公式简记为(FM宽高*FM宽高)*可训练参数
。
然后第二层S2,以2*2的范围去池化,我们也称其为一个2*2的池化核。6张28*28的FM被池化成了14*14的图,这个算法很简单,可以参考上面池化层的动图去理解。这里的每个池化核有两个可训练参数,一个负责与池化核里4个输入相加的和相乘,另一个作为可训练偏置加在乘积上,最后得到池化结果。6张FM,也对应6个池化核,每个池化核2个参数,也即12个可训练参数。那S2层与C1层有多少连接呢?(14*14)*6*5 = 5880
个连接,这里池化核与卷积核不同,对于卷积核而言,26个可训练参数,也即26个连接;而池化只有2个可训练参数,但是如果池化核也只有2个连接,那那4个输入是从哪来的呢,所以这里2*2的池化核对应5个连接。然后6张图,每张14*14,每个点5个连接,所以得到5880个连接。
然后是C3层,C3层同样通过5*5的卷积核去卷积每张图14*14的S2,然后得到的每张FM就是10*10的,算法与C1时相同。它有16种不同的卷积核,所以C3层就对应了16张FM。这里有个比C1层复杂的多的问题,就是C1层只卷积1张图,而C3层要卷积6张图。那这16张FM是如何卷积前面S2层的6张图的呢?如下图:
0-5这6张FM卷积S2中的3张图,6-11这6张FM卷积S2中连续的4张图,12-14这3张FM卷积S2中不连续的4张图,15这张FM卷积所有的6张图。FM0卷积图012、FM6卷积图0123、FM12卷积图0134、FM15卷积图012345。
我们知道一张FM对应一个卷积核,那一个卷积核怎样同时卷积多张图呢?我们把多张图上同一位置的卷积结果相加,然后代入激活函数:
我们来想一下卷积多张图的意义在哪?我们知道,越往后的卷积核的feature就越高级越抽象,而这个高级feature往往是通过组合低级feature得到的,比如低级feature可能是折线,而高级feature就可能是一个轮子、一张人脸。卷积多张图就是组合低级feature尝试生成高级feature的过程,这种不对称的组合连接的方式有利于提取多种组合特征。
这里计算可训练参数和连接数也比C1层复杂得多,C3层一共(5*5*3+1)*6 + (5*5*4+1)*6 + (5*5*4+1)*3 + (5*5*6+1)*1 = 1516
个训练参数,需要注意的是,之前的算法是(5*5+1)*卷积核数
这里不再适用,因为每个卷积核不再只卷积一张图,5\*5
后面要接着乘以卷积图的张图,因为之前只有1张,所以就省略了,这里每个卷积核都卷积多张图,所以不能省略,而且要分开计算,因为不同的卷积核卷积图的张数也分为不同的几种;至于连接数,代之前的公式,一共(10*10)*1516 = 151600
个连接。
再后面是S4层,用16个2*2的池化核将16张10*10的FM池化成16张5*5的图,一共16\*2 = 32
个可训练参数,一共(5*5)*16*5 = 2000
个连接。
然后是C5层,我们看到S4层每张图就已经是5*5的了,而我们的卷积核也是5*5的,所以卷积完了就是一个点。这里用120个卷积核,生成120张FM,每张FM就一个点。每张FM都卷积S4层全部16张图,卷积多张图的方法也和C3层讲的一样。
再后面是F6层,F6层是全连接层,有84个单元,与C5层全连接,有10164个可训练参数。如同经典神经网络,F6层计算输入向量和权重限量之间的点积,再加上一个偏置。然后将其传递给Sigmoid函数产生单元i
的一个状态。
输出层也是全连接层,前面提到过它的计算方式,这里重复一下,一共10个输出,10个单元,每个单元有84个输入,且如果节点i
的值为0,则网络识别的结果是数字i
。采用的是欧式径向基函数(ERBF)的网络连接方式。假设x是上一层的输入,y是ERBF的输出,则ERBF输出的计算方式是:
其意义是计算每个输入向量与参数向量之间的欧式距离,输入离参数向量越远,ERBF的输出值就越大,输出值越小,也即越匹配越契合。一个ERBF的输出可以被理解为衡量输入模式和ERBF相关联类的一个模型的匹配程度的惩罚项。
CNN主要用来识别位移、缩放及其他形式扭曲不变性的二维图形。由于CNN特征检测层通过训练数据进行学习,在使用CNN时,避免了显式的特征抽取,而隐式地从训练数据中进行学习;再者,由于同一FM上的神经元权值相同,所以网络可以并行学习,这也是卷积网络相对于神经元彼此相连网络的一大优势。卷积神经网络以其局部权值共享的特殊结构在语音识别和图像处理方面有着独特的优越性,其布局更接近于实际的生物神经网络,权值共享降低了网络的复杂性,避免了特征提取和分类过程中数据重建的复杂度。
递归神经网络(RNN)
递归神经网络(RNN),是两种人工神经网络的总称。一种是时间递归神经网络(recurrent neural network),另一种是结构递归神经网络(recursive neural network)。时间递归神经网络的神经元间连接构成有向图,而结构递归神经网络利用相似的神经网络结构递归构造更为复杂的深度网络。RNN一般指代时间递归神经网络,也叫循环神经网络。单纯递归神经网络因为无法处理随着递归,梯度爆炸或消失的问题,难以捕捉长期时间关联;而LSTM(长短期记忆神经网络,Long-Short Term Memory)可以很好地解决这个问题。
RNN的目的是用来处理序列数据,普通的前馈神经网络,是从输入层到隐层再到输出层,相邻层的节点之间是全连接的,然而一层内的节点之间却是没有连接的。这种普通的神经网络对很多问题是无能为力的,例如,你要预测句子中的下一个词是什么就一般要用到前面的词,因为词与词之间往往不是相互独立的。RNN之所以被称为循环神经网络,就是因为一个序列当前的输出不仅和当前的输入有关,还和过去的输出有关。RNN会对前面的输出进行记忆并用在当前的输出计算之中,同时意味着隐层内的节点也不再是无连接的。
举一个例子,填槽问题,有这样一句话,『I would like to arrive Taipei on November 2nd』。将这句话作为输入提供给机票预订系统,需要填的槽位有三个,一个是出发地,一个是目的地,另一个是到达时间,当然这句话只能填两个。
首先我们尝试用普通前馈神经网络来解决这个问题。比如有这样一个神经网络,它有两个输出神经元,分别对应三个槽,一个槽是出发地,一个槽是目的地,一个槽是到达时间;然后我们要训练这个神经网络,使其在接受『I would like to arrive Taipei on November 2nd』输入序列时,目的地槽位对应输出神经元的激活值在输入Taipei时达到最大,而到达时间槽位对应输出神经元的激活值在输入November 2nd时达到最大?
那我们考虑一下,如果将输入序列这样修改一下『I would like to leave Taipei on November 2nd』,我们还能把Taipei填到目的地槽位里面吗?难道我们要先检索一下句子里的是leave还是arrive?要是人家用其他的词呢?这就是人工学习而不是机器学习了,都是治标不治本的做法。最本质的解决方法是要让神经网络拥有『记忆』。
我们不断重复使用同样的网络结构,将其隐层相连,前面隐层的输出作为后面隐层输入的一部分使用。
上图是神经单元的展开,如果收起,则结构如下:
那使用RNN如何解决前面提到的问题呢?在RNN里,当前面的词不同时,对应的隐层输出也就不同,前面词的隐层输出作为输入的一部分提供给了Taipei对应的隐层,进而使得Taipei被填进每个槽位的概率也不同。从而实现,当前面是arrive时,将Taipei填进目的地槽;当前面是leave时,将Taipei填进出发地槽。
下面来讲RNN中广泛使用且效果极为出色的一种类型 —— LSTM(长短期记忆神经网络,Long-Short Term Memory)。
前面讲,普通RNN无法处理随着递归,梯度爆炸或消失的问题,这样导致普通RNN无法捕捉过长时间的关联。前面隐层输出的不同会随着梯度消失,距离越远,影响就越小。而LSTM通过特殊的结构解决了这个长期依赖的问题。
LSTM的结构这部分我主要读了两篇资料,一篇是Christopher Olah 的博文,另一篇是李宏毅教授Deep Learning Tutorial里面的对应内容。
这两篇资料的讲解,一篇以等价的逻辑结构来讲,更通俗;一篇以真实结构来讲,更深入,这里我准备加上自己的理解,将两种结构对比起来讲。
先说标准RNN的结构,所有的RNN都有神经网络的重复模块组成的链式结构。对于标准的RNN,这种重复模块有一个非常简单的结构,如一个单一的tanh(双曲正切)层:
LSTM中也有这样的链式结构,但重复模块却具有和一般RNN不同的结构。前面标准RNN只有一个交互层,而LSTM有四个。
为了便于理解,我们先以LSTM的等价逻辑结构进行讲解:
如图所示,LSTM的神经元由四部分组成,分别是:输入门、输出门、遗忘门和记忆细胞。接受4个输入,1个输出。输入门、输出门、遗忘门各接受一种控制信号输入。输入门还额外接受上个神经元的输入,输出门额外给出一个到下个神经元的输出。3种门是用来保护和控制细胞状态的。
下图展示了逻辑结构下的实际计算过程:
首先,前面讲输入门、输出门、遗忘门各接受一种控制信号输入,也就是这里图上的zi
、zo
、zf
,这三种信号进来要经过的函数f
为激活函数,通常使用Sigmoid函数,我们知道Sigmoid函数的输出在0到1之间,非常适合作为门的控制信号,0代表完全舍弃(关闭),1代表完全保留(开启)。
关注图中门的开启关闭是如何起作用的,我们可以发现,假如输入门接受信号zi
过小,导致对应Sigmoid函数的输出为0,则意味着输入门关闭,我们可以看到f(zi)
与神经元的输入z
处理得到的g(z)
是相乘的关系,也即如果输入门关闭,则该LSTM神经元不再接收输入门的输入z
。同理,如果输出门关闭,则该LSTM神经元输出门无法产生有效的输出a
。而对于遗忘门,根据图中的公式可知,如果遗忘门关闭,则意味着记忆细胞过去的记忆值c
将被忘记,该LSTM神经元的输出完全独立,不受过去输出的影响。(如果将遗忘门叫做记忆门或许更好理解,记忆门关闭则完全遗忘。)
LSTM计算过程举例:
左边的图,遗忘门那,c' = 3+c
。c
是7,c'
就得到10了。
理解了LSTM的逻辑结构,接下来我们看一下LSTM的真实结构,可以翻上去和标准RNN的结构图比较一下:
我们先来解释一下图中的各种图标的含义:
黄色方块代表一个神经网络层,里面的σ
代表Sigmoid激活函数,tanh代表双曲正切激活函数。粉色的圈代表一次操作,比如求积求和。每一条黑线传输着一整个向量,从一个节点的输出到其他节点的输入。合在一起的线表示向量的连接,分开的线表示内容被复制,然后分发到不同的位置。
我们这里准备一部分一部分的将真实结构对应到前面的逻辑结构上去。先是逻辑结构中的记忆细胞:
这里图中的Ct-1
到Ct
的过程,就是前面逻辑结构中c
到c'
的过程,前面讲c
到c'
的过程有两部分,一部分是由遗忘门f(zf)
决定前面的c
是否参与到c'
的计算中来,对应到真实结构上也就是图中的ft
,它决定了Ct-1
会不会加到C
中去;另一部分就是输入门的输入,在讲逻辑结构的过程中,我们知道,输入门的输入也由两部分构成,一部分是信号输入zi
代入Sigmoid函数的结果f(zi)
决定了是否接受输入z
,另一部分就是处理输入z
得到的g(z)
了。对应到上面的图中,it
就是f(zi)
,负责决定输入z
要不要被采用,图中与it
相乘的另一部分,就可以理解为g(x)
了。
接下来是遗忘门和输入门,在上面已经讲过了:
上面说了,f(zf)
就对应着ft
,看遗忘门图中的式子,括号里面的自然就是zf
了,前面说过f
函数就是Sigmoid函数就是图中的σ
;然后是输入门,前面说,f(zi)
就是it
,看输入门图中右侧的第一个式子,括号里面的自然就是zi
了,下面的式子就是g(z)
,那对应起来g
函数就是tanh了,z
就是括号里面的内容。
然后是输出门:
前面讲,输出门接受一个信号zo
,经过处理得到f(zo)
;计算出的c'
,经过h
函数处理,得到h(c')
,然后乘起来得到a
,a = h(c')f(zo)
(如果忘了可以往回翻一下逻辑结构图)。这对应关系就很明显了,zo
是图中第一个式子括号里面的那一大堆,ot
就是f(zo)
,计算出的c'
就是图中的Ct
,这个前面说过了,然后h
函数还是tanh函数,然后乘起来得到的那个a
就是ht
。我觉得我讲的真是不能再清楚了:)
LSTM可以通过控制门的开闭天然避免梯度消失。因为前面提到过,梯度消失的原因在于连乘式的产生,计算每层的梯度都需要用到下一层的梯度,需要一直乘到输出层,而对于LSTM来说,输入和记忆是相加的,也就是在逻辑结构那说的:
这有个什么好处呢,就是只要遗忘门不关闭(即f(zf)
不等于0),前面的输出c
的影响就永远不会消失。
以上,这样这篇《浅谈深度学习基础》也就结束了,下一篇可能会是《浅谈自然语言处理基础》,因为我本身就是Chatbot方向的产品经理,对NLP也很感兴趣。当然也可能不是:>