本文主要讲解了 RNN 和 LSTM 的结构、前馈、反馈的原理,参考了https://www.jianshu.com/p/f3bde26febed/、https://www.jianshu.com/p/9dc9f41f0b29 与 https://blog.csdn.net/zhaojc1995/article/details/80572098等文章,并纠正了公式的错误、更新了无效的论文链接。
RNN(Recurrent Neural Network)是一类用于处理序列数据的神经网络。
什么是序列呢?序列是一串有顺序的数据,比如某一条数据为 [ x 1 , x 2 , x 3 , x 4 ] [x_1, x_2, x_3, x_4] [x1,x2,x3,x4],其中每个元素可以是一个字符、一个单词、一个向量,甚至是一个声音。比如:
- 自然语言处理问题。 x 1 x_1 x1可以看做是第一个单词, x 2 x_2 x2可以看做是第二个单词,依次类推。
- 语音处理。此时,每个元素是每帧的声音信号。
- 时间序列问题。例如每天的股票价格等。
RNN 处理这种序列数据,在结构上具有天然的优势(相对于普通的神经网络而言,如全连接、CNN等)。
RNN 的结构
我们从基础的神经网络中知道,神经网络包含输入层、隐层、输出层,通过激活函数控制输出,层与层之间通过权值连接。激活函数是事先确定好的,那么神经网络模型通过训练“学“到的东西就蕴含在“权值“中。单层的神经网络如图:
其中,x为输入,W为权重矩阵,b为偏置,f为激活函数,如 sigmoid 等,y 为输出。这样,就建立了输入与输出之间的关联。
基础的神经网络只在层与层之间建立了权连接,RNN最大的不同之处就是在层之间的神经元之间也建立的权连接。如图。
这是一个标准的RNN结构图,图中每个箭头代表做一次变换,也就是说箭头连接带有权值。左侧是折叠起来的样子,右侧是展开的样子,左侧中h旁边的箭头代表此结构中的“循环“体现在隐层。
在展开结构中我们可以观察到,在标准的RNN结构中,隐层的神经元之间也是带有权值的。也就是说,随着序列的不断推进,前面的隐层将会影响后面的隐层。图中O代表输出,y代表样本给出的确定值,L代表损失函数,我们可以看到,“损失“也是随着序列的推荐而不断积累的。
除上述特点之外,标准RNN的还有以下特点:
- 权值共享,图中的W全是相同的,U和V也一样。
- 每一个输入值都只与它本身的那条路线建立权连接,不会和别的神经元连接。
以上是RNN的标准结构,属于多输入多输出,并且输入与输出的个数是相同的,即每次输入都会对应一个输出。
然而在实际中这一种结构并不能解决所有问题,常见的变种有:
1、多输入单输出
有的时候,我们要处理的问题输入是一个序列,输出是一个单独的值而不是序列,应该怎样建模呢?实际上,我们只在最后一个h上进行输出变换就可以了:
这种结构通常用来处理序列分类问题。如输入一段文字判别它所属的类别,输入一个句子判断其情感倾向,输入一段视频并判断它的类别等等。
2、单输入多输出
输入不是序列而输出为序列的情况怎么处理?我们可以只在序列开始进行输入计算,其余只需要隐层状态进行传递。
还有一种结构是把输入信息X作为每个阶段的输入:
这种单输入多输出的结构可以处理的问题有:
- 从图像生成文字(image caption),此时输入的X就是图像的特征,而输出的y序列就是一段句子
- 从类别生成语音或音乐等
3、多输入多输出(输入和输出个数不同)
实际中,还有另外一种多输入多输出的结构,其输入与输出并不是一一对应的,如图:
这种结构又叫Encoder-Decoder模型,也可以称之为Seq2Seq模型。
Encoder-Decoder结构先将输入数据编码成一个上下文向量c。得到c有多种方式,最简单的方法就是把Encoder的最后一个隐状态赋值给c,还可以对最后的隐状态做一个变换得到c,也可以对所有的隐状态做变换。
拿到c之后,就用另一个RNN网络对其进行解码,这部分RNN网络被称为Decoder。具体做法就是将c当做之前的初始状态h0输入到Decoder中。
还有另外一种 Decoder,是将c当做每一步的输入:
由于这种Encoder-Decoder结构不限制输入和输出的序列长度,因此应用的范围非常广泛,比如:
- 机器翻译。Encoder-Decoder的最经典应用,事实上这一结构就是在机器翻译领域最先提出的
- 文本摘要。输入是一段文本序列,输出是这段文本序列的摘要序列。
- 阅读理解。将输入的文章和问题分别编码,再对其进行解码得到问题的答案。
- 语音识别。输入是语音信号序列,输出是文字序列。
RNN的前向输出流程
下面对多输入多输出(一一对应)的经典结构作分析:
其中,x是输入,h是隐层单元,o为输出,L为损失函数,y为训练集的标签。这些元素右上角带的t代表t时刻的状态,其中需要注意的是,隐层单元h在t时刻的表现不仅由此刻的输入决定,还受t时刻之前时刻的影响。V、W、U是权值,同一类型的权连接权值相同。
前向传播算法其实非常简单,对于t时刻,隐层单元为:
h ( t ) = f ( U x ( t ) + W h ( t − 1 ) + b ) h^{(t)}=f(Ux^{(t)}+Wh^{(t-1)}+b) h(t)=f(Ux(t)+Wh(t−1)+b)
其中,f 为激活函数,如 sigmoid、tanh 等,b 为偏置。
t时刻的输出为:
o ( t ) = V h ( t ) + c o^{(t)}=Vh^{(t)}+c o(t)=Vh(t)+c
RNN的训练方法
BPTT(back-propagation through time)算法是常用的训练RNN的方法,其实本质还是BP算法,只不过RNN处理时间序列数据,所以要基于时间反向传播,故叫随时间反向传播。BPTT的中心思想和BP算法相同,沿着需要优化的参数的负梯度方向不断寻找更优的点直至收敛。综上所述,BPTT算法本质还是BP算法,BP算法本质还是梯度下降法,那么求各个参数的梯度便成了此算法的核心。
再次拿出这个结构图观察,需要寻优的参数有三个,分别是U、V、W。与BP算法不同的是,其中W和U两个参数的寻优过程需要追溯之前的历史数据,参数V相对简单只需关注目前,那么我们就来先求解参数V的偏导数。
∂ L ( t ) ∂ V = ∂ L ( t ) ∂ o ( t ) ∂ o ( t ) ∂ V \frac{\partial L^{(t)}}{\partial V}=\frac{\partial L^{(t)}}{\partial o^{(t)}} \frac{\partial o^{(t)}}{\partial V} ∂V∂L(t)=∂o(t)∂L(t)∂V∂o(t)
RNN的损失也是会随着时间累加的,所以需要求出所有时刻的偏导然后求和:
L = ∑ t = 1 n L ( t ) L=\sum_{t=1}^n L^{(t)} L=t=1∑nL(t)
∂ L ∂ V = ∑ t = 1 n ∂ L ( t ) ∂ o ( t ) ∂ o ( t ) ∂ V \frac{\partial L}{\partial V}=\sum_{t=1}^n\frac{\partial L^{(t)}}{\partial o^{(t)}} \frac{\partial o^{(t)}}{\partial V} ∂V∂L=t=1∑n∂o(t)∂L(t)</