第五门课 序列模型(Sequence Models)

1.1为什么选择序列模型(Why Sequence Models?)

循环神经网络(RNN)之类的模型在语音识别、自然语言处理和其他领域中引起变革。序列问题有很多不同类型:
在这里插入图片描述
以上问题都可以被称作使用标签数据(X,Y)(X,Y)(X,Y)作为训练集的监督学习。

1.2数字符号(Notation)

假设一自然语言处理(NLP)问题:语音识别系统。给定输入数据xxx,想要一个序列输出yyy,使得输入的每个单词都对应一个输出值。用x<t>x^{<t>}x<t>y<t>y^{<t>}y<t>索引输入和输出数据序列的中间位置,ttt意味着是时序序列,但不论是否是时序序列,都用ttt来索引序列中的位置。用TxT_xTxTyT_yTy表示输入和输出序列的长度,此处Tx=TyT_x=T_yTx=Tyx(i)<t>x^{(i)<t>}x(i)<t>表示训练样本iii的序列中第ttt个元素,Tx(i)T_x^{(i)}Tx(i)表示第iii个训练样本的输入序列的长度。对于yyy同理。

如果遇到了不在词表中的单词,需要创建一个新的标记,也就是一个Unknow Word伪造单词,用<UNK>作为标记,来表示不在词表中的单词,

1.3循环神经网络(Recurrent Neural Network Model)

了解了用来定义序列学习问题的符号。现讨论怎样才能建立一个模型,建立一个神经网络来学习XXXYYY的映射。使用标准的神经网络并不好,主要有两个问题:

  • 输入和输出数据在不同的例子中可以有不同的长度
  • 标准神经网络并不共享从文本的不同位置上学到的特征

循环神经网络(RNN)就没有以上两个问题。在每一个时间步中,循环神经网络传递一个激活值到下一个时间步用于计算。零时刻需要构建一个激活值a<0>a^{<0>}a<0>,通常用零向量作为伪激活值,或者随机用其他方法初始化。循环神经网络是从左向右扫描数据,同时每个时间步的参数也是共享的。用WaxW_{ax}Wax表示管理着从x<1>x^{<1>}x<1>到隐藏层的连接的一系列参数,每个时间步使用的都是相同的参数WaxW_{ax}Wax。而激活值也就是水平联系是由参数WaaW_{aa}Waa决定的,同时每一个时间步都使用相同的参数WaaW_{aa}Waa,同样的输出结果由WyaW_{ya}Wya决定。
在这里插入图片描述
在RNN中,比如在预测y^<3>\hat y^{<3>}y^<3>时,不仅要使用x<3>x^{<3>}x<3>的信息,还要使用来自x<1>x^{<1>}x<1>x<2>x^{<2>}x<2>的信息。所以RNN的缺点是它在某一时刻的预测仅使用了序列之前的输入信息并没有使用序列中后部分的信息。双向循环神经网络(BRNN)会处理这个问题。

RNN过程:首先输入a<0>a^{<0>}a<0>,接着进行前向传播过程,在ttt时刻:a<t>=g1(Waaa<t−1>+Waxx<t>+ba)=g(Wa[a<t−1>,x<t>]+ba)a^{<t>}=g_1(W_{aa}a^{<t-1>}+W_{ax}x^{<t>}+b_a)=g(W_a[a^{<t-1>},x^{<t>}]+b_a)a<t>=g1(Waaa<t1>+Waxx<t>+ba)=g(Wa[a<t1>,x<t>]+ba)

y^<t>=g2(wyaa<t>+by)=g(Wya<t>+by)\hat y^{<t>}=g_2(w_{ya}a^{<t>}+b_y)=g(W_ya^{<t>}+b_y)y^<t>=g2(wyaa<t>+by)=g(Wya<t>+by)

waxw_{ax}wax第一个下标a表示用来计算某个a类型的变量,第二个下标表示waxw_{ax}wax要乘以某个x类型的量。RNN用的激活函数经常是tanh,有时候也会用ReLU。选用哪个激活函数取决于输出,如果是一个二分问题,会用sigmoid函数,如果是类别分类问题,可以选用softmax作激活函数。RNN前向传播示意图:
在这里插入图片描述

1.4通过时间的方向传播(Backpropagation through time)

在编程框架中实现循环神经网络时,编程框架通常会自动处理反向传播。现介绍反向传播原理:
在这里插入图片描述
定义一个元素的损失函数为标准逻辑回归损失函数(交叉熵损失函数Cross Entropy Loss):L<t>(y^<t>,y<t>)=−y<t>logy^<t>−(1−y<t>)log(1−y^<t>)L^{<t>}(\hat y^{<t>},y^{<t>})=-y^{<t>}log\hat y^{<t>}-(1-y^{<t>})log(1-\hat y^{<t>})L<t>(y^<t>,y<t>)=y<t>logy^<t>(1y<t>)log(1y^<t>)

定义整个序列的损失函数:L(y^,y)=∑t=1TxL<t>(y^<t>,y<t>)L(\hat y,y)=\sum^{T_x}_{t=1}L^{<t>}(\hat y^{<t>},y^{<t>})L(y^,y)=t=1TxL<t>(y^<t>,y<t>)

反向传播算法需要在相反的方向上进行计算和传递信息,最终做的就是把前向传播的箭头都反过来,在这之后就可以计算出所有合适的量,然后就可以通过导数相关的参数,用梯度下降法来更新参数。

在反向传播过程中,最重要的信息传递或递归运算就是从右到左的运算。对于前向传播需要从左到右进行计算,时刻ttt不断增加。而对于反向传播需要从右到左进行计算,就像时间倒流,穿越时光。

在这里插入图片描述

1.5不同类型的循环神经网络(Different types of RNNs)

并不是所有的RNN都满足Tx=TyT_x=T_yTx=Ty,关于RNNs类型的总结:
在这里插入图片描述

  1. “一对一”的结构,当去掉a<0>a^{<0>}a<0>时它就是一种标准类型的神经网络。
  2. “一对多”的结构,如音乐生成或者序列生成。
  3. “多对一”的结构,如情感分类的例子,首先读取输入,一个电影评论的文本,然后判断他们是否喜欢电影。
  4. “多对多”的结构,如命名实体识别,其中Tx=TyT_x=T_yTx=Ty
  5. “多对多”结构的其他版本,如机器翻译这样的应用,TxT_xTxTyT_yTy就可以不同了。

参考博客:Andrej Karpathy. The Unreasonable Effectiveness of Recurrent Neural Networks《循环神经网络的非理性效果》

1.6语言模型和序列生成(Language model and sequence generation)

利用语言模型会得出某个特定的句子或某个句子序列中各个单词出现的概率。对于语言模型来说,用yyy来表示输入的文本序列比用xxx表示更好。

使用RNN建立语言模型首先需要一个训练集,包含一个很大的英文文本语料库(corpus)或者其它想用于构建模型的语言的语料库。语料库是自然语言处理的一个专有名词,指数量众多的英文句子组成的文本。

第一件事要将输入的句子标记化,建立一个字典将每个单词都转换成对应的one-hot向量,也就是字典中的索引。要定义句子的结尾,一般做法是增加一个额外的标记叫做EOS,表示句子的结尾。EOS标记可以被附加到训练集中每一个句子的结尾,如果想把句号或者其他符号也当作标志,可以将句号也加入字典中。如果训练集中有一些词并不在字典里,可以把UNK作为代表未知词的标志,只针对UNK建立概率模型,而不是针对这个具体的不在字典里的词。

完成标识化过程后,意味着输入的句子都映射到了各个标志上,或者说字典中的各个词上。下一步要构建一个RNN来构建这些序列的概率模型。过程如下图:
在这里插入图片描述

1.7对新序列采样(Sampling novel sequences)

在训练一个序列模型之后,要想了解到这个模型学到了什么,一种非正式的方法就是进行一次新序列采样,过程如下:
在这里插入图片描述
第一步要做的是对想要模型生成的第一个词进行采样,于是输入x<1>=0,a<0>=0x^{<1>}=0,a^{<0>}=0x<1>=0,a<0>=0,第一个时间步得到的是所有可能的输出是经过softmax层后得到的概率,然后根据softmax的分布进行随机采样。Softmax分布的信息是第一个词aaa的概率是多少,第一个词是aaron的概率是多少,第一个词是zulu的概率是多少,第一个词是UNK(未知标识)的概率是多少,然后对这个向量使用np.random.choice,来根据向量中这些概率的分布进行采样,这样就能对第一个词进行采样了。之后再进行下一个时间步,一直将采样进行下去直到达到所设定的时间步。

根据需求还可以构建基于字符的RNN结构,优点就是不必担心会出现未知的标识,缺点是最后会得到太多太长的序列。基于字符的语言模型在捕捉句子中的依赖关系也就是句子较前部分如何影响较后部分不如基于词汇的语言模型那样可以捕捉长范围的关系,并且基于字符的语言模型训练起来计算成本比较高昂。

以上就是基础的RNN结构和如何去建立一个语言模型并使用它,对于训练出的语言模型进行采样。在训练RNN时会存在梯度消失问题需要建立更加强大的RNN模型,如GRU,也就是门控循环单元和LSTM长期记忆网络模型。

1.8带有神经网络的梯度消失(Vanishing gradients with RNNs)

RNN首先从左到右前向传播,然后反向传播。但反向传播会很困难,因为同样的梯度消失的问题,后面层的输出误差很难影响前面层的计算。RNN不擅长处理长期依赖的问题。如果有梯度爆炸问题,一个解决方法是用梯度修剪。梯度修剪就是观察梯度向量,如果它大于某个阈值,缩放梯度向量,保证它不会太大,这就是通过一些最大值来修剪的方法。

1.9GRU单元(Gated Recurrent Unit)

门控循环单元改变了RNN的隐藏层,使其可以更好地捕捉深层连接,并改善了梯度消失问题。
在这里插入图片描述
GRU单元有个新变量称为ccc,代表细胞(cell),即记忆细胞。记忆细胞的作用是提供了记忆的能力。在ttt时间处,有记忆细胞c<t>c^{<t>}c<t>,GRU实际上输出了激活值a<t>=c<t>a^{<t>}=c^{<t>}a<t>=c<t>,在每个时间步,用一个候选值c^<t>\hat c^{<t>}c^<t>重写记忆细胞替代c<t>c^{<t>}c<t>的值。c^<t>=tanh(Wc[c<t−1>,x<t>]+bc)\hat c^{<t>}=tanh(W_c[c^{<t-1>},x^{<t>}]+b_c)c^<t>=tanh(Wc[c<t1>,x<t>]+bc)

在GRU中真正重要的思想是有一个更新(update)门Γu\Gamma _uΓu,是一个0到1之间的值。Γu=σ(Wu[c<t−1>,x<t>]+bu)\Gamma _u=\sigma(W_u[c^{<t-1>},x^{<t>}]+b_u)Γu=σ(Wu[c<t1>,x<t>]+bu)

Γu\Gamma _uΓu的作用是决定什么时候用c^\hat cc^更新ccc这个等式。c<t>=Γu∗c^<t>+(1−Γu)∗c<t−1>c^{<t>}=\Gamma _u*\hat c^{<t>}+(1-\Gamma _u)*c^{<t-1>}c<t>=Γuc^<t>+(1Γu)c<t1>

元素对应的乘积做的就是告诉GRU单元哪个记忆细胞的向量维度在每个时间步要做更新,可以选择保存一些比特不变,而去更新其他的比特。如果更新值,Γu=1\Gamma _u=1Γu=1c<t>=c^<t>c^{<t>}=\hat c^{<t>}c<t>=c^<t>;如果不更新用旧的值,Γu=0\Gamma _u=0Γu=0c<t>=c<t−1>c^{<t>}=c^{<t-1>}c<t>=c<t1>,这非常有利于维持细胞的值,就不会有梯度消失的问题了,因此允许神经网络运行在非常庞大的依赖词上。

激活值aaa的维度 = c<t>c^{<t>}c<t>的维度 = c^<t>\hat c^{<t>}c^<t>的维度 = Γu\Gamma _uΓu的维度

完整的GRU单元:增加一个代表相关性(relevance)的门Γr\Gamma _rΓrΓr\Gamma _rΓr门告诉我们计算出的下一个c<t>c^{<t>}c<t>的候选值c^<t>\hat c^{<t>}c^<t>c<t−1>c^{<t-1>}c<t1>有多大的相关性。计算这个门需要新的参数矩阵WrW_rWrc^<t>=tanh(Wc[Γr∗c<t−1>,x<t>]+bc)\hat c^{<t>}=tanh(W_c[\Gamma _r *c^{<t-1>},x^{<t>}]+b_c)c^<t>=tanh(Wc[Γrc<t1>,x<t>]+bc)

Γr=σ(Wr[c<t−1>,x<t>]+br)\Gamma _r=\sigma(W_r[c^{<t-1>},x^{<t>}]+b_r)Γr=σ(Wr[c<t1>,x<t>]+br)

参考文献:(Chung J, Gulcehre C, Cho K H, et al. Empirical Evaluation of Gated Recurrent Neural Networks on Sequence Modeling[J]. Eprint Arxiv, 2014.
Cho K, Merrienboer B V, Bahdanau D, et al. On the Properties of Neural Machine Translation: Encoder-Decoder Approaches[J]. Computer Science, 2014.)

1.10长短期记忆LSTM(Long short term memory)

GRU(门控循环单元)能够在序列中学习非常深的连接。LSTM即长短时记忆网络也可以做到,甚至比GRU更有效。在这里插入图片描述
在LSTM中没有a<t>=c<t>a^{<t>}=c^{<t>}a<t>=c<t> c^<t>=tanh(Wc[a<t−1>,x<t>]+bc)\hat c^{<t>}=tanh(W_c[a^{<t-1>},x^{<t>}]+b_c)c^<t>=tanh(Wc[a<t1>,x<t>]+bc)

Γu=σ(Wu[a<t−1>,x<t>]+bu)\Gamma _u=\sigma(W_u[a^{<t-1>},x^{<t>}]+b_u)Γu=σ(Wu[a<t1>,x<t>]+bu)

LSTM没有相关门,增加了遗忘门(the update gate)和输出门(the output gate):Γf=σ(Wf[a<t−1>,x<t>]+bf)\Gamma _f=\sigma(W_f[a^{<t-1>},x^{<t>}]+b_f)Γf=σ(Wf[a<t1>,x<t>]+bf)

Γo=σ(Wo[a<t−1>,x<t>]+bo)\Gamma _o=\sigma(W_o[a^{<t-1>},x^{<t>}]+b_o)Γo=σ(Wo[a<t1>,x<t>]+bo)

则记忆细胞的更新值为:c<t>=Γu∗c^<t>+Γf∗c<t−1>c^{<t>}=\Gamma _u*\hat c^{<t>}+\Gamma _f*c^{<t-1>}c<t>=Γuc^<t>+Γfc<t1>

a<t>=Γo∗c<t>a^{<t>}=\Gamma _o*c^{<t>}a<t>=Γoc<t>

在这里插入图片描述
只要正确设置了遗忘门和更新门,LSTM相当容易把c<0>c^{<0>}c<0>的值一直往下传递到右边,比如c<3>=c<0>c^{<3>}=c^{<0>}c<3>=c<0>。这就是为什么LSTM和GRU擅长于长时间记忆某个值。

最常用LSTM版本是门值不仅取决于a<t−1>a^{<t-1>}a<t1>x<t>x^{<t>}x<t>,有时候也可以偷窥一下上一个记忆细胞c<t−1>c^{<t-1>}c<t1>的值,这叫做“窥视孔连接”(peephole connection),然后“偷窥孔连接”就可以结合这三个门(Γu、Γf、Γo\Gamma _u、\Gamma _f、\Gamma _oΓuΓfΓo)来计算了。

GRU的优点是更加简单,更容易创建一个更大的网络,而且它只有两个门,在计算性上也运行得更快,可以扩大模型的规模。但LSTM更加强大和灵活,因为它有三个门。LSTM是个更优先的选择,但越来越多的团队也正在使用GRU,因为它更加简单,而且还效果还不错,更容易适应规模更加大的问题。

LSTM完整过程
前向传播:
在这里插入图片描述
反向传播计算:
门求偏导:
在这里插入图片描述
参数求偏导:
在这里插入图片描述
最后,计算隐藏状态、记忆状态和输入的偏导数:
在这里插入图片描述
参考文献:Hochreiter S, Schmidhuber J. Long Short-Term Memory[J]. Neural Computation, 1997, 9(8):1735-1780.

1.11双向神经网络(Bidirectional RNN)

双向RNN模型在序列的某点处不仅可以获取之前的信息,还可以获取未来的信息。
在这里插入图片描述
双向循环神经网络的基本单元不仅仅是标准RNN单元,也可以是GRU单元或者LSTM单元。很多的NLP问题,对于大量有自然语言处理问题的文本,有LSTM单元的双向RNN模型是用的最多的。双向RNN网络模型的缺点是需要完整的数据的序列,才能预测任意位置。对于很多自然语言处理的应用,如果总是可以获取整个句子,标准的双向RNN算法实际上很高效。

1.12深层循环神经网络(Deep RNNs)

要学习非常复杂的函数,需要把RNN的多个层堆叠在一起构建更深的模型。
在这里插入图片描述

错题

7.关于GRU
在这里插入图片描述

Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐