【RNN】剖析RNN 之 从RNN-(Simple|LSTM) 到 序列生成 再到 seq2seq框架(encoder-decoder,或称为seq2seq)

前言

最近在搞一个多标签分类的项目,发现多标签分类问题中的多标签难点可以转化为序列生成问题(如下图,引自论文《Ensemble Application of Convolutional and Recurrent Neural Networks for Multi-label Text Categorization》[1]),论文中思想讲的很透彻,图也一目了然,但是RNN的具体实现上还是要自己搞清楚,因此这个思考过程整个从最简单的RNN到seq2seq都梳理了一个遍,特此记录。

【RNN】剖析RNN 之 从RNN-(Simple|LSTM) 到 序列生成 再到 seq2seq框架(encoder-decoder,或称为seq2seq)

为了清楚透彻,下面围绕上面这张图来展开。

RNN

先将上图简化为基本的RNN结构:

【RNN】剖析RNN 之 从RNN-(Simple|LSTM) 到 序列生成 再到 seq2seq框架(encoder-decoder,或称为seq2seq)

一目了然,【RNN】剖析RNN 之 从RNN-(Simple|LSTM) 到 序列生成 再到 seq2seq框架(encoder-decoder,或称为seq2seq)~【RNN】剖析RNN 之 从RNN-(Simple|LSTM) 到 序列生成 再到 seq2seq框架(encoder-decoder,或称为seq2seq)是输入序列,【RNN】剖析RNN 之 从RNN-(Simple|LSTM) 到 序列生成 再到 seq2seq框架(encoder-decoder,或称为seq2seq)~【RNN】剖析RNN 之 从RNN-(Simple|LSTM) 到 序列生成 再到 seq2seq框架(encoder-decoder,或称为seq2seq)是输出序列(有时我们只用到最后一个时序的输出【RNN】剖析RNN 之 从RNN-(Simple|LSTM) 到 序列生成 再到 seq2seq框架(encoder-decoder,或称为seq2seq),比如后面将要提到的 编码器-解码器 框架中的编码器),h0~h3之间的横向箭头就是状态【RNN】剖析RNN 之 从RNN-(Simple|LSTM) 到 序列生成 再到 seq2seq框架(encoder-decoder,或称为seq2seq)的传递。

Simple-RNN

Simple-RNN非常简单,就是把t时刻的输出【RNN】剖析RNN 之 从RNN-(Simple|LSTM) 到 序列生成 再到 seq2seq框架(encoder-decoder,或称为seq2seq)作为t时刻的状态【RNN】剖析RNN 之 从RNN-(Simple|LSTM) 到 序列生成 再到 seq2seq框架(encoder-decoder,或称为seq2seq)传递给下一个时刻t+1的状态输入,一句话就是时序间传递的状态就是上一时序的输出。

【RNN】剖析RNN 之 从RNN-(Simple|LSTM) 到 序列生成 再到 seq2seq框架(encoder-decoder,或称为seq2seq)

LSTM

Simple-RNN虽然简单,但有一个问题就是梯度消失,发生在序列长度较长时,具体分析不在此展开来讲了,介绍LSTM的基本都有涉及,而LSTM就是为解决这个问题而提出的,采用门机制(好复杂的样子),门机制其实可以简单理解为:把两个相邻时序【RNN】剖析RNN 之 从RNN-(Simple|LSTM) 到 序列生成 再到 seq2seq框架(encoder-decoder,或称为seq2seq)【RNN】剖析RNN 之 从RNN-(Simple|LSTM) 到 序列生成 再到 seq2seq框架(encoder-decoder,或称为seq2seq)之间传递的状态【RNN】剖析RNN 之 从RNN-(Simple|LSTM) 到 序列生成 再到 seq2seq框架(encoder-decoder,或称为seq2seq)分为两部分(不一定是连续的),一部分保留上个时序【RNN】剖析RNN 之 从RNN-(Simple|LSTM) 到 序列生成 再到 seq2seq框架(encoder-decoder,或称为seq2seq)传递的状态(即【RNN】剖析RNN 之 从RNN-(Simple|LSTM) 到 序列生成 再到 seq2seq框架(encoder-decoder,或称为seq2seq)的部分取值),另一部分是当前时序【RNN】剖析RNN 之 从RNN-(Simple|LSTM) 到 序列生成 再到 seq2seq框架(encoder-decoder,或称为seq2seq)计算出的状态(即原本RNN做的那样)。门就是控制状态向量s中的哪些维被保留,哪些维被更新,一个简单体现思想但不太准确举例如下图。那么状态中哪些被保留,哪些被更新呢?由门来选,所以可以形象地理解为一些门关了新计算出的就过不去,从而保留了旧的。那么门是怎么选的呢,答案是通过对每个维度乘一个0~1之间的小数来实现开闭,可见门就是一个与状态s维度相同的向量,门向量的取值则通过学习得来(看作网络参数)。说了半天,总之一句话,LSTM解决了Simple-RNN里的梯度消失问题,更好就完了。

【RNN】剖析RNN 之 从RNN-(Simple|LSTM) 到 序列生成 再到 seq2seq框架(encoder-decoder,或称为seq2seq)

序列生成

之前都是从RNN网络结构的角度来展开说明,现在我们从任务的角度来看RNN,看一看RNN的应用模式——序列生成。

序列生成问题就是给定一个序列,生成另一个序列。比如机器翻译、文本生成。为了引出本篇的核心,考虑这样的文本生成任务:给出一个词,生成一个序列。思路很简单:给出的词作为RNN的t=0时刻的输入,预测得到输出,再把输出作为下一时刻的输入,如此循环地进行预测,直到到达指定长度或者预测出指定的结束符。如下图(是不是跟最开始论文中的图越来越像了?)。

【RNN】剖析RNN 之 从RNN-(Simple|LSTM) 到 序列生成 再到 seq2seq框架(encoder-decoder,或称为seq2seq)

思路很清晰,但问题来了,实现不好操作啊!想一下在训练时,训练数据X往往都是预定好的直接输入网络,而现在的情况是后续的输入要从输出中得到,这就需要更接近底层的设置,是现有的一些封装好的RNN(比如Keras里的LSTM/GRU之类)所不能实现的,虽然话是这样说,但其实Keras或TensorFlow还是可以实现的,虽然在Keras中就不能简单的通过定义一个LSTM layer来实现,但可以通过定义RNN(rnn_cell, ...) layer然后自定义rnn_cell相应操作来处理。(所谓cell就是每个时序的计算单元,就是图中的h0/h1/h2/h3方框,注意它们本质是同一个!看起来是多个只是按时间展开的图便于理解)。当然也可以通过Tensorflow中的tf.nn.raw_rnn来实现,具体可以看我另一篇博文

条件生成框架

现在我们来对序列生成的输入上做一下手脚,我们不再输入单纯的词了,而是把词向量和一个有意义的向量c拼接起来,从而使得输入中包含更多的信息,这个向量c就被称为条件上下文向量。说到这里关于为什么要拼接这样一个向量可能还是不太清楚明白,下面就用下图解释一下(再向论文原图迈近一步,哎呀,已经到了)。

【RNN】剖析RNN 之 从RNN-(Simple|LSTM) 到 序列生成 再到 seq2seq框架(encoder-decoder,或称为seq2seq)

图中的【RNN】剖析RNN 之 从RNN-(Simple|LSTM) 到 序列生成 再到 seq2seq框架(encoder-decoder,或称为seq2seq)就可以看作是条件上下文,它在每个时序都被输入了cell。Text feature vector很好地解释了这样做的目的:我们可以从一段长文本中提取出特征,用这个特征来帮助我们后续网络的预测。怎么帮助呢?下面就回到最开始提到的多标签分类问题。

序列生成 解决 多标签分类

先考虑单标签分类问题,很简单嘛,根据特征X来预测分类就OK了,那么是不是可以看作是下图呢?

【RNN】剖析RNN 之 从RNN-(Simple|LSTM) 到 序列生成 再到 seq2seq框架(encoder-decoder,或称为seq2seq)

看到这里可能就有点明白了,如果多进行几次分类呢?如下图。

【RNN】剖析RNN 之 从RNN-(Simple|LSTM) 到 序列生成 再到 seq2seq框架(encoder-decoder,或称为seq2seq)

这里为什么要多重复几次分类呢?通过重复,制造了这样一种机会:对于类别特征近似的类别,在第a次分类的结果 不同于 第b次分类的结果,当然仅凭当前这种形式还达不到预期效果,因为相同的输入基本会得到相同的输出(这里说基本是因为h0 h1 h2 h3参数权重不同,对于近似的类别可能会输出不同,但这种差异不可控,所以不能达到预期)。那么我们怎么来动摇,使得在h0预测倾向相近类别A,在h1预测倾向相近类别B呢?显然再加一个输入来动摇就可以了,看下图(左)。

【RNN】剖析RNN 之 从RNN-(Simple|LSTM) 到 序列生成 再到 seq2seq框架(encoder-decoder,或称为seq2seq)【RNN】剖析RNN 之 从RNN-(Simple|LSTM) 到 序列生成 再到 seq2seq框架(encoder-decoder,或称为seq2seq)

这张图跟论文中的图差别在哪里?就是动摇和预测输出之间未建立联系。想一下建立联系会带来什么?对比左右两图,建立联系意味着预测的标签之间将会有所关联,这种关联可以是相近标签之间的“近义”。举个实际的例子,比如有两个相近的标签分别是校园安全和校园暴力(一般校园暴力往往也会跟校园安全挂钩),当仅用h0进行单次预测时,y1既想预测为校园安全,也想预测为校园暴力,两者在概率上互相压制,导致无论预测哪个置信度都不高;当采用上面右图的方式时,由于y1在y2的前面,训练数据中都是先将y1预测为校园安全,到y2预测时,由于y1向其传达了已预测为校园安全的信息,所以y2将不再左右不定而是倾向于预测为校园暴力。

说白了,Text feature vector定下基本基调(把置信度小的筛掉),RNN时序之间传播的信息学习标签之间的关系(用于给左右不定的标签左右加码)。

到这里,多标签问题好像解决了,到此可以结束了,但好像图还跟论文原图不太一样啊,再看一下。

【RNN】剖析RNN 之 从RNN-(Simple|LSTM) 到 序列生成 再到 seq2seq框架(encoder-decoder,或称为seq2seq)

这一对比又发现一些了不得的事情,这一切要从不同点说起。

论文中给出的图的不同之处在于,cell之间的状态传递(cell之间的横向箭头),而上面的结构已经达到目标了,难道这样在时序之间再加一种状态传递会提升效果?不得而知,但我的猜想是有可能的:RNN中某一时刻的输出【RNN】剖析RNN 之 从RNN-(Simple|LSTM) 到 序列生成 再到 seq2seq框架(encoder-decoder,或称为seq2seq)是由上一时刻的状态【RNN】剖析RNN 之 从RNN-(Simple|LSTM) 到 序列生成 再到 seq2seq框架(encoder-decoder,或称为seq2seq)和当前时刻的输入【RNN】剖析RNN 之 从RNN-(Simple|LSTM) 到 序列生成 再到 seq2seq框架(encoder-decoder,或称为seq2seq)经过一定的变换确定的,即【RNN】剖析RNN 之 从RNN-(Simple|LSTM) 到 序列生成 再到 seq2seq框架(encoder-decoder,或称为seq2seq),这说明状态中包含了变换前的信息,这可能是有用的(假如O()导致了标签间有效信息丢失)。

在猜测这种重复是否有效之后,也应该考虑一下能不能去掉一个,就用一个呢?再仔细看看只用一个的图,突然又发现一个不得了的事情,那就是Simple-RNN!看下图对比:

【RNN】剖析RNN 之 从RNN-(Simple|LSTM) 到 序列生成 再到 seq2seq框架(encoder-decoder,或称为seq2seq)【RNN】剖析RNN 之 从RNN-(Simple|LSTM) 到 序列生成 再到 seq2seq框架(encoder-decoder,或称为seq2seq)

左右两图都是只保留了一种时序间信息传递,一个是y一个是s,那么要是y=s呢?这不就是Simple-RNN嘛!为什么要这么想呢,是因为如果仅保留一个,那直接使用Simple-RNN就避免了之前的实现难题——把上一个时刻的输出作为下一个时刻的输入的动态难题。因为Simple-RNN中向后一传递的状态s正是前一时刻的输出,在图中意味着左图中的折虚线箭头和右图中的横向箭头是等价的。(不知道论文中是不是就是使用Simple-RNN实现的,如果是的话可能图就不太准确了,也有可能虚线就代表着只是表示信息传递,而不是网络中实际的连接,whatever,解决问题最关键)。

seq2seq

在之前说明了条件生成框架,通过结合多标签分类的具体实例来对RNN序列生成问题加深认识。下面再进一步就到了seq2seq了,seq2seq是 序列到序列的条件生成 的简称,也被称为 编码器-解码器(encoder-decoder) 框架。它只不过是指定了条件生成框架中上下文条件向量c的来源——通过编码器得到。什么是编码器?可以说编码器就是一个RNN,它接收一个序列,输出一个向量作为条件上下文向量c,它把序列编码成一个向量,因此称为编码器。至于解码器,就是条件生成框架中的主体部分。如下图。

【RNN】剖析RNN 之 从RNN-(Simple|LSTM) 到 序列生成 再到 seq2seq框架(encoder-decoder,或称为seq2seq)

其实广义来讲,编码器不一定是RNN,比如论文中的文本特征向量Text feature vector就是由CNN提取出来的,所以编码器是CNN。同样,解码器也不一定是CNN。换句话说,编码器-解码器 框架应该是涵盖 seq2seq的。

结语

本文只简要介绍了从RNN到seq2seq的主要思想,其中在序列生成问题上较深入地进行分析,但还有未提到的问题,比如序列生成中的teacher-forcing训练模式,它也是使模型学习到先后词之间关系的一种训练方式,与本文提到的训练模式不同的是,它的训练中的Y就是X在时间上后移一位,相对于本文中的训练模式更为直接,但也因此存在非gold序列的问题,其具体细节这里就不展开讲了,有兴趣的可以查找相关资料。

参考文献

[1] G. Chen, D. Ye, Z. Xing, J. Chen and E. Cambria, "Ensemble application of convolutional and recurrent neural networks for multi-label text categorization," 2017 International Joint Conference on Neural Networks (IJCNN), 2017, pp. 2377-2383, doi: 10.1109/IJCNN.2017.7966144.

上一篇:动手学深度学习 | 双向循环神经网络 | 59


下一篇:10.4-10.5随笔