这个专题的笔记是我在学习斯坦福大学的公开课程CS224N:Deep Learning For Nature Language Processing时所做的一些课程笔记。这是第四部分,主要内容是RNN和LSTM模型。
语言模型
语言模型是一种计算在一个特定序列出现的概率的模型,对于m个单词
n-gram语言模型
因此要获得一个语言模型,我们需要对每一组连续n个单词进行出现频率的计算,这就是n-gram语言模型,比如说对于一个2-gram的语言模型,我们可以这样计算:
n-gram语言模型可能会非常稀疏,因为一些单词组合可能永远也不会在训练样本中出现,我们可以用smoothing的方法来解决这一问题,也就是说不管有没有出现过我们都算这个组合至少出现了
次 随着n的增长,n-gram语言模型需要的存储空间也会越来越大,并且这一复杂度是指数规模的
基于窗口的神经语言模型
基于窗口的神经网络语言模型可以学习出单词的分布式表示和计算序列出现概率的函数,它的基本结构如下所示:
这个模型可以用下面这个表达式来表示:
上面提到的这些语言模型实际上只是基于有限长度的滑动窗口的卷积模型,但事实上,窗口比较小的时候模型预测的准确度也是无法保证的,而窗口大了又会导致参数的规模急剧增长,因此我们需要一个可以处理任意长度的输入,并且参数规模维持在一定范围内的新模型。
循环神经网络RNN
RNN的架构
和只能给予一个有限长度的滑动窗口不同,卷积翻译模型不同,循环神经网络(Recurrent Neural Networks)是可以根据语料库中所有之前出现过的额单词来调整模型,RNN的基本组成单元如下图所示:
每个神经网络单元(也称为timestep)会对输入的词嵌入向量
RNN的损失函数
RNN的损失函数通常采用交叉熵来计算,对于第t个timestep,其交叉熵可以表示为:
RNN的反向传播
我们可以对上面的RNN表达式进行一定的修改,并假设激活函数
首先我们需要注意到,softmax函数可以写成:
其中
RNN的优缺点
RNN的优点主要有如下几个:
RNN可以处理任意长度的输入
RNN模型的size不会随着输入的语料库长度变化而变化
某个timestep计算得到的结果理论上可以在后面的很多timestep中被用到
因为每个timestep的权重是相同的,因此RNN模型具有一定的对称性
但是同时,RNN的缺点也是很明显的:
RNN是一个序列化的模型,因此RNN不能进行并行的训练
事实上随着timestep的推移,前面的信息越来越难以被保留下来,可能存在梯度消失和梯度爆炸的问题
梯度消失Vanishing Gradient
在RNN的反向传播过程中,参数需要根据其梯度来更新,而梯度需要用链式求导法则来进行计算,如果其中的一部分梯度非常小,可能会导致最终要求的梯度累积越来越小,造成梯度的消失。
梯度爆炸Exploding Gradient
同样的如果梯度过大也会使得参数的更新出现问题,因为梯度值过大导致反向传播更新的时候容易出现参数大幅度的更新,引起波动,这称之为bad update,可能会导致最终的计算结果是INF或者NAN
解决办法:Gradient Clipping,当梯度g的大小超过一个固定的阈值的时候,对g进行一定的压缩,防止梯度爆炸。因此LSTM被踢出用来解决梯度消失和梯度爆炸的问题。
双向RNN
前面介绍的RNN模型实际上只能把前面的单词信息传递给后面,信息的传递是单向的,而双向RNN则可以进行双向的信息传递,实现的方式也很简单,就是通过在隐层增加一系列神经元来进行反向的信息传递,如下图所示:
而这一架构用公式可以表示为:
长短期记忆模型LSTM
门控循环单元
虽然理论上来说,RNN可以提取长期的,距离较远的单词依赖关系,但是实际训练过程中其实是难以实现的,因为计算机数值计算的有限精度导致了RNN存在梯度消失和梯度爆炸等问题,而门控循环单元(Gated recurrent units,GRU)的目的就是实现更加长持久性的对长依赖关系的记忆,在这种门控循环单元的作用下,使用
重置门:重置信号
可以决定 对于 的重要性,如果发现没有啥关系,重置门有权利彻底消除过去的隐藏状态 更新门:更新信号
可以决定 对当前层究竟有多高的权重,以此来决定隐藏状态的最后结果相对于上一层的信息和这一层新的meemory的权重 新记忆生成:根据当前单元输入的
生成新的 隐藏状态:最终按一定权重将新的记忆和上个单元传递下来的
组合
上面的这些过程可以用下面的这幅图来表示:
不得不说CS224N的插图对于GRU的讲解还是非常清楚的,而GRU中我们主要需要学习的参数有6个矩阵,同样的也是用反向传播的算法来进行训练。
长短期记忆LSTM
长短期记忆模型(Long-Short-Term-Memories,LSTM)和门控循环单元略有区别,但也是用于保存长期记忆的一种复杂激活单元架构,如下图所示:
这种架构下,隐藏层中主要进行的数学运算有:
生成新记忆:根据上一个单元传入的
和当前位置的词向量 计算出一个结果 输入门:根据上一个单元传入的
和当前位置的词向量 计算出一个结果 ,并评估当前位置的单词 是否有用,并有必要在之后的计算中使用 遗忘门:根据上一个单元传入的
和当前位置的词向量 计算出一个结果 ,并且评估上一个隐藏状态 是否有价值用在当前层的计算中 生成最终记忆:根据输入门和遗忘门的判断,并结合新的记忆单元和上一个传递过来的记忆单元的信息生成最终记忆
输出门:这是一个GRU中不存在的门,其目的是将最终的记忆单元和隐藏状态分开,因为最终生成的记忆单元有很多前文的信息是没有必要保存在当前隐藏状态中的,因此这一个单元决定了最终记忆
有多少内容需要暴露给 ,根据这个权重来产生最终的隐藏状态
事实上LSTM相比于前面提到的GRU主要多出了一个输出门用来控制信息暴露给当前隐藏状态的程度,可以用下面一张图来表示LSTM的各个门之间的关系:
LSTM如何解决梯度问题
LSTM的架构使得神经网络可以在许多timestep之后依然保存一些持久化的信息,可以通过改变门的开关达到保存信息的目的,但事实上,并没有彻底解决梯度消失和梯度爆炸的问题,但是让模型更容易学习到长距离的依赖关系,事实上梯度的消失和爆炸是所有神经网络架构都存在的问题,不仅仅是RNN