团员分享_NLP相关Deep Learning模型小结_@Lily_20190427
2020年8月29日 更新
开启更多功能,提升办公效能

前言:本文是团员Lily的第2篇分享输出,是对深度学习模型相对全面、细致&生动的总结;也欢迎有更多的团员们也来分享你的AI干货~


总结这篇,我基本的想法是重点理解模型是什么(what)、为什么要用这种模型(why)以及哪些场景中可以用这种模型(where),至于如何实现模型(how)可以了留给RD小哥哥们

一、词的表达

如果大家已经了解了词表达相关的概念,可以直接看看能不能回答这一小节最后的几个问题:)


要知道计算机是看不懂人类语言的,要想让机器理解语言、实现自然语言处理,第一步就是把自然语言转化成计算机语言——数字。由于词是人类语言表达时的一种基本单位(当然更细的单位是字或者字母),nlp处理的时候很自然的想要用一组特定的数字代表一个特定的词,这就是词的表达,把这些表示词的数字连起来就可以表达一句话、一篇文章了。这一part里有很多常见的名词,distributed representation、word embedding、word2vec等等,它们的关系大概是这样的



  1. one-hot representation v.s. distributed representation

表达方式,我觉得就是自然语言到机器语言怎么转化的一套规则。比如“我”这个词转化到机器语言应该用“1”还是“100”表示呢?而且机器语言中代表“我”的这个数还不能和代表其他词的数重复吧,必须是一个唯一的id


顺着id这个思路,假设我们的词典收录了10个词,那么我们就给词典里的每一个词分配一个唯一的id。词表示的时候用一个和字典一样长的向量表示,这个向量里只有id这一位为1,其他位都为0。比如说abandon这个词的id是1,那么就表示成abandon=[1 0 0 0 0 0 0 0 0 0],这就是one-hot representation。这种表示好理解,但是也有问题。问题一是向量会随着字典变大而变大。很明显如果我的词典有100000个词的话,每一个词都要用长度100000的向量表示,如果一句话有20个词,那么就是一个100000*20的矩阵了,按这种操作基本就走远了。另外一个问题是这种表示不能体现语义的相关性。比如香蕉和苹果在人看来是非常类似的,但是用one-hot表示香蕉可能是[1,0,0,0,0],苹果可能是[0,0,1,0,0],之间没有任何相关性,这样的话如果我们用“我吃了香蕉”训练模型,结果模型可能并不能理解“我吃了苹果”,泛化能力就很差


于是机智的大佬们提出了一个假说,就是distributed hypothesis:词的语义由其上下文决定基于这种假说生成的表示就叫做distributed representation,用在词表示时也就是word embedding,中文名有词向量、词嵌入所以distributed representation≈word embedding,因为现阶段主流的nlp处理大都是基于词的,当然也有对字、句子、甚至文章进行embedding的,所以不能说完全完全相等。至于具体如何基于这种假说实现词表示,根据模型不同可以分成基于矩阵(GloVe)、基于聚类、基于神经网络(NNLM、Word2Vec等)的方法


  1. word embedding

个人理解,从字面意思上看word embedding就是把一个one-hot这样的稀疏矩阵映射成一个更稠密的矩阵,比如之前的abandon用one-hot(词典大小为10)表示为[1 0 0 0 0 0 0 0 0 0],但word embedding可能用维度为2的向量[0.4 0.5]就可以表示,正好解决了前边说的one-hot的维度过大问题,同时还增大了信息熵,所以word embedding表示信息的效率要高于one-hot。但词向量这个名字没有体现出它表示语义的本质,所以第一次看到很容易会不知所云。为了说明word embedding可以体现语义,这时候就可以搬出著名的queen,king,woman,man的栗子了。(图来自Andrew Ng deeplearning.ai)



上图是通过训练得出的词向量,man=[-1 0.01 0.03 0.09],woman=[1 0.02 0.02 0.01],king=[-0.95 0.93 0.70 0.02],queen=[0.97 0.95 0.69 0.01]。矩阵相减man-woman=[-2 -0.01 0.01 0.08],king-queen=[-1.92 -0.02 0.01 0.01],两个差值非常相近,或者说两个向量的夹角很小,可以理解为man和woman之间的关系与king和queen之间非常相近。而apple-orange=[-0.01 -0.01 0.05 -0.02]就和man-woman、king-queen相差很大



很有意思的是最初word embedding其实是为了训练NNLM(Neural Network Language Model)得到的副产品。训练语言模型会得到一个lookup table,这个lookup table有点像地下工作者用的密码本,通过这个密码本可以将one-hot向量转换成更低维度的word embedding向量,可见词向量实现的关键就是得到密码本lookup table。后来更高效的得到word embedding的模型之一就是word2Vec,word2Vec又有两种模型,分别是CBOW和skip-gram,两者都可以得到lookup table,具体模型和实现不在这里展开


word embedding可以作为判断语义相似度的一种手段,但更多的是作为其他nlp任务的第一步。实际中如果不是特殊领域(军事、法律等)的词典,word embedding可以用别人训练好的,提高效率,所以word embedding也可以看做神经网络预处理的一种


另外说一下,word embedding有个最大的问题是不能处理多义词。举个栗子“苹果员工爱吃苹果”,第一个苹果是指苹果公司,第二个是指水果,但对于word embedding来说二者只能对应一个向量(比如[0.1 -0.3]),在处理后续任务时只要是苹果就对应成[0.1 -0.3],所以通过词向量并不能区分出苹果的不同词义


总结一下,词的表达我觉得要知道: 1)为什么需要词表达,2)几个常见名词(one-hot representation, distributed representation, word embedding, word2Vec)之间的关系,3)word embedding比one-hot强在哪里,4)word embedding有什么缺点

二、RNN/LSTM

  1. RNN原理

循环神经网络(RNN)最大的特点是可以在一定程度上保留前一时刻的信息,所以特别适合处理语音、语音这样前后有关联性的序列数据。那么具体RNN是怎么实现这种保留前一时间的信息的呢?关键在于隐含状态。CNN没有时间概念,一张图片的所有像素点一起输入,不管经过多复杂的隐含层,一个输入对应的始终是一个输出;而RNN输入是每次输入一个词,处理完这个词再输入下一个,每个输入会产生两个东西,一是当前时刻的输出,二是隐含状态,隐含状态将输入到下个时刻



我觉得公式表示更清晰一些(o代表输出,x代表输入,s代表隐含状态):


ot=V*(W*st-1+U*xt)····························1式

st-1=W*st-2+U*xt-1······························2式,带入1式得:

ot=V*(W*(W*st-2+U*xt-1)+U*xt)········3式


也就是t时间的输出ot和t-2时间的隐含状态st-2有关,如果一直迭代下去,理论上可以追溯到往前任意时刻的信息st-n。但实际中如果步数太多,即st-n里的n太大,由于梯度消失,信息可能就捕捉不到了,可以理解为模型"忘记"了之前的信息,有点像复习时候”马冬梅“,考试时候”什么冬梅“的意思。这就是RNN的长期依赖问题,于是才有了RNN的变种LSTM


  1. LSTM原理

LSTM和RNN一样,每个时刻的输入产生一个输出和一个隐含状态,上一时间的隐含状态输入到下一时刻从而实现“记忆”的目的。和RNN不同的是,LSTM用了更复杂的方式(多个门结构)产生隐含状态,达到长期记忆的效果。门这个名字也很形象,像是高铁站的大门(带安检那种),经过门时候有些带着危险物品的人被拦下来,而其他人却可以通过,像是一个过滤器。学过数电魔电的同学对门电路一定不陌生,比如最常见的与门,只有和被与的数相同的数才能通过(为1),不同的数不能通过(置0)。LSTM中的门结构也是这种过滤的思想,LSTM共有三个门共同控制着隐含状态该记录什么信息和输出什么信息。忘记门决定上一时刻的记忆Ct-1的保留程度;输入门决定当前时刻的信息有多少要被记录在记忆Ct中;从忘记门保留下来的信息和输入门保留下来的信息合起来就是当前时间状态的记忆Ct;最后输出门决定当前时间t的输出值。门结构具体使用sigmoid+位乘实现的。LSTM的图解也有很多很好的文章啦(虽然我觉得公式更好理解啊),这里就不赘述,最经典的文章:http://colah.github.io/posts/2015-08-Understanding-LSTMs/,中文翻译:https://www.jianshu.com/p/9dc9f41f0b29


总结一下,LSTM通过遗忘门、输入门、输出门共同控制隐含状态和输出,达到长期记忆的效果,解决了RNN长期依赖问题。另外还有一种RNN的变体GRU,和LSTM原理类似,但在实际应用中好像不如LSTM应用广泛




  1. RNN/LSTM优化

不论是RNN还是LSTM,还有几种优化形式。第一个双向RNN/LSTM,单向RNN/LSTM只考虑了上文信息, 比如“the sky is ___”,通过上文大概可以判断空应该填blue。但如果句子是“the sky is ___,it's raining.”,空里填什么就可能不光要考虑上文,还要考虑下文了。双向RNN/LSTM就可以考虑到下文信息,原理也很简单,就是把句子倒过来训练一遍,这样原来的下文就成了上文了


第二种优化就是多层RNN/LSTM,原始模型只有一层中间状态st,多层RNN/LSTM引入多层隐含层st(1)、st(2)、st(3),可以理解为让模型更充分的记忆上文信息,但一般隐含层数量也不会特别多,常用2-3层。两种优化结合起来就是双向多层RNN/LSTM


  1. 应用场景

其实对于AI PM来说原理和模型理解即可,应用场景才是关注的重点。nlp任务大概可以分为4类:一是序列标注,比如词性标注、命名实体识别;二是分类任务,比如情感分析;三是关系判断,比如推理、SQuAD阅读理解等;四是生成式任务,比如翻译、对话、文本摘要等通过一段文字生成另一段文字的任务。从产品角度看,要实现一个完整产品,可能涉及到以上一个或者多个任务。比如一个对话系统中会涉及到识别意图(分类问题)、槽信息识别(序列标注问题)等等。可见任务的粒度是要比产品细的,所以接下来的所有应用场景分析还是都从偏技术的任务角度展开,举得栗子可能比较偏向简单的应用(即一个应用=一个任务)


RNN/LSTM模型按输入输出的对应关系可以分为3种,多输入对多输出(多对多)、单输入对多输出(一对多)、多输入对单输出(多对一)



A) 多对多,可以用来解决标注问题。命名实体识别实现中常用biLSTM+crf算法,输入是待标注的句子,输出是每个词的BIO标记。比如对句子“王梅梅在香港大学读研究生”标注人名和专有名词



chatbot中的slot filling也可以看做是标注问题,所以也可以用RNN/LSTM实现,输入是用户表达,输出是IOB标注出的槽。比如“订明天到上海的机票”,输出的标签序列是“O B-time I-time O B-toloc I-toloc O O O”,其中识别出了两个槽——时间和目的地


类似标注任务有个很有意思的应用--短信卡片。短信卡片就是提取短消息中的有效信息,用卡片的形式表现出来(如下图,左图是原始信息,右图是卡片信息),从而提高信息的传递效率。我猜测这种短信卡片的两种实现途径:一是用规则匹配,只能做特定格式的短信信息提取;二是用LSTM做,把要识别的信息作为一个个命名实体,这样泛化能力会比较好



B) 多对一,可以用于分类任务例如根据用户对商品的评价生成评分(1到5分),但我感觉这个应用是个伪命题,一般评价系统都会先评分再详细评价吧(比如淘宝好中差评、亚马逊1-5星),所以实际场景中应该用不太到?还有就是情感分析,比如对话系统中通过用户的输入判断用户当前情绪,辅助回答,使回答更加自然合理。


C) 一对多,典型的应用是生成特定风格的音乐、写诗、写文章等等。很久之前百度出过一个为你写诗的小应用,估计就是这个原理。还有一个有意思的git项目https://github.com/hzy46/Char-RNN-TensorFlow实现了生成英文、写诗、歌词、小说、生成代码、生成日文等功能。


总结一下,关于RNN和LSTM需要知道:1)两种模型的大概结构和变体,2)为什么适合处理序列数据,3)LSTM相对于RNN的优点,4)实际应用场景

三、seq2seq

  1. seq2seq原理

细心的读者会发现RNN/LSTM有个最大的问题就是输入序列和输出序列长度一样,对于翻译或者对话任务这就很有问题了,举个栗子,中译英:“在 星辉 斑斓 中 放歌”->"sing aloud in the splender of starlight",中文5个词,英文7个,如果直接用RNN/LSTM,中文5个词输入最多只能对应5个词输出,很显然不能解决翻译问题;对话也是一样的,毕竟我们说话不是绝句律诗,不能问句几个字回答就几个字。所以只用RNN/LSTM不能解决翻译、对话这样由一个语句生成另一个语句的问题,所以大佬们又发明了seq2seq模型


sequence to sequence(seq2seq)模型的是一个encoder-decoder的结构,encoder是LSTM(用RNN也没毛病),输入是待翻译的一段话或者对话中的问句,输出不重要,重要的是最后时刻的隐含状态,在seq2seq中叫做中间语义向量C,C可以看做包含了输入语句的所有信息。然后把C作为初始隐含状态输入decoder,decoder是一个一对多的LSTM,输入是代表句子起始的占位符<SOS>,输出是翻译结果的一段话或者是对话中的回答。通过巧妙的把两个LSTM用中间向量连接起来(两个LSTM可以分别设定任意长度),seq2seq解决了RNN输入和输出必须一样长的问题,所以可以较好的解决翻译、对话、文本摘要的问题



  1. seq2seq+attention

当然seq2seq也不是完美的,就像基础的RNN一样,模型会由于长度过长”忘记“了前边的信息,这个也好理解,毕竟中间状态C的大小是一定的,把所有信息都压缩在里边不太现实,可以把语义向量C想成一个盒子,为了把后边的信息装进盒子里只能把之前的信息压一压或者干脆挤出去


于是大佬们又想出了attention机制,注意力这个名字特别形象,比如我一边看剧一边吃饭一边刷微博,如果把注意力过多的放在吃饭或者刷微博上,很有可能会把饭吃到鼻子里,这个故事告诉我们分配注意力很重要!attention机制的道理也是一样,本质就是通过求加权值来决定当前应该关注的信息,比如我给看剧、吃饭、刷微博分别分配0.3、0.5、0.2的权重,我就能顺利完成边吃饭边看剧边刷微博的任务。在翻译任务中,比如翻译”小明 今年 七岁,他 喜欢 玩 游戏“时,当翻译到”他“的时候,注意力应该集中在”小明“和”他“上,给它们分别分配0.2 和0.8的权重,其他词权重都为0。attention机制通过计算encoder各时刻产生的隐含状态和decoder当前时刻产生的隐含状态的相似度来分配注意力,相似度越高那分配的注意力就越多,也就是在隐含状态中占的比重越大


seq2seq+attention模型比起纯seq2seq有了一些变化,还是用栗子来说明,现在要把”机器 学习“翻译成”machine learning“:


A)encoder

1)首先是encoder不再只输出一个中间向量C了,而是每个时刻都输出一个隐含状态,在例子”机器 学习“中,即输出h1、h2,分别等于[2, 4, 1]、[1, 0, 4](假设隐含层维度为3)


2)然后最后时刻h2的隐含状态[1, 0, 4]作为初始隐含状态输入decoder,这点和seq2seq一样


B)decoder的第一个时刻t1

3)decoder的第一个时刻t1的输入是<SOS>和context vector的拼接,初始context vector为0,之后时刻的context vector是通过attention模块计算出的


4)decoder 时刻t1通过LSTM输出hidden state s1=[10,10,3]


5),s1和encoder产生的所有隐含状态h1、h2输入到attention模块,计算二者的相关性score,计算score的一种方法是点乘,即e11=h1.*s1=[2, 4, 1].*[10, 10, 3]=63,e12=h2^s1=22


6),把scores转化成权重α11=e11/(e11+e12)=63/(63+22)=0.74,α12=e12/(e11+e12)=0.26


7),计算encoder的隐含状态的加权平均值作为context vector:c1=α11*h1+α12*h2=0.74*[2,4,1]+0.26*[1,0,4]=[1.74,2.96,1.78]。至此attention模块的运算结束,输出decoder t1时刻的context vector c1


8)decoder t1时刻的hidden state s1和context vector c1拼接,之后经过一层线性变换和softmax,得到输出o1”machine“


C)decoder的第二个时刻t2

9)t1时刻的输出”machine“的词向量[0.3,0.3]和c1[1.74,2.96,1.78]拼接,作为t2时刻的输入


10)重复上述4-8,得到t2时刻的输出o2


seq2seq+attention流程我参考https://mp.weixin.qq.com/s/0k71fKKv2SRLv9M6BjDo4w 画成了gif图



  1. 应用场景

seq2seq搭配LSTM应该是现在nlp领域运用最广泛的模型之一了,主要用在生成任务中。按场景分的话还是翻译、对话、文本摘要几个大类。当然seq2seq作为单RNN/LSTM的进化形态,也可以解决上一小节中单用RNN/LSTM就能解决的问题,比如命名实体识别。


seq2seq被用在翻译中诞生了谷歌翻译这样划时代的产品,可以说吊打当时其它所有机器翻译系统。但即使现在,基于神经网络的翻译仍然不能达到人工翻译的水平,前段时间微信翻译还因为没能正确翻译某明星上了热搜。讲道理,像caixukun这样根本算不上英文单词的词,翻译模型没见过、训练语料里没有,翻译不对真的可以理解。那为啥会翻译成这样呢?举个栗子,“don't be caixukun”翻译成“别傻了”,我猜测是训练语料中有些“don't be silly”、“don't be ridiculous”之类的句子,因为翻译是根据上下文的context vector嘛,再加上最后名字的这个词不认识,可能正好caixukun的词向量和silly更接近,所以就翻译成别傻了;如果另一个人的名字词向量和ridiculous更接近那很有可能就翻译成别闹了。那改进方法有没有呢?算法角度可能暂时没有,增加语料应该能解决一部分问题,但是不能解决根本;产品角度还是可以有的,比如可不可以做成集外词不翻译呢,像是“别那么caixukun”这样,不翻译我觉得比翻译错强一点,毕竟流量明星的粉丝是惹不起的:(



对话机器人大致可以分为任务型、问答型和闲聊型,其中任务型和问答型需要明确的答案,所以肯定是不能直接通过seq2seq来生成回答的。相比之下闲聊型对话机器人更适合seq2seq的应用,通过大量真实数据训练可以得到丰富且更有”人性“的回答,当然通过seq2seq训练的出的模型也可以用来做问答型、任务型机器人的兜底回答。另外有篇很有意思的paper:https://mp.weixin.qq.com/s/5Dgoe9MQzPkEDsiP6Xs49g做了关于ECM(Emotional Chatting Machine,情绪化聊天机器人)方面的研究。在decoder输入时,除了语义向量C还添加了一个emotion embedding(like,happiness,sadness,disgust、anger),从而实现了根据情感的不同输出不同的回答。开个脑洞,如果用同样的输入数据再训练一个RNN模型做情感分析,输出是一句话的情感分类,再把这个情感分类作为emotion embedding输入ECM,这样就能实现根据用户的感情做出相应的回答了


文本摘要个人感觉可玩性也很高,常见的比如给新闻加标题、提炼用户评价的关键信息等。再开个脑洞,一提到“概括XXX的内容”,我就不由自主的想到小时候做的阅读理解,以后只能机器评分中就可以用上文本摘要+文本相似度判断来给概括内容这类题目打分啦!(总之阅读理解是逃不掉的)


另外还有类应用就是encoder是CNN,decoder是RNN,实现看图说话的任务,可以用来给图片新闻加标题。输入是图片,输出是描述图片的一句话。看图说话告诉我们一个道理,seq2seq不仅仅可以用于文本生成文本,也可以用于图片等其他信息生成文本,用2seq连接就完事了


总结一下,seq2seq需要了解:1)seq2seq模型、seq2seq+attention的大概样子,2)什么是attention机制,3)可以解决哪些实际问题

四、transformer

总结到这里,细心的读者应该发现了,新模型的出现总是为了解决旧模型这样那样的问题。我们的需求、想解决的问题并没什么变化,模型的改进只是逐步提供更高效的解法、得出更准确的答案


那看似已经很完善的LSTM+seq2seq+attention又有什么问题呢?主要是太慢。其实慢是所有RNN/LSTM共同的问题,因为模型每个时刻输入一个词,处理完这个词才能处理下个词,是串行输入如果句子很长,处理的时间自然也会变长。再加上如果用了多层双向LSTM,每个时刻的神经网络都很会变复杂,处理的时间就更长了。Transformer的出现就是为了提高训练的效率,实现并行输入。另外transformer比RNN/LSTM的长期记忆能力要好,经科学研究证明transformer对提取特征的能力要远强于LSTM。所以transformer是一种在未来很有可能会替代LSTM的特征提取器(不是我说的)


如果不想了解具体的模型细节,下边的内容就可以忽略了


  1. transformer模型

transformer来自google2017年发表的论文《Attention Is All You Need》,和前边提到的RNN、LSTM相比是个很新的模型。论文中采用了encoder-decoder结构+transformer来解决翻译问题,(我理解就是把seq2seq里的LSTM换成了transformer)。transformer和RNN/LSTM模型非常不同!只用了multi-head self-attention模块提取特征。并行输入实现方法是首先对输入的word embedding进行positional encoding(表示输入的位置信息,举个栗子,没有位置信息的话“我吃了”和“吃了我”对模型来说就一样了),然后把输入的所有词组成一个二维矩阵一起输入


还是用“机器学习”翻译成“machine learning”举例说明:


A)生成输入矩阵

1)把输入“机器”、“学习”转化成词向量[0.6, 0.2],[0.1,0.5]


2)计算positional embedding为[0,1],[0.84,0.54],计算position embedding方法之一是通过正余弦得到。我们只要知道positional embedding代表的是位置信息即可,可以理解成[0,1]代表输入的第一个词,[0.84,0.54]代表输入的第二个词


3)word embedding和position embedding相加得到输入[0.6,1.2],[0.94,1.04],实际输入的时候组成一个矩阵,每一行代表一个词,所以我们最终的输入X为[[0.6,1.2],[0.94,1.04]]


B)encoder的self-attention:

4)transformer的encoder是多个子encoder串联起来的(例子中我们用两个子encoder说明),每个encoder又有两个sub layers分别是self-attention和feed forward。输入X进入第一个子encoder的self-attention层,分别与WQ,WK,WV矩阵(通过训练得到)相乘,得到Q=[[3,1,2],[1,1,1]]、K=[[5,2,2],[2,4,1]]、V=[[6,3,2],[1,5,4]]


5)Q和K的转置矩阵相乘得到scores=[[21,7],[10,7]]


6)scores通过softmax转化为权重[[0.75,0.25],[0.59,0.41]](求权重之前原论文中每个score除以了 scaling factor 8,可以使梯度更稳定。这里暂时忽略这个操作),这个权重就是模型给输入的每个词分配的注意力


7)权重和V矩阵相乘,得到self-attention层的输出Z=[[4.75,3.5,2.5],[3.95,3.82,2.82]]


C)encoder的Multi-Head Attention

8)multi-head是把不同的n个self-attention层同时计算上述4-7步,各个self-attention层不共享WQ,WK,WV矩阵,所以最后的得到n个输出Z1~Zn。这里我们假设只有2个head,所以输出为Z1=[[4.75,3.5,2.5],[3.95,3.82,2.82]]、Z2=[[5,3.4,2.4],[3,4.2,3.2]]


9)Z1、Z2拼接[[4.75,3.5,2.5,5,3.4,2.4],[3.95,3.82,2.82,3,4.2,3.2]],然后和一个代表权重的矩阵WO相乘,最终得到Z=[[4.88,3.45,2.45],[3.68,4.01,3.01]]


D)encoder的feed forward netrual network

10)Z经过FFN输出到下一个encoder(self-attention和FFN后边都跟了残差连接和归一化,这里暂时忽略),至此一个encoder的处理结束


11)在下一个encoder重复步骤4-10,直到最后一个encoder


E)decoder(预测时)

decoder是和encoder一样数量(在我们的例子中是2个)的子decoder堆叠,每个子decoder和encoder一样有self-attention层和feed-forward层,除此之外还加了一个encoder-decoder attention层。训练时,decoder输入为了不让模型获取后边的信息,要mask掉当前时刻后边的词,我们在例子中暂时不考虑;预测时,decoder输出是像seq2seq一样每个时刻输出一个词,输出会作为下一时刻的输入


12)时刻t1的第一个子decoder:输入是<SOS>+position embedding,先通过self-attention得输出Z,Z输入到encoder-decoder attention,在encoder-decoder attention层中,K、V矩阵来自encoder的最后输出,Q和encoder的self-attention一样是通过WQ相乘得到的。K、V、Q计算得到输出Z11,Z11经过FFN输出


13)时刻t1的第二个子decoder:输入为上一步的输出Z11,其他都和上一步相同,输出Z12经过线性变换和softmax,得到时刻t1的最终输出”machine“


14)时刻t2的第一个子decoder:输入是上一时刻t1的输出”machine“+position embedding,输出是Z21


15)时刻t2的第二个子decoder:输入是Z21,输出Z22经过线性变换和softmax,得到时刻t2的最终输出”learning“


还是像seq2seq一样,参考https://jalammar.github.io/illustrated-transformer/,把上述模型步骤画成了gif



  1. 应用场景

由于transformer和RNN/LSTM一样是特征抽取器,所以它的应用场景和RNN/LSTM几乎是一样的,如果套上encoder-decoder结构的话,那上边seq2seq、RNN/LSTM解决的翻译、对话、命名实体识别等问题都可以用transformer实现,而且效果可能更好。所以transformer的应用更像是在实际中针对不同情况如何选择特征抽取器,是选RNN/LSTM还是transformer呢?根据大佬的分析,未来transformer很有可能会取代LSTM成为nlp方向最主流的特征提取器


但transformer也不是没有缺点,比如对文字顺序的捕捉能力就不如RNN/LSTM,期待未来通过和RNN/LSTM结合或者改进position embedding来增强transformer顺序特征提取的能力。另外transformer对数据的依赖比较大,要大规模的数据才能取得好的效果,而且对计算力要求比较高。所以短期之内如果在小数据上的训练,或者说在计算资源比较匮乏的情况下,仍然可以考虑传统的RNN/LSTM


transformer给我的一个感觉是nlp方向的deep learning已经从“解决问题”逐步走向“更好的解决问题”,在实际工程中就给提供可靠、高效的服务提供了可能性(激动的搓搓小手)


总结一下,需要了解:1)transformer的大概结构;2)transformer比RNN/LSTM优势

五、bert

如果关注nlp方向的深度学习发展的话,一定有听过bert的大名(名字来自芝麻街很可爱了,同样来自芝麻街命名的模型还有ELMo和百度的Ernie)。去年年底google发布的bert模型刷新了11项nlp比赛的最好成绩,可以算的上nlp领域里程碑的成果了


bert的厉害之处一是“快”,快是建立在预训练的基础上的像是我们要做一个汇报演示的ppt,之前同事已经写过一个很类似的了,我们要做的就是把同事写的拿过来改下数据、文字描述之类的内容就可以用了,大幅提高了工作效率。这和预训练的思路很像,就是把自己的工作建立在前人的基础上。bert模型中google爸爸给出了在大量数据上预训练出来的基础模型,我们要做到就是在这个基础模型上fine-tuning就完事了,因此对于我们来说节约了大量模型训练的时间。关于预训练思想,强烈推荐感兴趣的同学读这篇https://zhuanlan.zhihu.com/p/49271699


bert的第二个厉害之处是通用性针对nlp的某一项比赛刷新最好成绩都是正常操作,但是同时刷新11项比赛的最好成绩就是大魔王了,四大类nlp任务几乎都可以通过bert实现


  1. bert模型


bert模型是一个两段式的模型,第一部分是利用上边讲到的transformer预训练生成通用模型,第二部分是通过fine-tuning调整模型用于下游的各式各样任务


预训练通过两个任务完成,一是Masked LM完形填空,就是输入时随机挖掉句子中的一些词让模型来预测;二是next sentence prediction下一句预测,输入时候会把两个句子拼起来,让模型判断第二个句子是否是第一个句子的下一句,是为0,不是为1。具体的流程如下:


A)生成输入:

1)首先每个输入第一位加一个[CLS]位,为了输出时有一个地方可以表示任务二next sentence prediction的结果(0或1)


2)然后把两个句子拼起来(50%第二句是第一句的下一句,50%不是)组成输入input,中间用[SEP]来分割


3)对输入input随机挖掉15%的词,被挖掉的词的80%用[MASK]代替,10%用真实的词代替,10%用随机词代替。比如“My dog is cute”,mask掉cute,80%的情况变为“My dog is [MASK]”,10%的情况维持“My dog is cute”,10%的情况变为“My dog is apple”


4)input转化为embeddings:embeddings是三个部分相加得到的,分别是token embeddings,即词的one-hot representation;二是segment embeddings,第一个句子的所有词segement embedding都为1,第二个句子中的词都为0;第三个部分是position embeddings,和transfomer一样,position embeddings用来表示句子中词的顺序,最大支持512个词的输入


5)输入embeddings到transformer中,经过12个子encoder,最后输出为[seq_length,hidden_size]大小的矩阵Z,seq_lengh是输入句子的长度,hidden_size是transformer隐层大小,这个输出矩阵的每一行可以理解为每个词的新的表达,也就是类似word embedding的存在,只不过这回多义词可以有不同的表达了!


B)Masked LM任务:

6)通过masked_ids得到输入中的哪些词在输入时被挖掉了,比如masked_ids=[1,5,10]就表示输入的第1个、第5个和第10个词被挖掉了。然后在矩阵Z中把对应的那些行挑出来,形成[masked_ids_length, hidden_size]大小的矩阵,假设叫矩阵M。实际中一个输入最多可以mask掉20个词,masked_ids存在一个固定大小为20的数组里,这是数组记做max_pred_pre_seq,如果mask的词不足20个后边的都用0补齐,所以上边的那个masked_ids实际应该是[1,5,10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],矩阵M的大小应该是[20,hidden_size],也就是[max_pred_pre_seq,hidden_size]。矩阵M是输入被mask掉的词对应的最终输出


7)下边把输出矩阵M和大小为[vocab_size,embed_size]的词汇表(对应于word embedding里的lookup table)的相乘,得到矩阵W,大小为[max_pred_pre_seq,vocab_size]。这个矩阵W是矩阵预测的被mask掉的词的one-hot表达


8)把预测的最终结果矩阵W和真实值点乘求出masked_lm_loss,至此预训练的第一个任务就结束了


C)next sentence prediction下一句预测:

9)和masked LM很类似,只不过不需要用masked_ids去找要预测的词了,而是直接取输出矩阵Z的第一行,也就是[CLS]对应的输出,大小为[1,hidden_size]


10)[CLS]对应的输出和[2,embed_size]的矩阵相乘,得到大小为[2]的最终输出,代表了句子是下一句和不是下一句的概率,比如[0.1,0.9]说明该输入的第二句不是第一句的下一句


11)最终输出和真实值点乘求出next_sentence_loss


12)最后把两个任务的masked_lm_loss和next_sentence_loss相加,得到最后的loss,到这里就是预训练的所有过程了。整个预训练是为了得到基础模型


fine-tuning阶段根据任务不同而不同。比如对于sentence classification句子级分类问题(例如情感分类),输出即为[CLS]那一位进行简单的softmax即可


  1. 应用场景

论文中针对几类任务给出的fine-tuning方式,包括句子关系判断、句子分类、文本抽取、序列标注(命名实体识别),对应的常见应用有情感分析、阅读理解QA(比如SQuAD)、命名实体识别等等


类似句子分类的一个应用:https://mp.weixin.qq.com/s/rSdfAI_u9T0ByFoGPS_yFA?scene=25#wechat_redirect,在司法领域通过民事判决书预测判决结果是支持原告还是被告


类似阅读理解的一个有趣的项目:https://www.jiqizhixin.com/articles/2019-01-24-19,通过上下文提取了红楼梦中对话人


以上是bert的直接应用,不知道大家还记不记得之前写到过的nlp四大类任务:序列标注、分类任务、关系判断和生成式任务,bert的直接应用基本可以涵盖前三类任务了,已经很厉害了。但能不能用于翻译、对话这样的生成式任务呢?答案是可以的,但是应用的方式有一点点区别,叫做bert-as-service,也就是把bert作为生成词向量的手段,上边有说过预训练的输出其实就是一种词向量而且解决了传统word embedding多义词的问题,所以把bert作为生成词向量的方式后边再接seq2seq之类的模型就可以用于生成式任务了,这也是bert从预训练思想出发的一种用法,相关项目好像不是很多,我在git也只是看到一个https://github.com/whqwill/seq2seq-keyphrase-bert,如果有类似的应用欢迎补充:)


bert这么厉害,以后是不是可以无脑选bert了呢?我觉得当然不是,毕竟模型、数据、算力是三位一体的,但bert的确给未来nlp的发展提供了思路。而且实际问题中有很多特殊情况,比如离线环境或者端上计算能力受限,可能带不动bert,这种场景下用bert-as-service可能是个更好的选择。或者对实时性要求很高的话,bert模型复杂可能也不是一个最优解。但从数据角度看,如果数据很少的话bert是一个比较好的选择,因为预训练模型已经包含了一些学习的文本信息,可以帮助我们得到更好的预测结果


最后再稍稍总结一下,从应用的角度看模型的选择,可以把应用细化并抽象成一个或多个nlp任务,再去根据不同任务类型和现有的数据、算力资源确定合适的模型。以上仅仅是根据我自己的学习理解对nlp领域最最最基本的几个模型的一点总结,需要探索的还有很多,我们的目标是星辰大海!



附1:Lily的历史文章在

附2:延伸阅读


-END-


以上内容,来自饭团“AI产品经理大本营”,点击这里可关注:http://fantuan.guokr.net/groups/219/ (如果遇到支付问题,请先关注饭团的官方微信服务号“fantuan-app”)


---------------------

作者:黄钊hanniman,图灵机器人-人才战略官,前腾讯产品经理,6年AI实战经验,9年互联网背景,微信公众号/知乎/在行ID“hanniman”,饭团“AI产品经理大本营”,分享人工智能相关原创干货,200页PPT《人工智能产品经理的新起点》被业内广泛好评,下载量1万+。