深度学习 —— 循环神经网络RNN

本博客是基于吴恩达老师深度学习视频所做的笔记,没有任何代码也无需强大的数学基础,主要是对一些概念的理解。

如有错误欢迎指出~

文章目录

  • 循环神经网络RNN
    • 序列模型
    • 数学符号
    • 循环神经网络
      • 前向传播
      • 反向传播
    • 不同类型的循环神经网络
      • 多对多
      • 多对一
      • 一对一
      • 一对多
    • 语言模型和序列生成
    • 新序列采样
    • RNN的梯度消失
    • GRU单元
    • 长短期记忆LSTM
    • 双向循环神经网络
    • 深层循环神经网络

 

循环神经网络RNN

序列模型

  1. 输入一段音频片段X,输出对应片段的文字记录Y。此时X和Y都是序列数据,因为X是按时序播放的片段,而Y是一系列单词。
  2. 音乐生成问题,只有输出数据Y是序列,而输入数据X可以是空集,也可以是个单一的整数用来表示想要的音乐风格。
  3. 处理情感分类时,输入数据X是序列,Y可以是评分等级。
  4. 在DNA序列识别中也很有用,我们可以用ACGT 表示DNA序列,然后用来匹配蛋白质。
  5. 机器翻译中,我们将输入的语句翻译为另外的语句,两者都是序列。
  6. 对一系列视频贞进行行为判断。
  7. 给定一个句子,对其中的人名进行判断。

上面这些例子中,不过X和Y的长度并不都是相同的,也可能不都是序列。在后面的学习中我们就会碰见各种情况。

 

数学符号

假设我们对一个句子进行人名判断,我们会用 x < t > x^{} x<t>来表示每个单词,下面句子有9个单词。【t是时间序列的意思】,用 y < t > y^{} y<t>来表示每个单词的预测情况,例如 y < 1 > = 1 y^{<1>}=1 y<1>=1表示第一个单词是人名。

image-20211008102632109

我们如何来表示一个句子里的每个单词呢?通常我们会做一张词表(也称为词典)。如下图左侧所示,假设我们词表共有10000个单词,单词a在位置1,单词and在位置367,单词harry在4075,单词potter在6830……而我们输入一个句子时,可以利用One-Hot编码,即输入句子里的Harry映射为一个向量,该向量在4075位置值为1,其他位置值都为0,这样就可以用来表示Harry单词。最终,这句有9个单词的句子会用10000x9的二维向量来表示。【后面会说更方便的编码方式】

深度学习 —— 循环神经网络RNN_第1张图片

 

循环神经网络

为什么要用循环神经网络?想象一下,如果我们使用标准神经网络来处理人名判断该怎么做?

按照前面的例子,有9个单词作为输入,我们可能采取One-Hot编码,因此输入的是9个编码向量,再经过一些隐藏层,最终输出9个值,可能是0或者1来表示每个单词是否为人名。

深度学习 —— 循环神经网络RNN_第2张图片

这个过程中主要存在两个问题:

  • 最开始就举例过,输入和输出可能有不同的长度,即使我们设置最大长度然后用0或者其他来填充来使其达到最大长度,但仍然看起来不是一个好的表达方式。
  • 一个像这样的单纯的神经网络结构,它并不能共享从文本的不同位置上学到的特征。简单来说就是我们希望神经网络学习到Harry在位置1出现时,判断其为人名的一部分,如果Harry在位置t也出现时也能被判断为人名的一部分。这有点类似于卷积神经网络中,我们希望将部分图片里学到的内容快速推广到其他部分。

循环神经网络RNN可以解决上面两个问题。

1、首先我们会读取到句子的第一个单词,将其送到神经网络中的一个隐藏层,对其尝试进行预测,判断是否属于人名的一部分。

深度学习 —— 循环神经网络RNN_第3张图片

2、循环神经网络做的事就是,当我们读取到第二个单词时,它不会直接就利用输入值来预测,还会利用到来自时间步1的信息(一个激活函数的值)。

深度学习 —— 循环神经网络RNN_第4张图片

3、以此类推,每次都会利用到之前的信息。当然,这个例子中输入和输出的长度是相同的,如果长度不同结构会采取一些改变,后面会说。在零时刻,我们会有一个初始激活值,通常是零向量。图中的 w a a 、 w a x 、 w y a w_{aa}、w_{ax}、w_{ya} waawaxwya是权重参数矩阵,每个时间步都是相同的,在后面会说明它们的含义。

深度学习 —— 循环神经网络RNN_第5张图片

因此,在RNN中,假设我们预测 y < 3 > y^{<3>} y<3>,我们不仅会使用 x < 3 > x^{<3>} x<3>还会使用 a < 2 > a^{<2>} a<2>,这样就利用到了 x < 1 > x^{<1>} x<1> x < 2 > x^{<2>} x<2>的信息。

当然,这样做也还是有缺陷,我们只是利用到了过去的信息,没有利用到未来的信息。

就像下面两个句子,只看前三个单词我们是无法确切判断Teddy是否为人名的,在第一个句子中Teddy Roosevelt组合起来我们知道Teddy是人名的一部分,而第二个句子Teddy bears是泰迪熊的意思,Teddy就不属于人名的一部分。因此我们还需要结合未来的信息。

我们会在之后的双向循环神经网络(Bi-RNN)中说明如何解决这个问题。

深度学习 —— 循环神经网络RNN_第6张图片

   

前向传播

假设现在给出了一句话,我们通过RNN对这句话里单词进行人名判断。

我们会一个词一个词输入到神经网络中,同时每次输入的词在计算的时候都会利用到之前的信息(即 a < t − 1 > a^{} a<t1>,也称之为激活项)

深度学习 —— 循环神经网络RNN_第7张图片

  • 初始状态 a < 0 > a^{<0>} a<0>通常是零向量,我们以此来计算后续的 a a a,例如第一次的 a < 1 > = t a n h ( x < 1 > w a x + a < 0 > w a a + b a ) a^{<1>}=tanh(x^{<1>}w_{ax}+a^{<0>}w_{aa}+b_a) a<1>=tanh(x<1>wax+a<0>waa+ba) w a x 、 w a a w_{ax}、w_{aa} waxwaa是权重矩阵,其第一个角标表示这个权重矩阵是为了得到什么值,第二个角标表示这个权重矩阵是和什么值相乘。 b a b_a ba是偏置。最后对值使用了激活函数, t a n h tanh tanh或者 R e L U ReLU ReLU,通常是 t a n h tanh tanh
  • 因此每个词的状态值 a < t > = t a n h ( x < t > w a x + a < t − 1 > w a a + b a ) a^{}=tanh(x^{}w_{ax}+a^{}w_{aa}+b_a) a<t>=tanh(x<t>wax+a<t1>waa+ba)
  • 每个词的预测值会利用到这次的状态a: y < t > = s i g m o i d ( a < t > w y a + b y ) y^{}=sigmoid(a^{}w_{ya}+b_y) y<t>=sigmoid(a<t>wya+by),然后根据我们对预测值的需求使用不同的激活函数。这里由于我们是预测每个单词是否为人名,是一个二分类问题用01来表示,因此使用 s i g m o i d sigmoid sigmoid激活函数。
  • 注意一下,计算预测值是用 a < t > a^{} a<t>而不是 a < t − 1 > a^{} a<t1>,因为 a < t > a^{} a<t>才是第t个单词的激活项,

可以把这里的公式化简一下, a < t > = t a n h ( w a [ x < t > , a < t − 1 > ] + b a ) a^{}=tanh(w_a[x^{},a^{}]+b_a) a<t>=tanh(wa[x<t>,a<t1>]+ba),即把 w a x w_{ax} wax w a a w_{aa} waa和成一个矩阵 w a w_a wa w a w_a wa的下标a表示是用来计算a的矩阵,把 x < t > x^{} x<t> a < t − 1 > a^{} a<t1>合成一个矩阵。

假设 a < t − 1 > a^{} a<t1>形状是(100,1),那么 w a a w_{aa} waa就是(100,100),根据之前的例子 x < t > x^{} x<t>是(10000,1),那么 w a x w_{ax} wax是(100,10000),所以合并后 w a w_a wa是(100,10000)。

深度学习 —— 循环神经网络RNN_第8张图片

 

反向传播

为了计算反向传播,我们需要定义损失函数。

我们先定义一个单词位置上的损失函数(或者叫一个时间步的损失函数):

L < t > ( y ^ < t > , y < t > ) = − y < t > l o g y ^ < t > − ( 1 − y < t > ) l o g ( 1 − y ^ < t > ) L^{}(\hat y^{},y^{})=-y^{}log \hat y^{}-(1-y^{})log (1- \hat y^{}) L<t>(y^<t>,y<t>)=y<t>logy^<t>(1y<t>)log(1y^<t>),其中 y < t > y^{} y<t>表示真实值, y ^ < t > \hat y^{} y^<t>是预测值。这个损失函数叫做交叉熵损失函数。

那么整个序列的损失函数,就是对每个位置的损失函数求和: L ( y ^ , y ) = ∑ t = 1 T L < t > ( y ^ < t > , y < t > ) L(\hat y, y)=\sum\limits_{t=1}^T L^{}(\hat y ^{}, y^{}) L(y^,y)=t=1TL<t>(y^<t>,y<t>),正如上图最上方。

有了损失函数,就可以如我们所知道的那样,在相反的方向上进行计算和传递信息,就像上图的红色箭头。通过导数相关的参数,用梯度下降算法来更新参数。这个算法在RNN中也称为通过时间的反向传播(Backpropagation through time)

下面是一个单元的内部结构:

深度学习 —— 循环神经网络RNN_第9张图片

 

不同类型的循环神经网络

正如开头所说,像下面的音乐生成,我们输入甚至可以是空集,输出得到一段序列,而对于情感分析输入一段文本序列,输出是一个评分。它们输入和输出的长度并不相同,对应的RNN结构也就有所区别。

image-20211008150711480

RNN结构大致分为下列几种:

深度学习 —— 循环神经网络RNN_第10张图片

 

多对多

就像我们前面的举例,对一段文本的人名进行判断,我们一个单词就是一个输入,同时每个输入对应这一个输出,判断这个单词是否是人名。【这种是输入和输出长度相同的情况】

深度学习 —— 循环神经网络RNN_第11张图片

 

如果输入和输出长度不同,例如机器翻译,可能我们输入的是法语,输出的是英语,它们都是表示同一个意思的序列,但它们长度可能不同。

此时的结构如下图所示,先把法语的句子一次读完(这部分叫编码器),然后再输出英语内容(这部分叫解码器)。

深度学习 —— 循环神经网络RNN_第12张图片

多对一

对于像文本情感分类或是电影评分之类的,就属于多对一,many-to-one。我们输入一段文本,包含了多个单词,最终输出的可能是0/1判断文本是正向还是负向,或是输出0~5来评价电影分数。这种结构就不需要每次输入都有一个输出,只需要在最后输出一个结果就行。

深度学习 —— 循环神经网络RNN_第13张图片

 

一对一

这个结构就和我们了解的小型的标准神经网络,输入X,得到输出Y。

深度学习 —— 循环神经网络RNN_第14张图片

 

一对多

类似音乐生成就是一对多的问题,我们可以输入一个整数来表示想要某种音乐风格,然后就没有输入了,或是初始就不输入使得初始状态是零向量。最终会得到多个输出,即多个音符。

当然,这里面其实有一个技术细节,我们会把第一个输出值喂给下一层,依次进行,所以最终得到如下图所示结构。

深度学习 —— 循环神经网络RNN_第15张图片

 

语言模型和序列生成

什么是语言模型?比如我们在做一个语音识别系统时,我们听到一个句子【The apple and pear salad was delicious.】,其中【pear】可能会听成【pair】(读音相同),但一个是苹果和梨的萨拉,一个是苹果和一对沙拉,显然我们应该是想表达第一个。而语音识别系统对于这种情况就需要判断我们到底希望表达什么意思,它会去计算这两句话各自的可能性。

例如:

  • P(The apple and pair salad) = 3.2x10^(-13)
  • P(The apple and pear salad) = 5.7x10^(-10)
  • 第二句话的概率比第一句话大了接近1000倍,因此应该输出第二句话。【为什么句子的概率都比较低?是因为这个句子概率是根据每个词出现的概率相乘得到的】

语言模型所做的就是告诉我们某个特定的句子它出现的概率是多少,它是语音识别系统、机器翻译系统等系统的基本组成部分。

那么如何来建立一个语言模型呢?首先我们需要一个很大的文本语料库,语料库是自然语言处理的一个专有名词,意思就是很长的或者说数量众多的英文句子组成的文本。

假如说我们在训练集中得到这么一句话"猫一天睡15个小时"。

image-20211008165055388

我们要做的第一件事就是将这个句子标记化,就像前面那样建立一个字典,把每个单词转换成对应的One-Hot编码。

其次,我们还可能需要设定一个句子的结尾,例如在末尾增加标志符或是直接以句号结尾(如果我们选择保留标点符号,那么要像单词那样给标点符号也添加到字典里)。

注意,这里是把单词用 y < t > y^{} y<t>来表示的,后面我们看到模型就会明白为什么用y而不是x了

如果句子中有些词并不在我们的初始语料库中,我们会把它们当做标志处理,即未知单词。例如下面句子中”Mau“可能并不在我们预先设置的10000个单词中,我们会把所有的单词作为一个来预测概率。

image-20211008165408431

 

下面我们使用”Cats average 15 hours of sleep a day.“这句话构建模型。

深度学习 —— 循环神经网络RNN_第16张图片

  • 刚开始我们的激活项和输入都是零向量,仅通过一个softmax激活函数来得到每个词的一个概率。也就是说, y < 1 > y^{<1>} y<1>输出的是每个单词的概率,通过softmax实现了全部概率加起来等于1。 这一步得到的是字典中哪个单词会是最终输出句子的第一个单词,它只是预测第一个单词的概率,不去管结果是什么。在我们的例子中最终得到的单词是“Cats”。
  • 在第二个时间步中我们要预测出第二个单词会是什么,我们会把第一个词"Cats"作为输入 x < 2 > x^{<2>} x<2>,然后同样经过softmax层,同样会得到10000个概率,其中P(averge|cats)最大即【在出现cats的条件下,出现average的概率】,于是预测出第二个词是"average"。【之所以average最大,是因为我们已经知道得到的结果就是Cats average 15…这个句子了】
  • 第三个时间步中,我们要给它前面两个词”Cats average“作为输入,得到概率P(15|cats averge)最大,于是预测出第三个词是”15“。依次类推,最后可以得到整句话。
  • 为了训练这个网络,我们需要定义每一个时间步的softmax损失函数: ( ^ < > , < > ) = − ∑ i i < > l o g ^ < > (\hat ^{<>},^{<>})=−\sum\limits_{i}_i^{<>}log \hat _^{<>} L(y^<t>,y<t>)=iyi<t>logy^i<t>
  • 同理,总体的损失函数就是把单个的损失函数求和: L = ∑ t L < t > ( y ^ < t > , y < t > ) L=\sum\limits_t L^{}(\hat y^{},y^{}) L=tL<t>(y^<t>,y<t>)

如果我们使用很大的训练集来训练这个RNN,我们可以通过开头一系列单词像是"Cats average 15"来预测之后单词的概率。假设现在有个只包含三个单词的新句子, y < 1 > 、 y < 2 > 、 y < 3 > y^{<1>}、y^{<2>}、y^{<3>} y<1>y<2>y<3>,现在要计算出整个句子中各个单词的概率。

  • 第一个softmax层会告诉我们 y < 1 > y^{<1>} y<1>的概率,同时这也是第一个输出。
  • 第二个softmax层会告诉我们在考虑 y < 1 > y^{<1>} y<1>的情况下 y < 2 > y^{<2>} y<2>的概率
  • 第三个softmax层会告诉我们在考虑 y < 1 > y^{<1>} y<1> y < 2 > y^{<2>} y<2>的情况下 y < 3 > y^{<3>} y<3>的概率。
  • 这三个概率相乘,就可以得到整个句子的概率 P ( y < 1 > , y > 2 > , y < 3 > ) = P ( y < 1 > ) P ( y < 2 > ∣ y < 1 > ) P ( y < 3 > ∣ y < 1 > y < 2 > ) P(y^{<1>},y^{>2>},y^{<3>})=P(y^{<1>})P(y^{<2>}|y^{<1>})P(y^{<3>}|y^{<1>}y^{<2>}) P(y<1>,y>2>,y<3>)=P(y<1>)P(y<2>y<1>)P(y<3>y<1>y<2>)

 

新序列采样

在你训练一个序列模型之后,要想了解到这个模型学到了什么,一种非正式的方法就是进行一次新序列采样,来看看到底应该怎么做。

假设现在有一个已经经过莎士比亚文章训练的模型,我们想要检验下它到底学到了些什么,让它生成一篇内容。

深度学习 —— 循环神经网络RNN_第17张图片

  • 首先我们需要对模型生成的第一个词进行采样,于是我们输入 x < 1 > = 0 , a < 0 > = 0 x^{<1>}=0,a^{<0>}=0 x<1>=0,a<0>=0,然后经过softmax后会得到一个向量,该向量会包含第一个词是a的概率、第一个词是arron的概率…第一个词是zulu的概率、第一个词是UNK的概率。接着我们对该向量使用np.random.choice,来根据向量中这些概率的分布进行采样,这样就实现了对第一个词的采样。
  • 然后到第二个时间步,我们使用 y ^ < 1 > \hat y^{<1>} y^<1>作为输入,无论我们在第一个时间步得到什么词都要把它作为第二个时间步的输入,接着经过softmax层,预测出 y ^ < 2 > \hat y^{<2>} y^<2>。举个例子,如果我们对第一个词进行采样后,得到的是”The“,这个词开头很常见,然后就把"The"当做 x < 2 > x^{<2>} x<2>,对预测的结果 y ^ < 2 > \hat y^{<2>} y^<2>再采样,依次类推。
  • 那么什么时候结束呢?一种方法是判断到,一种方法是设置采样的单词数量。这样就会得到一个通过RNN随机产生的句子,不过这种方式是基于单词实现的,我们也可以基于字符实现。即我们把语料库换成字符[A-Za-z],中间加点空格、数字之类的。
  • 经过莎士比亚文章训练的RNN可能生成如下所示的文章:

image-20211008200858792

 

RNN的梯度消失

基本的RNN算法是有一个很大的问题的,也就是梯度消失问题。

我们知道RNN模型的结构如下:

深度学习 —— 循环神经网络RNN_第18张图片

假设有个句子:The cat, which already ate …, was full.,这里cat是单数,所以后面用的was来保持一致。如果前面我们是用The cats,那么后面就应该用were。显然,对于中间部分的长度可以很长,这就会存在一个长期依赖,即最前面的单词会对后面单词造成影响。然而目前我们见到的RNN是不擅长处理这种长期依赖的。为什么?

学神经网络时我们知道,当一个网络层数很深时,它的反向传播就很难传播回去。对于RNN也是如此,我们一开始前向传播从左到右计算,然后进行反向传播从右到左,越往前面就会越难传播,由于梯度消失问题,后面层的输出误差很难影响到前面。

深度学习 —— 循环神经网络RNN_第19张图片

这就会导致我们很难让一个RNN意识到它前面记住的单词是单数还是复数,然后在序列后面生成依赖的was或是were。

同时,我们在反向传播的时候随着层数的增多,梯度不仅可能指数型下降,还可能指数型上升。也就是会存在梯度爆炸问题,不过梯度爆炸很明显,因为指数级大的梯度会让参数变得非常大,从而网络参数直接崩溃,所以很容易发现梯度爆炸。解决方法也很简单,可以使用梯度修剪,即观测到梯度大于某个阀值时缩放梯度向量就好。而想要解决梯度消失就比较麻烦,这也是我们后面需要解决的问题。

 

GRU单元

GRU(Gated Recurrent Unit,门控循环单元)改变了RNN的隐藏层,使其可以更好的捕捉深层连接,并改善了梯度消失问题。

从前面的学习中,我们知道RNN的隐藏层单元是下面这样的,其中

a < t > = t a n h ( w a [ x < t > , a < t − 1 > ] + b a ) , y < t > = s o f t m a x ( a < t > w y + b y ) a^{}=tanh(w_a[x^{},a^{}]+b_a),y^{}=softmax(a^{}w_y+b_y) a<t>=tanh(wa[x<t>,a<t1>]+ba),y<t>=softmax(a<t>wy+by)

深度学习 —— 循环神经网络RNN_第20张图片

从前面RNN的梯度消失问题中我们知道,对于句子”The cat, which already ate…, was full“,我们需要RNN记得最开始是单数的猫cat。

在GRU单元中,会有一个新的变量叫c,称为记忆细胞cell,它可以提供记忆能力,例如记住猫是单数还是复数。在时间t处,有记忆细胞 c < t > c^{} c<t>,本质上这里的 c < t > = a < t > c^{}=a^{} c<t>=a<t>,只是为了后面说明LSTM方便用两个符号来标记,因为在LSTM中它们不相等。

重点来了,在GRU中最重要的思想就是,我们用符号 Γ u \Gamma_u Γu表示, Γ u = s i g m o i d ( w u [ c < t − 1 > , x < t > ] + b u ) \Gamma_u=sigmoid(w_u[c^{},x^{}]+b_u) Γu=sigmoid(wu[c<t1>,x<t>]+bu)。从公式可以发现 Γ u \Gamma_u Γu其实就是加了sigmoid激活函数,所以 Γ u \Gamma_u Γu其实是在0~1之间的一个值,对于大多数可能的输入,sigmoid的输出总是非常接近0或者非常接近1。于是 Γ u \Gamma_u Γu可以充当一个门了,看下面图片。

深度学习 —— 循环神经网络RNN_第21张图片

现在,我们把上面的情况用公式来表示: c < t > = Γ u ∗ c ~ < t > + ( 1 − Γ u ) ∗ c < t − 1 > c^{}=\Gamma_u*\tilde{c}^{}+(1-\Gamma_u)*c^{} c<t>=Γuc~<t>+(1Γu)c<t1>,由于 Γ u \Gamma_u Γu接近0或者1,因此 c < t > c^{} c<t>可能取新值 c ~ < t > \tilde{c}^{} c~<t>也可能取旧值 c < t − 1 > c^{} c<t1>,这由门 Γ u \Gamma_u Γu来决定。(选择 Γ \Gamma Γ来表示门,是因为这个字符比较像门,也可以用G,因为门的单词是Gate,而u表示update,所以 Γ u \Gamma_u Γu是指更新门)

 

理解了原因,下面我们来看看GRU单元的结构图:

深度学习 —— 循环神经网络RNN_第22张图片

我们可以通过门决定在某个时间步中是要更新记忆细胞还是维持记忆细胞,只要维持细胞非常简单,只要门是0,这样最终输出的就会是输入值 c < t − 1 > c^{} c<t1>。当然,我们的c是多维的,例如门是100维,c也会是100维,它们的乘积也是对应元素的乘积,这样就可以维持100个单词。

 

上面我们描述的是简化版的GRU,它只包含了更新门,完整的GRU还会有重置门 Γ r \Gamma_r Γr(r表示reset重置),它的表达式和更新门表达式一样,只是参数矩阵和偏置不同: Γ r = s i g m o i d ( w r [ c < t − 1 > , x < t > ] + b r ) \Gamma_r=sigmoid(w_r[c^{},x^{}]+b_r) Γr=sigmoid(wr[c<t1>,x<t>]+br)。当重置门 Γ r = 1 \Gamma_r=1 Γr=1且更新门 Γ u = 0 \Gamma_u=0 Γu=0时,GRU单元就和普通的RNN单元一样。

重置门用于决定过去有多少信息需要遗忘,有了更新记忆细胞的公式就改变了 c ~ < t > = t a n h ( w c [ Γ r ∗ c < t − 1 > , x < t > ] + b r ) \tilde{c}^{}=tanh(w_c[\Gamma_r*c^{},x^{}]+b_r) c~<t>=tanh(wc[Γrc<t1>,x<t>]+br),和之前相比,重置门 Γ r \Gamma_r Γr与记忆细胞 c < t − 1 > c^{} c<t1>的乘积代替了单独的记忆细胞。同样地, Γ r \Gamma_r Γr是一个向量,每个元素值是0或者1,如果是0与记忆细胞对应元素相乘,就可以遗忘信息。

看看下面的结构图:

深度学习 —— 循环神经网络RNN_第23张图片

 

长短期记忆LSTM

LSTM(Long short term memory)也是一种RNN结构,它比GRU出现得更早,是一个比GRU更加强大和通用的坂本,相当于GRU单元是LSTM的简化版。GRU只含有重置门 Γ r \Gamma_r Γr和更新门 Γ u \Gamma_u Γu,而LSTM含有更新门 Γ u \Gamma_u Γu、遗忘门 Γ f \Gamma_f Γf、输出门 Γ o \Gamma_o Γo,它的公式如下:

深度学习 —— 循环神经网络RNN_第24张图片

对比一下GRU:

深度学习 —— 循环神经网络RNN_第25张图片

可以发现,LSTM里 c < t > ≠ a < t > c^{} \neq a^{} c<t>=a<t>,需要用到一个输出门 Γ o \Gamma_o Γo。在 c ~ < t > \tilde{c}^{} c~<t>中也没有重置门,而是有一个单独的遗忘门 Γ f \Gamma_f Γf,虽然它们二者功能相同。然后是在 c < t > c^{} c<t> 中,用遗忘门 Γ f \Gamma_f Γf替代了 1 − Γ u 1-\Gamma_u 1Γu,我们来看看结构图:

深度学习 —— 循环神经网络RNN_第26张图片

 

我们什么时候应该用GRU什么时候用LSTM?这其实没有统一的准则,GRU的优点是模型简单,它只有两个门,所以更容易创建一个大型网络,在计算性方面运行得比较快,但LSTM更加强大和灵活,因为它有三个门。无论是GRU还是LSTM,我们都可以用它们来构建更加深层连接的神经网络。

 

双向循环神经网络

前面我们已经了解了大部分RNN模型的关键构件,普通RNN、GRU单元、LSTM单元,还有两个方法可以提升我们的模型,分别是双向RNN模型和深层RNN。先从双向RNN开始吧~

依然沿用前面的例子,当我们需要判断人名时,如果是下面两句话,无论是普通RNN,还是用GRU单元、LSTM单元,它们都是从左往右依次处理。这样就会出现一个问题,对于"Teddy"这个词,它在第一句话里是泰迪熊,而第二句话里是人名的一部分,但这个结果需要我们考虑到第四个单词。也就是说,如果我们仅仅是从左往右来判断,可能无法知道第三个单词到底是不是人名。

深度学习 —— 循环神经网络RNN_第27张图片

 

那么对于一个双向的RNN是如何解决这个问题的呢?请看下面的图。

深度学习 —— 循环神经网络RNN_第28张图片

这里每个单元都是普通的RNN,如果我们只执行蓝色部分,那么就是单向的普通RNN进行预测。图中红色部分的四个单元就是反向循环层,它们是从右往左执行的,先输入beas到 a < 4 > a^{<4>} a<4>进行计算,然后将信息和Teddy传给 a < 3 > a^{<3>} a<3>,依次类推。

对于这个双向的RNN来说,执行流程是:我们先获取到完整的句子,然后正向序列执行,反向序列执行,最后正向的激活值结果要和反向的激活值结果进行一个预测处理。也就是说,不像之前单向那样,一个单元计算完就会出预测值,而是要等正向和反向全部执行完,才会预测每个单元的值。

预测公式: y ^ < t > = s i g m o i d ( w y [ a ← < t > , a → < t > ] + b y ) \hat{y}^{}=sigmoid(w_y[{\overleftarrow{a}}^{},{\overrightarrow{a}}^{}]+b_y) y^<t>=sigmoid(wy[a <t>,a <t>]+by)

现在来预测单词“Teddy”时,我们会有过去的信息(来自 a < 2 > a^{<2>} a<2> a < 1 > a^{<1>} a<1>)也会有未来的信息(来自 a < 4 > a^{<4>} a<4>),这样就能准确预测了。图中只是普通的RNN,我们可以用GRU或者LSTM来替换。

不过,对于双向的RNN显然是有一个缺点的,那就是我们必须要有完整的数据序列,假如我们想构建一个语音识别系统,我们用双向RNN去实现就需要等人把整句话说完,然后获取整个语音序列才能进行处理。对于多数NLP应用,只要我们能获取整个句子,那么双向RNN总是比较高效的。

 

深层循环神经网络

前面所说的无论是普通RNN、GRU单元、LSTM还是双向RNN,它们都是简单的一层。如果要学习非常复杂的函数,通常会把RNN的多个层堆叠在一起构建更深的模型。

这是一层的RNN,每个单元根据输入以及之前的信息直接进行预测,左上角角标表示层:

深度学习 —— 循环神经网络RNN_第29张图片

这是两层的RNN,第一层的激活值作为第二层的输入:

深度学习 —— 循环神经网络RNN_第30张图片

对于RNN来说不会像CNN那样有很多层,通常对于深层RNN最多也就到三层,因为RNN是带有时间维度的。不过可以在预测值上面堆叠隐藏层,就像下面这样,顶部的两层是没有水平方向连接的。:

深度学习 —— 循环神经网络RNN_第31张图片

当然,上面的单元都是标准的RNN单元,通常会是GRU单元或者LSTM等,也可以构建双向RNN。不过深层的RNN训练需要很多计算资源,虽然这看起来没有多少层。

你可能感兴趣的:(深度学习,深度学习,RNN,LSTM,GRU)