文章目录
前言
要处理序列模型,使用标准神经网络的效果并不好,这时应该使用循环神经网络(RNN)。
本文为吴恩达深度学习序列部分的学习笔记。
1.循环神经网络模型
1.1 为什么不使用标准神经网络?
使用标准神经网络来处理序列模型效果并不好,原因有二:
- 输入和输出的长度不一定相同。即使使用填充(pad)或零填充(zero pad)使每个输入语句达到设定的最大长度(假设有的话),这样的效果也并不好。
- 一个普通的神经网络结构,它并不共享从文本的不同位置上学到的特征。例如,如果神经网络已经学习到了在位置1出现的 Harry 可能是人名的一部分,那么如果Harry出现在其他位置,比如x时,它不能够自动识别其为人名的一部分。
另外,如果输入都是维数较多的向量,这会造成非常庞大的输入层,第一层的权重矩阵会有着巨大的参数。
1.2 RNN
RNN就没有上述的问题。
用循环神经网络(Recurrent Neural Networks)从左到右读一个句子,在读到第二个单词x<2>时,它不仅用x<2>来预测y<2>,还会输入一些来自时间步1(time step 1)的信息,以此类推。在每一个时间步中,RNN都会传递一个激活值到下一个时间步中用于计算。
其简化结构为:
1.3 RNN工作流程
在零时刻构造一个激活值a<0>,通常是0向量。RNN从左到右扫描数据,同时每个时间步的参数是共享的。用Wax来表示从x<1>到隐藏层的一系列参数,每个时间步使用的是相同的参数Wax,同时每个时间步使用相同的参数Waa,输出结果由同样的Wya决定。
在RNN中,在预测y<3>时,不仅要使用x<3>的信息,还要使用来自x<1> 和x<2>的信息,前面的信息通过图中的绿色箭头来帮助预测。这个循环神经网络有一个缺点是它只用了这个序列之前的信息来做出预测,在预测y<3>时,它没有用到后面的信息,如果是一些需要结合后文的句子,那么预测可能会出现差错。这就是前向传播RNN。
开始先输入a<0>,一般是零向量,随着向前传播的过程,先计算激活值a<1>,然后再计算y<1>。一般情况下,t时刻的激活值和输出值如下图所示。这些等式定义了神经网络的前向传播,像图中这样,从 左到右完成前向传播。
Wax第二个下标意味着Wax要乘以某个x类型的量,然后第一个下标a表示它是用来计算某个a类型的变量。同样的,可以看出这里的Wya乘上了某个a类型的量,用来计算出某个y类型的量。
循环神经网络用的激活函数经常是 tanh,不过有时候也会用 ReLU,选用哪个激活函数 是取决于你的输出y,如果它是一个二分问题,用 sigmoid 函数作为激活函数, 如果是k类别分类问题的话,那么可以选用 softmax 作为激活函数。
1.4 符号简化
可以把a=g(Waaa+Waxx+ba)简化为a=g(Wa[a,x]+ba),其中ba为偏差项。定义Wa的方式是将矩阵Waa和Wax水平并列放置:
[a,x]的意思是将这两个向量堆在一起,即:
上述矩阵相乘刚好等于Waaa+Waxx,这样可以不用使用两个参数矩阵,而是将它们压缩成一个参数矩阵Wa,从而建立更复杂模型时这就能够简化我们要用到的符号。
对于y可以简化为:
现在Wy和by仅有一个下标,它表示在计算时会输出什么类型的量,即表明它是计算 y 类型的量的权重矩阵,上面的Wa和ba表示这些参数是用来计算激活值的。
2.通过时间的反向传播
2.1 前向传播
前向传播的计算,若有一个输入序列,x<1>,x<2>,x<3>一直到x,然后用x<1>和a<0>计算出步1的激活项,再用x<2>和a<1>计算出a<2>,以此类推,直到a。
另外还要加上参数Wa和ba的影响。
2.2 反向传播
为了计算反向传播,需要一个损失函数:
它对应的是序列中一个具体的词,如果是应用于人名识别的程序是某个人的名字,那么y的值就是1,然后神经网络将输出这个词是名字的概率值,比如 0.1。我将它定义为标准logistic交叉熵损失函数(Cross Entropy Loss)。
整个序列的损失函数定义为:
在计算图中,通过y<1>可以计算对应的损失函数,于是可以计算出第一个时间步的损失函数,接着逐步计算出后面的时间步的损失函数,把它们都加起来就得到了总体的损失函数L。
2.3 多种RNN
RNN的种类有很多,“多对多”、“多对一”、“一对一”和“一对多”的结构都有。
3.循环神经网络的梯度消失(Vanishing gradients with RNNs)
3.1 梯度消失
基本的 RNN 算法还有一个很大的问题,就是梯度消失。
举个语言模型的例子,假如看到这个句子,“The cat, which already ate ……, was full.”,前后应该保持一致,因为 cat 是单数,所以应该用 was。“The cats, which ate ……, were full.” cats 是复数,所以用 were。这个例子中的句子有长期的依赖,最前面的单词对句子后面的单词有影响。 但是我们目前见到的基本的 RNN 模型(上图编号 3 所示的网络模型),不擅长捕获这种长期依赖效应。
梯度消失,比如说一个很深很深的网络,100 层甚至更深,对这个网络从左到右做前向传播然后再反向传播。我们知道如果这是个很深的神经网络,从输出y得到的梯度很难传播回去,很难影响前面层的计算。
对于RNN,因为梯度消失,实际上很难让一个神经网络意识到它要记住看到的单数名词还是复数名词,由此也很难判断后面应该使用was还是were。
3.2 梯度爆炸
随着层数的增多,梯度不仅可能指数型的下降,也可能指数型的上升。事实上梯度消失在训练 RNN 时是首要的问题,尽管梯度爆炸也是会出现,但是梯度爆炸很明显,因为指数级大的梯度会让你的参数变得极其大,以至于你的网络参数崩溃。
梯度爆炸基本上用梯度修剪就可以应对,但梯度消失比较棘手。
4.GRU单元
4.1 简化的GRU
下图是RNN的单元可视化呈现,门控循环单元的结构也是类似。
GRU单元会有一个新的变量c,代表记忆细胞(cell),记忆细胞的作用是ᨀ供了记忆的能力,比如说一只猫是单数还是复数,所以当它看到之后的句子的时候,它仍能够判断句子的主语是单数还是复数。
在时间t处,有记忆细胞c,c=a,两者的值相等。
在每个时间步会使用一个候选值来重写记忆细胞:
c~替代了c的值,
GRU真正重要的思想是有一个门Γu,u代表更新,这是一个0到1之间的值。实际上就是把这个式子带入sigmoid函数。
这样,对于大多数可能的输入来说,输出总是非常接近0或者非常接近1。因此,Γu在大多数的情况下非常接近0或1。
c~更新c的等式为:
由门来决定是否更新它。记忆细胞c被设定为0或1,GRU会记住这个值,门Γu的作用就是决定何时更新这个值。当使用完毕后就可以忘记它。
如果更新值Γu=1,也就是说把新值c设为候选值,对于中间的值,把门值设为0,即c为旧的值,不更新它。不更新他,就用旧的值就代表没有忘记这个值。
因为Γu的值很接近0,那么c几乎等于c,c的值很好地被维持了,即使经过了很多的时间步。这就是缓解梯度消失问题的关键。
如果c是向量,那么各个参数都是对应维数的向量,这样它们相乘就是对应位置元素的乘积,这代表更新的时候可能只是更新其中的一些比特,而其他比特可以用来做其他的事情。
4.2 完整GRU
完整的GRU要在第一个式子中增加一个Γr,r代表相关性(relevance)。Γr告诉你计算下一个c的候选值和c有多大的相关性。
计算门Γr需要一个新的参数Wr,
5.长短期记忆LSTM(long short term memory)
GRU(门控循环单元)。它能够让你可以在序列中学习非 常深的连接。其他类型的单元也可以让你做到这个,比如 LSTM 即长短时记忆网络,比GRU更有效。
上图是GRU和LSTM的公式对比,在LSTM中不再有a=c的情况,也不再使用相关门Γr。
LSTM同样有更新门Γu和更新参数Wu,会使用遗忘门Γf和新的输出门Γo:
其中记忆细胞的更新公式为:
这里使用了单独的更新门和遗忘门,也就给了记忆细胞选择权去位置旧的值c或者加上新的值c~。
a的公式是LSTM的主要式子
把各个输入值和上一时刻的a和c结合起来生成当前时刻需要的a,再通过激活函数得到输出y,把多个这种结构连接起来就是一个LSTM网络。
只要你设置了正确的遗忘门和更新门,LSTM很容易把c的值一直往下传,因此LSTM
比GRU更擅长记忆某个值。
有的版本的LSTM的门值不仅取决于a和x,有时候也可以偷窥一下c的值,这叫做“窥视孔连接”(peephole connection)。 “偷窥孔连接”其实意思就是门值不仅取决于a和x,也取决于上一个记忆细胞的值,然后“偷窥孔连接”就可以结合这三个门Γu、Γf、Γo来计算。
另外,关于更新,假如有一个 100 维的向量,你有一个 100 维的隐藏的记忆细胞单元,然后比如第 50 个c的元素只会影响第 50 个元素对应的那个门,所以关系是一对一的,并不是任意这 100 维的c可以影响所有的门元素。相反的,第一个c的元素只能影响门的第一个元素,第二个元素影响对应的第二个元素,以此类推。
6.构建更好的模型
6.1 双向神经网络
双向RNN可以构建更好的模型,这个模型可以让你在序列的某点处不仅可以获取之前的信息,还可以获取未来的信息。
在判断第三个词 Teddy是不是人名的一部分时,光看句子前面部分是不够的,为了判断y<3>是 0 还是 1,除了前 3 个单词,你还需要更多的信息,因为根据前3个单词无法判断他们说的是 Teddy 熊,还是前美国总统 Teddy Roosevelt,所以这是一个非双向的或者说只有前向的 RNN。我刚才所说的总是成立的,不管这些单元是标准的 RNN 块,还是 GRU 单元或者是LSTM 单元,只要这些构件都是只有前向的。
以上就是双向 RNN 的内容,这个改进的方法不仅能用于基本的 RNN 结构,也能用于
GRU 和 LSTM。通过这些改变,你就可以用一个用 RNN 或 GRU 或 LSTM 构建的模型,并且能够预测任意位置,即使在句子的中间,因为模型能够考虑整个句子的信息。这个双向 RNN网络模型的缺点就是你需要完整的数据的序列,你才能预测任意位置。比如说你要构建一个语音识别系统,那么双向 RNN 模型需要你考虑整个语音表达,但是如果直接用这个去实现的话,你需要等待这个人说完,然后获取整个语音表达才能处理这段语音,并进一步做语音识别。对于实际的语音识别的应用通常会有更加复杂的模块,而不是仅仅用我们见过的标准的双向 RNN 模型。但是对于很多自然语言处理的应用,如果你总是可以获取整个句子,这个标准的双向 RNN 算法实际上很高效。
6.2 深层循环神经网络
要学习非常复杂的函数,通常我们会把 RNN 的多个层堆叠在一起构建更深的模型。这节视频里我们会学到如何构建这些更深的 RNN。
通常这些单元没必要非是标准的 RNN,最简单的 RNN 模型,也可以是 GRU 单元或者 LSTM 单元,并且,你也可以构建深层的双向 RNN 网络。由于深层的 RNN 训练需要很多计算资源,需要很长的时间,尽管看起来没有多少循环层,这个也就是在时间上连接了三个深层的循环层,你看不到多少深层的循环层,不像卷积神经网络一样有大量的隐含层。