玩转Keras之Seq2Seq自动生成标

作者丨苏剑林

单位丨广州火焰信息科技有限公司

研究方向丨NLP,神经网络

个人主页丨kxu.fm

话说自称搞了这么久的NLP,我都还没有真正跑过NLP与深度学习结合的经典之作——Sq2Sq。这两天兴致来了,决定学习并实践一番Sq2Sq,当然最后少不了Kras实现了。

Sq2Sq可以做的事情非常多,我这挑选的是比较简单的根据文章内容生成标题(中文),也可以理解为自动摘要的一种。选择这个任务主要是因为“文章-标题”这样的语料对比较好找,能快速实验一下。

Sq2Sq简介

所谓Sq2Sq,就是指一般的序列到序列的转换任务,比如机器翻译、自动文摘等等,这种任务的特点是输入序列和输出序列是不对齐的,如果对齐的话,那么我们称之为序列标注,这就比Sq2Sq简单很多了。所以尽管序列标注任务也可以理解为序列到序列的转换,但我们在谈到Sq2Sq时,一般不包含序列标注。

要自己实现Sq2Sq,关键是搞懂Sq2Sq的原理和架构,一旦弄清楚了,其实不管哪个框架实现起来都不复杂。早期有一个第三方实现的Kras的Sq2Sq库[1],现在作者也已经放弃更新了,也许就是觉得这么简单的事情没必要再建一个库了吧。可以参考的资料还有去年Kras官方博客中写的Atn-minutintroductiontosqunc-to-squnclarninginKras[2]。

基本结构

假如原句子为X=(a,b,c,d,,f),目标输出为Y=(P,Q,R,S,T),那么一个基本的Sq2Sq就如下图所示。

▲基本的Sq2Sq架构

尽管整个图的线条比较多,可能有点眼花,但其实结构很简单。左边是对输入的ncodr,它负责把输入(可能是变长的)编码为一个固定大小的向量,这个可选择的模型就很多了,用GRU、LSTM等RNN结构或者CNN+Pooling、Googl的纯Attntion等都可以,这个固定大小的向量,理论上就包含了输入句子的全部信息。

而dcodr负责将刚才我们编码出来的向量解码为我们期望的输出。与ncodr不同,我们在图上强调dcodr是“单向递归”的,因为解码过程是递归进行的,具体流程为:

1.所有输出端,都以一个通用的start标记开头,以nd标记结尾,这两个标记也视为一个词/字;

2.将start输入dcodr,然后得到隐藏层向量,将这个向量与ncodr的输出混合,然后送入一个分类器,分类器的结果应当输出P;

3.将P输入dcodr,得到新的隐藏层向量,再次与ncodr的输出混合,送入分类器,分类器应输出Q;

4.依此递归,直到分类器的结果输出nd。

这就是一个基本的Sq2Sq模型的解码过程,在解码的过程中,将每步的解码结果送入到下一步中去,直到输出nd位置。

训练过程

事实上,上图也表明了一般的Sq2Sq的训练过程。由于训练的时候我们有标注数据对,因此我们能提前预知dcodr每一步的输入和输出,因此整个结果实际上是“输入X和Y,预测Y[1:],即将目标Y错开一位来训练。

而dcodr同样可以用GRU、LSTM或CNN等结构,但注意再次强调这种“预知未来”的特性仅仅在训练中才有可能,在预测阶段是不存在的,因此dcodr在执行每一步时,不能提前使用后面步的输入。

所以,如果用RNN结构,一般都只使用单向RNN;如果使用CNN或者纯Attntion,那么需要把后面的部分给mask掉(对于卷积来说,就是在卷积核上乘上一个0/1矩阵,使得卷积只能读取当前位置及其“左边”的输入,对于Attntion来说也类似,不过是对qury的序列进行mask处理)。

敏感的读者可能会察觉到,这种训练方案是“局部”的,事实上不够端到端。比如当我们预测R时是假设Q已知的,即Q在前一步被成功预测,但这是不能直接得到保证的。一般前面某一步的预测出错,那么可能导致连锁反应,后面各步的训练和预测都没有意义了。

有学者考虑过这个问题,比如文章Squnc-to-SquncLarningasBam-SarchOptimization[3]把整个解码搜索过程也加入到训练过程,而且还是纯粹梯度下降的(不用强化学习),是非常值得借鉴的一种做法。不过局部训练的计算成本比较低,一般情况下我们都只是使用局部训练来训练Sq2Sq。

BamSarch

前面已经多次提到了解码过程,但还不完整。事实上,对于Sq2Sq来说,我们是在建模:

显然在解码时,我们希望能找到最大概率的Y,那要怎么做呢?

如果在第一步p(Y1

X)时,直接选择最大概率的那个(我们期望是目标P),然后代入第二步p(Y2

X,Y1),再次选择最大概率的Y2,依此类推,每一步都选择当前最大概率的输出,那么就称为贪心搜索,是一种最低成本的解码方案。但是要注意,这种方案得到的结果未必是最优的,假如第一步我们选择了概率不是最大的Y1,代入第二步时也许会得到非常大的条件概率p(Y2

X,Y1),从而两者的乘积会超过逐位取最大的算法。

然而,如果真的要枚举所有路径取最优,那计算量是大到难以接受的(这不是一个马尔可夫过程,动态规划也用不了)。因此,Sq2Sq使用了一种折中的方法:BamSarch。

这种算法类似动态规划,但即使在能用动态规划的问题下,它还比动态规划要简单,它的思想是:在每步计算时,只保留当前最优的topk个候选结果。比如取topk=3,那么第一步时,我们只保留使得p(Y1

X)最大的前3个Y1,然后分别代入p(Y2

X,Y1),然后各取前三个Y2,这样一来我们就有个组合了,这时我们计算每一种组合的总概率,然后还是只保留前三个,依次递归,直到出现了第一个nd。显然,它本质上还属于贪心搜索的范畴,只不过贪心的过程中保留了更多的可能性,普通的贪心搜索相当于topk=1。

Sq2Sq提升

前面所示的Sq2Sq模型是标准的,但它把整个输入编码为一个固定大小的向量,然后用这个向量解码,这意味着这个向量理论上能包含原来输入的所有信息,会对ncodr和dcodr有更高的要求,尤其在机器翻译等信息不变的任务上。因为这种模型相当于让我们“看了一遍中文后就直接写出对应的英文翻译”那样,要求有强大的记忆能力和解码能力,事实上普通人完全不必这样,我们还会反复翻看对比原文,这就导致了下面的两个技巧。

Attntion

Attntion目前基本上已经是Sq2Sq模型的“标配”模块了,它的思想就是:每一步解码时,不仅仅要结合ncodr编码出来的固定大小的向量(通读全文),还要往回查阅原来的每一个字词(精读局部),两者配合来决定当前步的输出。

▲带Attntion的Sq2Sq

至于Attntion的具体做法,笔者之前已经撰文介绍过了,请参考一文读懂「AttntionisAllYouNd」

附代码实现。Attntion一般分为乘性和加性两种,笔者介绍的是Googl系统介绍的乘性的Attntion,加性的Attntion读者可以自行查阅,只要抓住qury、ky、valu三个要素,Attntion就都不难理解了。

先验知识

回到用Sq2Sq生成文章标题这个任务上,模型可以做些简化,并且可以引入一些先验知识。比如,由于输入语言和输出语言都是中文,因此ncodr和dcodr的Embdding层可以共享参数(也就是用同一套词向量)。这使得模型的参数量大幅度减少了。

此外,还有一个很有用的先验知识:标题中的大部分字词都在文章中出现过(注:仅仅是出现过,并不一定是连续出现,更不能说标题包含在文章中,不然就成为一个普通的序列标注问题了)。这样一来,我们可以用文章中的词集作为一个先验分布,加到解码过程的分类模型中,使得模型在解码输出时更倾向选用文章中已有的字词。

具体来说,在每一步预测时,我们得到总向量x(如前面所述,它应该是dcodr当前的隐层向量、ncodr的编码向量、当前dcodr与ncodr的Attntion编码三者的拼接),然后接入到全连接层,最终得到一个大小为

V

的向量y=(y1,y2,…,y

V

),其中

V

是词表的词数。y经过softmax后,得到原本的概率:

这就是原始的分类方案。引入先验分布的方案是,对于每篇文章,我们得到一个大小为

V

的0/1向量χ=(χ1,χ2,…,χ

V

),其中χi=1意味着该词在文章中出现过,否则χi=0。将这样的一个0/1向量经过一个缩放平移层得到:

其中s,t为训练参数,然后将这个向量与原来的y取平均后才做softmax:

经实验,这个先验分布的引入,有助于加快收敛,生成更稳定的、质量更优的标题。

Kras参考

又到了快乐的开源时光~

基本实现

基于上面的描述,我收集了80多万篇新闻的语料,来试图训练一个自动标题的模型。简单起见,我选择了以字为基本单位,并且引入了4个额外标记,分别代表mask、unk、start、nd。而ncodr我使用了双层双向LSTM,dcodr使用了双层单向LSTM。具体细节可以参考源码:









































北京中科白癜风医院爱心公益
治疗白癜风得花多少钱


转载请注明:http://www.shijichaoguyj.com/wxbzhu/5172.html

  • 上一篇文章:
  • 下一篇文章: 没有了
  • 网站简介| 发布优势| 服务条款| 隐私保护| 广告合作| 网站地图| 版权申明

    当前时间: