zoukankan      html  css  js  c++  java
  • SIGAI深度学习第十三集 循环神经网络2

    讲授LSTM、GRU、双向循环神经网络、序列标注问题、CTC、seq2seq、编码器解码器结构等.

    大纲

    面临的问题
    长短期记忆模型
    门控循环单元
    双向循环神经网络
    序列标注问题
    连接主义时序分类
    序列到序列学习

    本集内容简介

    今天主要讲RNN它面临的一些问题以及改进的措施和方案,我们会分下面几个主题进行讲述,首先介绍RNN面临的问题,主要是梯度消失问题,再接下来介绍两种典型的解决方案,即LSTM和门控循环单元GRU,这两种它解决问题的套路都是类似的,把连续的乘法转成加法来执行,然后这种思想在我们前边介绍RNN的时候已经讲过了,其实当时我们讲的高度公路网络Highway wetwork和它们是同一套思路来解决问题的。

    再接下来会介绍RNN的一个重要改进,叫双向循环网络,前边我们介绍的RNN它都是输入x1,...,xT的,在处理xt的时候只用了x1~xt-1,在某些场景中未来的这些信息也是有用的,因此诞生了双向循环神经网络这种网络结构。

    再接下来介绍RNN它解决了最根本性的一类问题,即序列标注问题,在RNN的基础之上出现两种非常重要的改进,已经大规模使用了,即连接主义时序分类CTC,是现在我们语音识别主流的方案,第二种是序列到序列的学习Seq2seq,这是解决诸如机器翻译、NLP问题的一种经典的手段。

    梯度消失问题

    前边在介绍全连接神经网络以及RNN的时候都讲过一个重要的问题是梯度消失与梯度爆炸问题,尤其是梯度消失问题更为严重,梯度消失产生的根本原因是反向传播时根据δ(l+1)算δ(l)的时候乘以激活函数的导数值f',如果这个导数值非常小的话,越乘的话这个δ越往前传就越小,最后我们参数值的梯度值根据δ来构造,因此损失函数对权重的梯度值它最后会趋向于0,这样的话用梯度下降法就没法更新了,在RNN中这个问题也非常严重,而且严格来说会更严重一些。

    首先看循环层递推公式

     根据这个递推公式,按时间进行展开后为

     ht展开到ht-2就要乘两次Whh,即t-2时刻隐藏层状态值传到t时刻的时候它要乘两次Whh,如果要把激活函数拿掉的话,全是矩阵乘法和加法运算,最后化简以后,我们的ht展开到h1的时候

     ,这是同样一个矩阵乘这么多次,这个问题就很严重了,这就类似于乘以一个αt-1*h1一样,若α小于1,那结果会很快衰减到0上边去,因此很早时刻的隐藏层状态值h传播到很晚时刻的话它基本上就不起作用了,趋向于0了。

    假设Whh可以对角化,即可以特征值分解,存在P,使得P-1AP=Λ,P是一个正交矩阵即P-1=PT,则W=QΛQT,因为W的T次方可以展开为

     如果特征值很小的话就导致Λ的T次方趋向于0了,正向传播的时候信息就传不动了,很远时刻的h传到很晚时刻的h上边的话它就趋向于0了,这样的话记录了以前的值就相当于没有记录,因为以前的值它在t时刻被利用起来的时候已经趋向于0了。

    而在反向传播的时候问题也更严重了,根据δt+1算δt的时候它每次都要乘以一个WhhT,他还要乘以一个激活函数的导数值f'(ut),光f'(ut)就可以引起梯度消失问题,再加上WhhT,问题就更严重了,因此我们RNN它是很难记住长时间的一个信息的,正向传播的时候很难记忆,反向传播的时候梯度值也没法更新,这就是它的梯度消失问题。

     解决思路

     我们分析一下这个问题的原因,正向传播的时候反复乘以Whh,然后反向传播的时候要乘以WhhT还乘以f',f'在前边人工神经网络里边已经介绍过了,这个是老问题了,而Whh引起的是一个新问题,那怎么解决新问题呢?老问题我们可以通过激活函数以及跨层连接等手段来解决,但是新的问题该怎么解决呢?

    我们想一下,避免做这样的反复乘同样一个矩阵,我们能不能把这样一个乘法改成加法,其实是可以的,目前主流的方案就是LSTM和GRU都采用了这种做法,避免了矩阵的每次乘积这种运算,那么接下来我们就看LSTM它是怎么做的。

    LSTM

    前边我们说了LSTM,由Schmidhuber等人在1997年提出的,这个人也被称为LSTM之父,它对整个深度学习的影响还是挺大的,因为后边的语音识别、NLP里边我们来建深层的RNN的话即建DNN的话,都需要依赖这种技术才能把网络建的比较深,它怎么做的呢?

    标准的RNN循环层是ht=f(WxhXt+Whhht-1+bn),LSTM他把这个映射改了,

     Ot是一个称为输出门的东西,Ct是一个像细胞单元的记忆值,Ot、Ct做逐元素相乘构成ht,Ot相当于控制Ct中的信息有多大的比例能流到ht里面去形成真正的输出值,Ot称为输出门,

     输出门它是根据Xt、ht-1构造的,这个式子和ht=f(WxhXt+Whhht-1+bn)非常接近,但是这里的σ表示sigmoid函数,因为sigmoid函数它的输出值是在0到1之间的,因此Ot它就表示一个比例,即记忆值Ct每个分量有多大的比例能输出到我们的最终的输出值ht里边去,Wxo、Who、bo这些都是输出门的参数,通过训练得到的。

    记忆值Ct的计算是上层的记忆值和本次加入的新的值来计算的,

     本次的新值是标准的RNN它的映射函数即tanh(WxcXt+Whcht-1+bc),Ct是两个东西加权和一下,ft是遗忘门,it是输入门,Xt是新输入的信息,而Ct-1是老的记忆值,Xt和上次隐含层输出值ht-1综合一下即我们标准的RNN产生的输出值ht,然后再逐元素和输入们it相乘,ft、it都控制了各自的向量有多大的比例能流到新的信息里边去,ft、it它们每个分量的值都是0到1之间的,表示每个分量有多大的比例流出来,遗忘门、输入门都是通过sigmoid函数算出来的,值都在0到1之间,刚好满足我们对比例的要求,

     

     ft、it的计算都各自有权重矩阵、偏置项,和前边的输出门也一样的,这些参数都是通过训练得到的。

    所以说,归纳起来正向传播就是利用上一层的记忆值乘以一个遗忘门加上本次新来的值乘以一个输入门,构造出了一个记忆值,记忆值再经过0到1之间的数即输出门再控制一下,得到最终的输出值ht,这样的话由ht-1、Xt构造ht的公式就比之前复杂很多的,它正向传播是这样做的。

    反向传播公式可以根据刚才讲的几个公式把它推导出来,和前边的BPTT算法是一样的推导过程,这里就不讲了。

    归纳起来看,我们的长短期记忆模型LSTM就是通过输入门、遗忘门、输出门来控制信息的比例,然后用ht-1来计算ht,构造了一种新的计算公式,这里面有一个重要的东西ct表示记忆值,其实就是类似于我们之前的ht那样一个东西。

    LSTM总结

    再总结一下,输入门作用于当前时刻的输入值类似于之前的Xt,遗忘门作用于之前的记忆值类似于ht-1,它们共同作用加权和,经过一个输出门成比例缩放一下产生ht,这三个门都是通过sigmoid计算出来的,它们都有各自的W、h,都是通过训练得到的。

    那我们就想一下LSTM它为什么一定程度上避免了梯度消失问题呢?

    它是把以前乘的Whh的T次方变成了一系列的加法,信息通过了一些加法运算从前边时刻往后传,它就是避免了反复乘一个矩阵越乘越小的问题,而是加法合成前边那些时刻的信息,前边那些时刻的信息总有一定的比例通过加法传过来而非通过乘法不停的衰减它。

    GRU

    下面我们来介绍第二种方案,叫门控循环单元GRU,它也是通过门来控制信息的流动的,和LSTM是非常类似的,但是GRU只用了两个门,它把LSTM的输入门和遗忘门合并成了更新门,

     ,也是通过sigmoid函数映射的,它是用来控制之前的记忆值Ct的。

    看记忆值Ct是怎样更新的,

     Xt是当前时刻引入的新的信息,后边是老的信息ht-1通过重置门衰减一下,

     然后根据ht-1和Ct来构造ht,

     双向循环神经网络简介

    双向循环神经网络是一种非常重要的结构,前边我们说了RNN它依次把X1,...,XT送到神经网络里边去预测,循环层会输出h1,...,hT,最后输出层会输出y1,...,yT,这是在输出yt的时候,它用的是x1~xt所有的信息,但是对有些应用不光要用t之前的信息还要利用t之后的信息,比如说NLP里边就有这种需求,即中间这个词的含义不光要看句子中前边的内容还要看句子中后边的内容即所谓的上下文上和下都是要的,因此需要我们的RNN在处理t时刻的Xt的时候,它不光利用x1~xt-1它还要利用xt+1~xT,怎么完成这个任务呢?我们的双向循环神经网络他就可以用来实现这种功能,它是用了两个循环层分别对正向的数据x1~xT以及反向送过来的数据xT~x1进行处理,然后再把结果综合起来形成的。接下来就看它具体是怎么做的。

    预测算法

    下面来看双向循环神经网络是怎么工作的,标准的RNN它有一个输入层,然后依次输入训练数据x1,...,xT,然后经过循环层得到h1,...,hT,而双向循环神经网络它里边有两个并列的循环层,出了一个标准的循环层以外它还有一个反向的循环层,反向循环层是把这些输入的数据按照反向的顺序送到该循环层里边去处理,即xT,...,x1这样依次送进来,这样也得到一组h1,...,hT

    这两个并列的循环层它们同时进行处理,第一个正向接受输入第二个反向接受输入,最后把他们处理完了,把左边的h1和右边的h1合并起来形成真正的h1,左右h2合并成真正的h2,...,一直到T为止,这些真正的ht作为最终的h再送到输出层里边去处理。这个说法有点抽象,接下来看具体是怎么做的。

    双向网络的简单例子

    前边说的有点抽象,下面用一个实际的例子来演示一下双向循环神经网络他到底是怎么运行的。

    假设一个序列长度为4的样本,隐含层输出值h1->,h2->,h3->,h4->汇总的是过去的信息,比如说h3->它汇总的就是x1、x2、x3的信息。

    同时把样本反序x4、x3、x2、x1输入到第二个循环层里边去,依次得到<-h4,<-h3,<-h2,<-h1,这里<-h3汇总的是x4、x3的信息。

    接下来就把左边的hi和右边的hi合并起来,比如说对h3来讲,左边的h3->代表从x1、x2、x3的信息,右边的<-h3代表的是x4、x3的信息,它们两个拼起来就完整了,从x1到x4的信息都被利用起来了,也就是说左边的代表是过去、右边的代表未来的信息,左边的hi代表1~i时刻,右边的hi代表的是i~T时刻的信息,因此合并后的hi它就完整的包含了整个序列的信息,过去和未来的信息都利用了,上下文信息都利用了。

    最后把hi以任意的顺序送入输入层里边去输出就可以了,得到我们最终的输出值,这里边根据每个hi算出来的yi代表了整个句子序列它完整的信息,比他早的时刻比它晚的时刻都利用上了。

    序列标注问题

    下面介绍一下RNN在序列预测问题中的应用,RNN最标准的形式实现的是从序列到序列的映射,输入是一个向量序列,输出也是一个向量序列,每一个时刻它的输入值对应一个输出值,以这种标准的映射关系为基础,我们可以构造出各种不同的解决序列预测问题的方法。

    序列预测问题它要求输入是一个序列数据x1,...,xT这样一个长度为T的向量的序列,输出可以是多样化的,我们可以把整个序列映射为一个向量,比如说用来做分类,整个输入序列可以是一篇文章,我们可以把它分成各种类型,如正面的文章、负面的文章二分类问题,输出就是一个二分类的向量结果,另外我们的输出的值可以不是一个向量,可以是一个向量的序列,输入序列和输出序列长度可以不相等,哪个大哪个小都是允许的。

    在序列预测问题中有一类非常重要的问题叫序列标注问题,它是指将一个序列数据映射成离散的标签值序列任务,也就是说输出值y1,...,yT它是一组标签值,也就是每一时刻的输出值yi它都是一个标签向量,它的本质就是根据这样一个上下文信息输入的一段话或一段声音进行预测,比如说把这段声音信号翻译成文字,那就是语音识别,然后把这段话做一个摘要或者分类那也是序列标注问题,整个序列标注问题它在机器嘘唏里边是非常重要的一类问题,而我们典型的实际应用时的序列标注问题有语音识别、机器翻译、NLP中的词性标注等问题,后边我们会详细解释。

    和普通的模式识别分类问题相比,序列标注问题有一个区别就是,普通的模式识别问题分类问题它输入就是一个向量X(可以把图像转化成一个向量也是可以的),输入向量X是指一个序列数据,有多个向量x1,...,xT构成的,并且xi和xj之间还存在相关性,同样的输出的序列数据之间也可能存在相关性,比如说语音识别,识别出来的一句话它里面由多个字或词构成的,这些字之间它们肯定是有关系的,它是要满足语法规则和语义上下文信息的。

    训练标注问题它的一个困难在于输入序列和输出序列的对齐关系是未知的,也就是我们输入是x1,...,xT这么多个数据,输出是y1,...,yT‘这么多个数据, 然后x对应哪些y这我们事先是不知道的,以语音识别为例,我们输入的是一段波形信号,然后这个波形里边哪一段从第几秒到第几秒它代表的是某个字,我们事先是不知道的,因此我们输入序列和输出序列他们之间各个向量的对应关系我们是不知道的,因此这个问题给我们的很多实际应用带来挑战。后边我们会介绍怎么来解决这样的问题。

    序列标注问题面临的问题

    下面来介绍RNN它在处理序列标注问题时面临的问题。第一个问题时标准的RNN它是单向的,即x1,...,xT依次输入的,每得到一个yi的时候它利用的是x1,...,xi这段的信息,未来的信息它没有利用,而有些实际应用,比如机器翻译,他要理解某个词xi,它不光和过去的词有关,还和未来的词有关,因此我们要利用未来的信息,这个问题好解决,我们的双向RNN就可以解决这样的问题。

    另外一个问题,我们的输出序列和输入序列之间是要对齐的,对于RNN,每给一个xi的输入我们要对应一个yi的输出,而在很多应用比如语音识别里边,这种是没法一一对应的,我们不知道它的对应关系即哪些xi对应哪些yi我们是不知道的,解决这个问题有一种通用的做法叫连接主义时序分类CTC,一会会重点介绍这种方法的原理。

    序列标注问题分类

    典型的序列标注问题,按照输入序列和输出序列的对应关系,我们可以把它分成三种类型。

    第一种类型称为序列分类问题,即输入一个序列x1,...,xT,我们给它一个类别标签输出一个类别标签向量,如它是第几类的,这种属于序列分类问题,就是把T个向量预测出一个向量值出来,因此输出序列长度为1。

    第二类分类问题是段分类问题,就是输入序列要预先的分成几段,每一段为一个序列,比如说x1~x3是第一段,x4~x6是第二段,我们要为每一段赋予一个标签值,也就是把每一段映射为一个向量,那我们输出向量的序列长度肯定是小于等于输入向量的序列长度的,段分类问题就是我们把一个输入序列切割成很多段,每一段我给它预测出一个向量出来,这种就成为段分类问题,典型的例子就是语音识别,我们把声音信号切分成很多个字,每个字就是你识别出来的结果。

    第三类问题更复杂,就是时序分类问题,这类问题,输入是一个序列输出也是一个序列,他们之间没有任何对齐关系,比如说输入是x1,...,xT,输出值y1可能包含的是后边那些词如x5、x6的信息,如机器翻译就是这样的,即中英文词的顺序是不一样的。

    我们重点来看这几个问题是怎么解决的。

    CTC简介

    接下来介绍连接主义时序分类CTC它的原理。前边我们说RNN它每输入一个xi它就输出一个yi,这样的话它最原始的输出序列长度和输入序列长度是一一对应的。

    用来做预测比如做语音识别,我们就需要把一段语音信号切分好,要知道一个字它的起始位置和终止位置(实际是按一个发音单位如声母韵母来划分的),这是不现实的,现实应用的时候,我们很难把它分割准确,因此我们不能指望事先把我们的序列分割好,然后对每个序列向量预测出一个向量,这是不现实的,那怎么办呢?我们还得用循环神经网络,RNN每输入一个值肯定会预测出一个值来,其实这个问题还挺好解决的,我们把语音信号切分的足够细,比如说每段是20ms,它肯定不会长过一个词/字,也就是一个字/词它是由很多段这样的基本单位构成的,那我们就以这个基本单位算一下它的特征出来作为向量,输入这样一些向量序列,然后每个单位他会预测一个输出值出来,然后我们把相邻的这些值合并掉就可以了,相邻几段预测出的都是同一个字,那把它们合并掉就可以了。

    但是还有一个问题,就是有些时候是不需要你输出值的,比如说语音信号空白的区域即静音状态,这段空白也要求输出出来,这怎么办呢?我们在输出的标签向量里面规定一种新的向量出来即空白值,这种问题就解决了。

    所以说CTC就是这样解决问题的,它第一个就是引入了空白符号,表示此时神经网络输出的是无意义的值,就像静音状态一样,另外用了一个映射函数,将带有空白符号以及识别出来的其他类型的原始输出序列即神经网络最原始的输出序列y1,...,yT,对它进行处理,把空白符去掉,把相邻的相同的合并掉,这样就形成了我们最后语音识别的结果了,这就是CTC解决问题的核心想法。

    CTC原理

    下面我们来介绍CTC它具体的原理,它的输入是一个m维的向量的序列,X=(Rm)*,*表示有大于等于1个(即x1,...,xT,T≥1的向量序列),它的输出值也是一个序列,是由L这个词典向量构成的序列,Z=L*,L一般采用one-hot编码形式,总之输入序列为x=(x1,...,xT),输出序列为z=(z1,...,zU),这里有条件即U≤T,输出序列的长度小于等于输入序列的长度,输出序列长度与输入序列长度就可能不相等,因此无法用先验知识将他们对齐,即让输出序列的某些元素和输入序列的某一个元素对应起来,因此CTC的目标就是训练一个时序分类器h:X->Z,根据X预测出Z来,如果我们用RNN来完成这个任务的话,关键一步就是将RNN最原始的输入值(x1,...,xT)的输出值(y1,...,yT)映射为真实的标签值(z1,...,zU),那CTC是怎么做的呢?

    它其实就是寻找h:X->Z这样一个映射分类器,使得在出现X的条件下出现Z的概率是最高的,那么这个Z就是我们识别出来的那个声音信号所代表的文字,这就是语音识别的结果。

    CTC它的基础就是一个RNN,这个RNN他的输出层就是softmax层,这个层是怎么设计的呢?如果标签字母集中的字母个数(可以理解为语音信号中的发音单位的个数)为|L|,则这一层有|L|+1(1是空白字符)个神经元,也就是说它的输出结果就表示属于|L|+1这每个类的概率值(前边|L|个分量表示属于那些字母的概率值,最后一个表示属于空白符的概率值),softmax层它每接收一个X(X1,...,XT)会产生一个输出值(y1,...,yT)这样一个概率向量,这个概率向量代表了我们最后分类的结果,我们可以以它来构造我们组后的分类结果。

    那我们看一下严格的定义,

    输入序列的长度为T,循环神经网络的输入数据为m维,输出向量为n维,(Rm)T->(Rn)T,(Rn)T是CTC中的循环神经网络它本身的输出值,然后我们就要用这个输出值来构造最后想要的结果,我们假设把这个网络映射写成y=Nw(X),X是输入序列,y是输出序列,w是我们网络的参数值,我们定义在t时刻,网络第k个输出单元的值为ykt,这里定义扩充后的输出符号集合L'=L∪{blank},则给定一个输入序列x1,...,xT,出现某一输出序列的概率值为

     ,将集合L'T中的元素称为路径(path),记为Π。

    定义一个多对一的映射,将神经网络的输出序列映射为最终需要的标签值序列B:L'T->L≤T,具体的做法是消除空白符和连续的重复标签值。得到的这个原始输出序列长度还是等于T的,我们要把L'T映射成L≤T,也就是把这里的空白符去掉,把相邻的相同的符号给合并掉,最后形成我们需要的标签序列,即做去重、去空白这样两样事情。

    接下来我们介绍B函数,前边我们说了序列x1,...,xT它先输出到RNN里边,得到预测结果y1,...,yT,我们要根据y1,...,yT经过去重和消空白符得到最后的结果,这个B函数就是用来去重和消空白符号的。举个例子,我们原始识别出来的这样一个序列a-ab-,这时候要把空白去掉把相同的合并,所以最后的结果是B(a-ab-)=aab,比如说预测出的另一个序列为-aa--abb,则经过B函数之后B(-aa--abb)=aab,这样的话和aab对应的可能路径就不止一个,而出现aab这种结果的概率等于所有出现的路径的概率求和,因此可以定义,对于一个输入序列x得到一个最终的序列L(不是我们预测出y的那个序列)它的概率的定义为

     ,通过B函数来构造的,把l映射回去得到所有的Π,这些Π对应的概率之和即为p(l|x)。

    这个东西就可以做语音识别,给定一段信号x,我们要估计每一句话出现的概率,我们把所有的话穷举出来,我们看哪一句话出现的概率最高,我们就把它识别为那句话就可以了。

    因此,CTC它是通过B函数构造最后的一个映射,然后把连续重复的去掉、把空白符也去掉,得到我们最后想要的结果l。就是根据x先用KNN映射出Y向量序列来,再把p经过B函数作用一下得到我们最后想要的标签l,而我们得到l的依据是我们要找概率最大的那个l即p(l|x)最大的那个l,p(Π|x)是我们原始的RNN的输出。

    在这里举一个实际的例子:

    直接计算概率p(l|x)显然不可能,因为能得到l的所有可能的Π太多。

    这里有两种方案:

    先计算概率最大的路径Π* ,然后用B对这个路径进行处理,得到最终的输出序列:h(x)≈B(Π*),其中Π*为条件概率最大的路径

     这种方法和最优标签序列并不一定是对应的。

    第二种方案为前缀搜索解码,通过使用前向后向算法,逐步的扩展输出的标签序列得到最优解。

    CTC的训练算法

    前边说了预测,预测的时候,先把x1,...,xT输到RNN里面去预测一下得到y1,...,yT,再通过B函数把相邻的重复值去掉把空白符去掉,这样得到我们最后的结果,用第一种方案找最大Π的方案,更复杂的是前缀编码搜索方案,大概就是这个原理,根据RNN原始的预测序列得到最后我们想要的标准的目标序列,这就是它的预测过程。

    接下来说一下它是怎么训练出来的,它训练的时候是不需要原始的预测序列进行标注的,它是直接有Z(最后想要的标签序列,即B函数映射后的结果)就可以了。

    输入是一个序列X输出是一个序列Z,然后让X的情况输出序列Z的概率最大化就可以了,其实就是对数似然概率,就是最大似然估计就可以了。

    因此我们的训练样本是由(X,Z)这样的样本构成的,输入序列X和对应的输出序列Z它们两个不需要对齐,我们算这样类似于交叉熵函数的极值就可以了,

     然后计算这个损失函数又要算p(l|x)这个东西了,前边我们说了,和一个标签序列l对应的所有可能的原始输出序列路径Π它有很多种,我们没法一一穷举,因此我们需要用一种高效的算法来解决这样一个问题,这里采用了动态规划的思想来计算这个概率值,这和HMM的估值问题是类似的,如果感兴趣可以查一下原始论文。

    seq2seq简介

    下面我们来说序列到序列学习这种技术,它其实是编码器解码器这种结构的一个实例, 前边我们介绍了很多这样的例子,比如说自动编码器它就是一个编码器解码器结构,然后前边讲RNN用来做图像分割FCN那里边也有编码器解码器这种结构,很多地方都有这种例子。

    seq2seq它是通过两个RNN来构造这样一个预测器,输入一个序列输出另外一个序列,它解决什么样的问题呢?对有些问题,它输入序列长度和输出序列长度不一定相等,并且我们事先并不知道输出序列长度有多大,典型的就是语音识别和机器翻译问题,尤其是机器翻译问题是seq2seq要解决的问题。

    标准的RNN没办法处理这种输入序列和输出序列长度不相等的情况,前边说了CTC虽然能处理输入序列和输出序列长度不相等的情况,但是他不能处理这种倒序的情况如机器翻译,因此就诞生了序列到序列这种技术。

    seq2seq它是基于RNN建立的一种框架,它能实现从一个输入序列到一个输出序列的映射,两个序列长度不相等,而且没有那种顺序的对应关系,也就是说x1对应y8、x2对应y1等这种交叉对应的情况它也是可以处理的。它是怎么做的呢?它是用两个RNN和起来完成这样一个从输入序列到输出序列的映射,用的是编码器解码器框架。

    编码器-解码器结构

     下面我们来介绍seq2seq它具体的结构,整个系统他用两个RNN组成,我们分别称为RNN1、RNN2,RNN1它充当编码器角色,RNN2它充当解码器角色,RNN1它依次接收每个时刻的输入向量x1,...,xT,输入完成加一个EOS表示一个句子或段落的结束,当送完最后一个输入向量以后它会得到hT,这时候RNN1的工作就结束了,hT它是汇总了x1,...,xT所有时刻的信息,因此这个hT称为语义向量,即汇总了所有输入序列的信息,hT也可以看成x1,...,xT所有向量序列的编码以后的结果,因此RNN1称为编码器,也就是说它把x1,...,xT这个序列压缩成hT这样一个向量了,代表了它们的含义。

    接下来就应该交给解码器来工作了,解码器RNN2它在每个时刻也会接收一个输入值z1,...,zT',它除了接收这样一个原本的输入以外,它还把hT也合并进来作为输入,也就是说把<zi, hT>合并在一起作为神经网络的输入,这很容易做到,把RNN的输入层加几个神经元,把hT接进来就可以了,也就是说它每次都利用了hT这个语义信息即浓缩了x1,...,xT精华的一段向量,然后作为RNN2解码器补充的输入,则输入变为y1,...,yT',其中包含z1,...,zT'和hT的信息,即包含了所有x1,...,xT的信息,一般yi就是softmax回归的结果即一个概率向量,可以认为是使用z1,...,zT'作为输入在最终输出结果中出现的概率值,yi一般是个概率值,zi是我们想要的结果。

    RNN2就是解码器,它依赖于hT,即上下文信息是x1,...,xT浓缩的结果,产生另外一个序列z1,...,zT',注意它不能直接产生这样一个序列,但它可以评估这个序列出现的概率值,因为每个时刻它输出的值是softmax回归的结果,就是p(yt),它连乘起来就表示z1,...,zT'这个序列它出现的概率值。

    可以简单的认为这个RNN2解码器它以RNN1编码器编码以后的向量作为输入,同时它还接收外边的其他输入,最后对输入结果进行一次打分评估,可以认为根据z1,...,zT'这样一个输入造出一个输出序列,不用概率也是可以的,这样就实现了从一个输入序列x1,...,xT到y1,...,yT'的这样一个映射,从一个输入序列造出了一个输出序列并且两个序列长度不相等,而且没有顺序的对应关系,也就是说y1它包含了整个x1,...,xT的信息的,因为hT是浓缩了x1,...,xT的所有的信息的,这刚好就符合我们的机器翻译或其他那种由一个输入序列造出另外一个长度不等的输出序列,并且输出序列的每一个向量他都包含了输入序列每个向量的信息,这样一种应用场景的要求。

    seq2seq的预测算法

    因此在seq2seq这种框架里边编码器的输入就是x1,...,xT,它依次接收这些输入向量作为输入,编码器网络最后时刻产生的状态是hT,作为x1,...,xT的编码值,就相当于读完这个句子理解了这个句子的含义就可以意会了,它包含了时刻1到T输入序列的所有信息,一般将hT简写为V,称为上下文向量或语音向量,解码器每个时刻的输入为V和yi,它输出的是y1,...,yT'概率值。

    因此RNN它实现了一个概率的映射,即已知一个x1,...,xT这个向量(其实最后把它浓缩为一个V了,然后输出在这种向量出现的情况下另外一个向量y1,...,yT'出现的条件概率,因此它是对一个条件概率进行建模,当然这是指RNN2最后一层是softmax层,这样它输出的就是一个概率值,因此它输出的就是给定一个向量序列的条件下另外一个不同长度的向量序列它出现的概率,

     在实现的时候,编码器和解码器同时训练,目标就是最大化上面的条件概率。

    seq2seq的训练算法

    前边我们说了seq2seq学习它可以根据一个输入序列得到一个输出序列,因此它训练的时候,训练样本也是成对的序列(A, B),训练的目标是让序列A编码之后解码得到序列B的概率最大,即最大化如下的条件概率类似于交叉熵损失函数:

     只要把该损失函数对编码器网络、解码器网络的参数θ它的梯度值算出来以后,就可以用反向传播算法进行迭代更新就OK了。

    这两个网络是同时训练出来的,这和训练标准的神经网络没有什么两样,只是把目标函数换成以上那种损失函数了。

    seq2seq的用法

    最后学习一个seq2seq是怎么用的,它第一种用法是打分,解码器网络它最后输出的就是这样一个条件概率pθ({yn}|{xn}),已知X这个序列经过第一个RNN映射以后得到V,以它为RNN2的初始隐含层的状态值,反复这样迭代,最后会得到yn这个序列的概率值,然后给定每一个yn,我们可以输出它的概率是多大,就像分类问题一样yn这个序列出现的概率有多大,这和前边的标准的RNN没什么两样,yn它就是序列就相当于x1,...,xT它出现的概率值有多大,只不过这时候循环层初始的状态值是用V来表示的。

    第二种用法是根据输入序列生成对应的输出序列,这种是更常用的一种用法,比如说机器翻译就是这种技术,而我们标准的seq2seq它只能估计pθ({yn}|{xn})这样一个概率出来,怎么找到概率值最大的yn呢?这里采用了一种叫集束搜索技术(其实就是前缀编码搜索技术),比如说机器翻译经过第一个RNN映射之后得到V,接下来我们选第一个词,从词典里面挑,比如说句子里的词直译古来的那些词列出来作为y1送到RNN2预测一把,得到一个概率值,接下来根据它输入预测y2,反复这样做扩展得到整个输出的句子序列。

    集束搜索具体是怎么做的呢?它是每次保留概率最大的k个序列,然后在它基础之上进行扩展,比如说我们翻译出来的目标序列,它只可能是以下三个单词{a,b,c},找第一个词,选择概率最大的k=2个序列的{a,b},选完以后扩展第二个词,扩展完以后共有{aa,ab,ac,ba,bb,bc}这么多种情况,这么多种情况里边算一下,ab、bb出现概率最大,那就保留这两种{ab,bb}(因为k=2,即保留概率最大的前两个序列),然后继续往后扩展,依次造出每个词来,最后得到我们翻译的结果。

    所以说seq2seq它就是算pθ({yn}|{xn})用的,如果把{xn}拿掉的话,其实就是我们单个的RNN,这个条件概率怎么体现的呢?它影响的是解码器网络,就是算yn概率的初始值,就是解码器隐含层的初始值设置成我们的xn理解后的语义指V这个向量,这就是seq2seq技术。

    本集总结

    首先讲了RNN面临的问题挑战,主要是梯度消失问题,接下来讲了几种解决这个问题的方法,有LSTM、GRU,它都是通过一些门单元来合成信息的,避免这样直接连乘矩阵计算。

    再接下来我们介绍了双向循环神经网络,这是一个非常重要的改进。

    然后介绍了时序分类问题即时序标注问题,它有三种类型的,分别是序列分类、段分类以及真正意义上的时序分类器。

    再接下来我们介绍了在标准的RNN上面构造的两种重要的技术,第一种是CTC连接主义时序分类,用来解决语音识别这样的问题的,它是通过让RNN原始预测出来的序列值做个加工,然后把空白符号去掉以及合并相邻相同非空白符号,导致最后出现我们想要的那种结果,另外一种是seq2seq技术,采用的是编码器解码器框架,一个RNN充当编码器,另一个RNN充当解码器,它们两个配合起来完成从一个序列到另外一个序列的映射,训练的时候两个是一起训练的,无论是CTC还是seq2seq你只要把它的损失目标函数写出来,我们就可以很方便的用我们的反向传播算法来求它对神经网络参数w的梯度值,然后完成网络的训练,seq2seq它只是估计一个概率值出来即p(Y|X),其实CTC也是一样的,即X序列出现的情况下标签序列L出现的概率有多大p(L|X),然后我们要得到概率最大的那个输出序列都要通过类似于BeamSearch集束搜索等这样的技术来得到的,所以说一定要理解这种技术的原理。

  • 相关阅读:
    powershell 模拟IE行为
    php获取post中的json数据
    转:实例学习PHP程序对用户身份认证实现两种方法
    PHP中间件ICE
    常用的PHP数据库操作方法(MYSQL版)
    PHP 读取网页文件
    解决当distinct和join同时存在distinct失效问题
    web test LoadRunner web_reg_find / jianchadian / flag / placeflag
    my ReadHealth / drink / body cancer
    my ReadFood_Nanking Delicious / food / delicious
  • 原文地址:https://www.cnblogs.com/wisir/p/12341279.html
Copyright © 2011-2022 走看看