放弃幻想,全面拥抱Transformer:NLP三大特征抽取器比较
在辞旧迎新的时刻,大家都在忙着回顾过去一年的成绩(或者在灶台前含泪数锅),并对2019做着规划,当然也有不少朋友执行力和工作效率比较高,直接把2018年初制定的计划拷贝一下,就能在3秒钟内完成2019年计划的制定,在此表示祝贺。2018年从经济角度讲,对于所有人可能都是比较难过的一年,而对于自然语言处理领域来说,2018年无疑是个收获颇丰的年头,而诸多技术进展如果只能选择一项来讲的话,那么当之无愧的应该就是Bert模型了。在上一篇介绍Bert的文章“从Word Embedding到Bert模型—自然语言处理中的预训练技术发展史”里,我曾大言不惭地宣称如下两个个人判断:一个是Bert这种两阶段的模式(预训练+Finetuning)必将成为NLP领域研究和工业应用的流行方法;第二个是从NLP领域的特征抽取器角度来说,Transformer会逐步取代RNN成为最主流的的特征抽取器。关于特征抽取器方面的判断,上面文章限于篇幅,只是给了一个结论,并未给出具备诱惑力的说明,看过我文章的人都知道我不是一个随便下结论的人(那位正在补充下一句:“你随便起来不是……”的同学请住口,请不要泄露国家机密,你可以继续睡觉,吵到其它同学也没有关系,哈哈),但是为什么当时我会下这个结论呢?本文可以看做是上文的一个外传,会给出比较详实的证据来支撑之前给出的结论。
如果对目前NLP里的三大特征抽取器的未来走向趋势做个宏观判断的话,我的判断是这样的:RNN人老珠黄,已经基本完成它的历史使命,将来会逐步退出历史舞台;CNN如果改造得当,将来还是有希望有自己在NLP领域的一席之地,如果改造成功程度超出期望,那么还有一丝可能作为割据一方的军阀,继续生存壮大,当然我认为这个希望不大,可能跟宋小宝打篮球把姚明打哭的概率相当;而新欢Transformer明显会很快成为NLP里担当大任的最主流的特征抽取器。至于将来是否会出现新的特征抽取器,一枪将Tranformer挑落马下,继而取而代之成为新的特征抽取山大王?这种担忧其实是挺有必要的,毕竟李商隐在一千年前就告诫过我们说:“君恩如水向东流,得宠忧移失宠愁。 莫向樽前奏花落,凉风只在殿西头。”当然这首诗看样子目前送给RNN是比较贴切的,至于未来Transformer是否会失宠?这个问题的答案基本可以是肯定的,无非这个时刻的来临是3年之后,还是1年之后出现而已。当然,我希望如果是在读这篇文章的你,或者是我,在未来的某一天,从街头拉来一位长相普通的淑女,送到韩国整容,一不小心偏离流水线整容工业的美女模板,整出一位天香国色的绝色,来把Transformer打入冷宫,那是最好不过。但是在目前的状态下,即使是打着望远镜,貌似还没有看到有这种资质的候选人出现在我们的视野之内。
我知道如果是一位严谨的研发人员,不应该在目前局势还没那么明朗的时候做出如上看似有些武断的明确结论,所以这种说法可能会引起争议。但是这确实就是我目前的真实想法,至于根据什么得出的上述判断?这种判断是否有依据?依据是否充分?相信你在看完这篇文章可以有个属于自己的结论。
可能谈到这里,有些平常吃亏吃的少所以喜欢挑刺的同学会质疑说:你凭什么说NLP的典型特征抽取器就这三种呢?你置其它知名的特征抽取器比如Recursive NN于何地?嗯,是,很多介绍NLP重要进展的文章里甚至把Recursive NN当做一项NLP里的重大进展,除了它,还有其它的比如Memory Network也享受这种部局级尊贵待遇。但是我一直都不太看好这两个技术,而且不看好很多年了,目前情形更坚定了这个看法。而且我免费奉劝你一句,没必要在这两个技术上浪费时间,至于为什么,因为跟本文主题无关,以后有机会再详细说。
上面是结论,下面,我们正式进入举证阶段。
战场侦查:NLP任务的特点及任务类型
NLP任务的特点和图像有极大的不同,上图展示了一个例子,NLP的输入往往是一句话或者一篇文章,所以它有几个特点:首先,输入是个一维线性序列,这个好理解;其次,输入是不定长的,有的长有的短,而这点其实对于模型处理起来也会增加一些小麻烦;再次,单词或者子句的相对位置关系很重要,两个单词位置互换可能导致完全不同的意思。如果你听到我对你说:“你欠我那一千万不用还了”和“我欠你那一千万不用还了”,你听到后分别是什么心情?两者区别了解一下;另外,句子中的长距离特征对于理解语义也非常关键,例子参考上图标红的单词,特征抽取器能否具备长距离特征捕获能力这一点对于解决NLP任务来说也是很关键的。
上面这几个特点请记清,一个特征抽取器是否适配问题领域的特点,有时候决定了它的成败,而很多模型改进的方向,其实就是改造得使得它更匹配领域问题的特性。这也是为何我在介绍RNN、CNN、Transformer等特征抽取器之前,先说明这些内容的原因。
NLP是个很宽泛的领域,包含了几十个子领域,理论上只要跟语言处理相关,都可以纳入这个范围。但是如果我们对大量NLP任务进行抽象的话,会发现绝大多数NLP任务可以归结为几大类任务。两个看似差异很大的任务,在解决任务的模型角度,可能完全是一样的。
通常而言,绝大部分NLP问题可以归入上图所示的四类任务中:一类是序列标注,这是最典型的NLP任务,比如中文分词,词性标注,命名实体识别,语义角色标注等都可以归入这一类问题,它的特点是句子中每个单词要求模型根据上下文都要给出一个分类类别。第二类是分类任务,比如我们常见的文本分类,情感计算等都可以归入这一类。它的特点是不管文章有多长,总体给出一个分类类别即可。第三类任务是句子关系判断,比如Entailment,QA,语义改写,自然语言推理等任务都是这个模式,它的特点是给定两个句子,模型判断出两个句子是否具备某种语义关系;第四类是生成式任务,比如机器翻译,文本摘要,写诗造句,看图说话等都属于这一类。它的特点是输入文本内容后,需要自主生成另外一段文字。
解决这些不同的任务,从模型角度来讲什么最重要?是特征抽取器的能力。尤其是深度学习流行开来后,这一点更凸显出来。因为深度学习最大的优点是“端到端(end to end)”,当然这里不是指的从客户端到云端,意思是以前研发人员得考虑设计抽取哪些特征,而端到端时代后,这些你完全不用管,把原始输入扔给好的特征抽取器,它自己会把有用的特征抽取出来。
身为资深Bug制造者和算法工程师,你现在需要做的事情就是:选择一个好的特征抽取器,选择一个好的特征抽取器,选择一个好的特征抽取器,喂给它大量的训练数据,设定好优化目标(loss function),告诉它你想让它干嘛……..然后你觉得你啥也不用干等结果就行了是吧?那你是我见过的整个宇宙中最乐观的人…….你大量时间其实是用在调参上…….。从这个过程可以看出,如果我们有个强大的特征抽取器,那么中初级算法工程师沦为调参侠也就是个必然了,在AutoML(自动那啥)流行的年代,也许以后你想当调参侠而不得,李斯说的“吾欲与若复牵黄犬,俱出上蔡东门逐狡兔,岂可得乎!”请了解一下。所以请珍惜你半夜两点还在调整超参的日子吧,因为对于你来说有一个好消息一个坏消息,好消息是:对于你来说可能这样辛苦的日子不多了!坏消息是:对于你来说可能这样辛苦的日子不多了!!!那么怎么才能成为算法高手?你去设计一个更强大的特征抽取器呀。
下面开始分叙三大特征抽取器。
沙场老将RNN:廉颇老矣,尚能饭否
RNN模型我估计大家都熟悉,就不详细介绍了,模型结构参考上图,核心是每个输入对应隐层节点,而隐层节点之间形成了线性序列,信息由前向后在隐层之间逐步向后传递。我们下面直接进入我想讲的内容。
为何RNN能够成为解决NLP问题的主流特征抽取器
我们知道,RNN自从引入NLP界后,很快就成为吸引眼球的明星模型,在NLP各种任务中被广泛使用。但是原始的RNN也存在问题,它采取线性序列结构不断从前往后收集输入信息,但这种线性序列结构在反向传播的时候存在优化困难问题,因为反向传播路径太长,容易导致严重的梯度消失或梯度爆炸问题。为了解决这个问题,后来引入了LSTM和GRU模型,通过增加中间状态信息直接向后传播,以此缓解梯度消失问题,获得了很好的效果,于是很快LSTM和GRU成为RNN的标准模型。其实图像领域最早由HighwayNet/Resnet等导致模型革命的skip connection的原始思路就是从LSTM的隐层传递机制借鉴来的。经过不断优化,后来NLP又从图像领域借鉴并引入了attention机制(从这两个过程可以看到不同领域的相互技术借鉴与促进作用),叠加网络把层深作深,以及引入Encoder-Decoder框架,这些技术进展极大拓展了RNN的能力以及应用效果。下图展示的模型就是非常典型的使用RNN来解决NLP任务的通用框架技术大礼包,在更新的技术出现前,你可以在NLP各种领域见到这个技术大礼包的身影。
上述内容简单介绍了RNN在NLP领域的大致技术演进过程。那么为什么RNN能够这么快在NLP流行并且占据了主导地位呢?主要原因还是因为RNN的结构天然适配解决NLP的问题,NLP的输入往往是个不定长的线性序列句子,而RNN本身结构就是个可以接纳不定长输入的由前向后进行信息线性传导的网络结构,而在LSTM引入三个门后,对于捕获长距离特征也是非常有效的。所以RNN特别适合NLP这种线形序列应用场景,这是RNN为何在NLP界如此流行的根本原因。
RNN在新时代面临的两个严重问题
RNN在NLP界一直红了很多年(2014-2018?),在2018年之前,大部分各个子领域的State of Art的结果都是RNN获得的。但是最近一年来,眼看着RNN的领袖群伦的地位正在被动摇,所谓各领风骚3-5年,看来网红模型也不例外。
那这又是因为什么呢?主要有两个原因。
第一个原因在于一些后起之秀新模型的崛起,比如经过特殊改造的CNN模型,以及最近特别流行的Transformer,这些后起之秀尤其是Transformer的应用效果相比RNN来说,目前看具有明显的优势。这是个主要原因,老人如果干不过新人,又没有脱胎换骨自我革命的能力,自然要自觉或不自愿地退出历史舞台,这是自然规律。至于RNN能力偏弱的具体证据,本文后面会专门谈,这里不展开讲。当然,技术人员里的RNN保皇派们,这个群体规模应该还是相当大的,他们不会轻易放弃曾经这么热门过的流量明星的,所以也想了或者正在想一些改进方法,试图给RNN延年益寿。至于这些方法是什么,有没有作用,后面也陆续会谈。
另外一个严重阻碍RNN将来继续走红的问题是:RNN本身的序列依赖结构对于大规模并行计算来说相当之不友好。通俗点说,就是RNN很难具备高效的并行计算能力,这个乍一看好像不是太大的问题,其实问题很严重。如果你仅仅满足于通过改RNN发一篇论文,那么这确实不是大问题,但是如果工业界进行技术选型的时候,在有快得多的模型可用的前提下,是不太可能选择那么慢的模型的。一个没有实际落地应用支撑其存在价值的模型,其前景如何这个问题,估计用小脑思考也能得出答案。
那问题来了:为什么RNN并行计算能力比较差?是什么原因造成的?
我们知道,RNN之所以是RNN,能将其和其它模型区分开的最典型标志是:T时刻隐层状态的计算,依赖两个输入,一个是T时刻的句子输入单词Xt,这个不算特点,所有模型都要接收这个原始输入;关键的是另外一个输入,T时刻的隐层状态St还依赖T-1时刻的隐层状态S(t-1)的输出,这是最能体现RNN本质特征的一点,RNN的历史信息是通过这个信息传输渠道往后传输的,示意参考上图。那么为什么RNN的并行计算能力不行呢?问题就出在这里。因为T时刻的计算依赖T-1时刻的隐层计算结果,而T-1时刻的计算依赖T-2时刻的隐层计算结果……..这样就形成了所谓的序列依赖关系。就是说只能先把第1时间步的算完,才能算第2时间步的结果,这就造成了RNN在这个角度上是无法并行计算的,只能老老实实地按着时间步一个单词一个单词往后走。
而CNN和Transformer就不存在这种序列依赖问题,所以对于这两者来说并行计算能力就不是问题,每个时间步的操作可以并行一起计算。
那么能否针对性地对RNN改造一下,提升它的并行计算能力呢?如果可以的话,效果如何呢?下面我们讨论一下这个问题。
如何改造RNN使其具备并行计算能力?
上面说过,RNN不能并行计算的症结所在,在于T时刻对T-1时刻计算结果的依赖,而这体现在隐层之间的全连接网络上。既然症结在这里,那么要想解决问题,也得在这个环节下手才行。在这个环节多做点什么事情能够增加RNN的并行计算能力呢?你可以想一想。
其实留给你的选项并不多,你可以有两个大的思路来改进:一种是仍然保留任意连续时间步(T-1到T时刻)之间的隐层连接;而另外一种是部分地打断连续时间步(T-1到T时刻)之间的隐层连接 。
我们先来看第一种方法,现在我们的问题转化成了:我们仍然要保留任意连续时间步(T-1到T时刻)之间的隐层连接,但是在这个前提下,我们还要能够做到并行计算,这怎么处理呢?因为只要保留连续两个时间步的隐层连接,则意味着要计算T时刻的隐层结果,就需要T-1时刻隐层结果先算完,这不又落入了序列依赖的陷阱里了吗?嗯,确实是这样,但是为什么一定要在不同时间步的输入之间并行呢?没有人说RNN的并行计算一定发生在不同时间步上啊,你想想,隐层是不是也是包含很多神经元?那么在隐层神经元之间并行计算行吗?如果你要是还没理解这是什么意思,那请看下图。
上面的图只显示了各个时间步的隐层节点,每个时间步的隐层包含3个神经元,这是个俯视图,是从上往下看RNN的隐层节点的。另外,连续两个时间步的隐层神经元之间仍然有连接,上图没有画出来是为了看着简洁一些。这下应该明白了吧,假设隐层神经元有3个,那么我们可以形成3路并行计算(红色箭头分隔开成了三路),而每一路因为仍然存在序列依赖问题,所以每一路内仍然是串行的。大思路应该明白了是吧?但是了解RNN结构的同学会发现这样还遗留一个问题:隐层神经元之间的连接是全连接,就是说T时刻某个隐层神经元与T-1时刻所有隐层神经元都有连接,如果是这样,是无法做到在神经元之间并行计算的,你可以想想为什么,这个简单,我假设你有能力想明白。那么怎么办呢?很简单,T时刻和T-1时刻的隐层神经元之间的连接关系需要改造,从之前的全连接,改造成对应位置的神经元(就是上图被红箭头分隔到同一行的神经元之间)有连接,和其它神经元没有连接。这样就可以解决这个问题,在不同路的隐层神经元之间可以并行计算了。
第一种改造RNN并行计算能力的方法思路大致如上所述,这种方法的代表就是论文“Simple Recurrent Units for Highly Parallelizable Recurrence”中提出的SRU方法,它最本质的改进是把隐层之间的神经元依赖由全连接改成了哈达马乘积,这样T时刻隐层单元本来对T-1时刻所有隐层单元的依赖,改成了只是对T-1时刻对应单元的依赖,于是可以在隐层单元之间进行并行计算,但是收集信息仍然是按照时间序列来进行的。所以其并行性是在隐层单元之间发生的,而不是在不同时间步之间发生的。
这其实是比较巧妙的一种方法,但是它的问题在于其并行程度上限是有限的,并行程度取决于隐层神经元个数,而一般这个数值往往不会太大,再增加并行性已经不太可能。另外每一路并行线路仍然需要序列计算,这也会拖慢整体速度。SRU的测试速度为:在文本分类上和原始CNN(Kim 2014)的速度相当,论文没有说CNN是否采取了并行训练方法。 其它在复杂任务阅读理解及MT任务上只做了效果评估,没有和CNN进行速度比较,我估计这是有原因的,因为复杂任务往往需要深层网络,其它的就不妄作猜测了。
第二种改进典型的思路是:为了能够在不同时间步输入之间进行并行计算,那么只有一种做法,那就是打断隐层之间的连接,但是又不能全打断,因为这样基本就无法捕获组合特征了,所以唯一能选的策略就是部分打断,比如每隔2个时间步打断一次,但是距离稍微远点的特征如何捕获呢?只能加深层深,通过层深来建立远距离特征之间的联系。代表性模型比如上图展示的Sliced RNN。我当初看到这个模型的时候,心里忍不住发出杠铃般的笑声,情不自禁地走上前跟他打了个招呼:你好呀,CNN模型,想不到你这个糙汉子有一天也会穿上粉色裙装,装扮成RNN的样子出现在我面前啊,哈哈。了解CNN模型的同学看到我上面这句话估计会莞尔会心一笑:这不就是简化版本的CNN吗?不了解CNN的同学建议看完后面CNN部分再回头来看看是不是这个意思。
那经过这种改造的RNN速度改进如何呢?论文给出了速度对比实验,归纳起来,SRNN速度比GRU模型快5到15倍,嗯,效果不错,但是跟对比模型DC-CNN模型速度比较起来,比CNN模型仍然平均慢了大约3倍。这很正常但是又有点说不太过去,说正常是因为本来这就是把RNN改头换面成类似CNN的结构,而片段里仍然采取RNN序列模型,所以必然会拉慢速度,比CNN慢再正常不过了。说“说不过去”是指的是:既然本质上是CNN,速度又比CNN慢,那么这么改的意义在哪里?为什么不直接用CNN呢?是不是?前面那位因为吃亏吃的少所以爱抬杠的同学又会说了:也许人家效果特别好呢。嗯,从这个结构的作用机制上看,可能性不太大。你说论文实验部分证明了这一点呀,我认为实验部分对比试验做的不充分,需要补充除了DC-CNN外的其他CNN模型进行对比。当然这点纯属个人意见,别当真,因为我讲起话来的时候经常摇头晃脑,此时一般会有人惊奇地跟我反馈说:为什么你一讲话我就听到了水声?
上面列举了两种大的改进RNN并行计算能力的思路,我个人对于RNN的并行计算能力持悲观态度,主要因为RNN本质特性决定了我们能做的选择太少。无非就是选择打断还是不打断隐层连接的问题。如果选择打断,就会面临上面的问题,你会发现它可能已经不是RNN模型了,为了让它看上去还像是RNN,所以在打断片段里仍然采取RNN结构,这样无疑会拉慢速度,所以这是个两难的选择,与其这样不如直接换成其它模型;如果我们选择不打断,貌似只能在隐层神经元之间进行并行,而这样做的缺点是:一方面并行能力上限很低;另外一方面里面依然存在的序列依赖估计仍然是个问题。这是为何悲观的原因,主要是看不到大的希望。
偏师之将CNN:刺激战场绝地求生
在一年多前,CNN是自然语言处理中除了RNN外最常见的深度学习模型,这里介绍下CNN特征抽取器,会比RNN说得详细些,主要考虑到大家对它的熟悉程度可能没有RNN那么高。
NLP中早期的怀旧版CNN模型
最早将CNN引入NLP的是Kim在2014年做的工作,论文和网络结构参考上图。一般而言,输入的字或者词用Word Embedding的方式表达,这样本来一维的文本信息输入就转换成了二维的输入结构,假设输入X包含n个字符,而每个字符的Word Embedding的长度为d,那么输入就是d*n的二维向量。
卷积层本质上是个特征抽取层,可以设定超参数F来指定卷积层包含多少个卷积核(Filter)。对于某个Filter来说,可以想象有一个d*k大小的移动窗口从输入矩阵的第一个字开始不断往后移动,其中k是Filter指定的窗口大小,d是Word Embedding长度。对于某个时刻的窗口,通过神经网络的非线性变换,将这个窗口内的输入值转换为某个特征值,随着窗口不断往后移动,这个Filter对应的特征值不断产生,形成这个Filter的特征向量。这就是卷积核抽取特征的过程。卷积层内每个Filter都如此操作,就形成了不同的特征序列。Pooling 层则对Filter的特征进行降维操作,形成最终的特征。一般在Pooling层之后连接全联接层神经网络,形成最后的分类过程。
这就是最早应用在NLP领域CNN模型的工作机制,用来解决NLP中的句子分类任务,看起来还是很简洁的,之后陆续出现了在此基础上的改进模型。这些怀旧版CNN模型在一些任务上也能和当时怀旧版本的RNN模型效果相当,所以在NLP若干领域也能野蛮生长,但是在更多的NLP领域,还是处于被RNN模型压制到抑郁症早期的尴尬局面。那为什么在图像领域打遍天下无敌手的CNN,一旦跑到NLP的地盘,就被RNN这个地头蛇压制得无颜见图像领域江东父老呢?这说明这个版本的CNN还是有很多问题的,其实最根本的症结所在还是老革命遇到了新问题,主要是到了新环境没有针对新环境的特性做出针对性的改变,所以面临水土不服的问题。
CNN能在RNN纵横的各种NLP任务环境下生存下来吗?谜底即将揭晓。
CNN的进化:物竞天择的模型斗兽场
下面我们先看看怀旧版CNN存在哪些问题,然后看看我们的NLP专家们是如何改造CNN,一直改到目前看上去还算效果不错的现代版本CNN的。
首先,我们先要明确一点:CNN捕获到的是什么特征呢?从上述怀旧版本CNN卷积层的运作机制你大概看出来了,关键在于卷积核覆盖的那个滑动窗口,CNN能捕获到的特征基本都体现在这个滑动窗口里了。大小为k的滑动窗口轻轻的穿过句子的一个个单词,荡起阵阵涟漪,那么它捕获了什么?其实它捕获到的是单词的k-gram片段信息,这些k-gram片段就是CNN捕获到的特征,k的大小决定了能捕获多远距离的特征。
说完这个,我们来看Kim版CNN的第一个问题:它只有一个卷积层。表面看上去好像是深度不够的问题是吧?我会反问你说:为什么要把CNN作深呢?其实把深度做起来是手段,不是目的。只有一个卷积层带来的问题是:对于远距离特征,单层CNN是无法捕获到的,如果滑动窗口k最大为2,而如果有个远距离特征距离是5,那么无论上多少个卷积核,都无法覆盖到长度为5的距离的输入,所以它是无法捕获长距离特征的。
那么怎样才能捕获到长距离的特征呢?有两种典型的改进方法:一种是假设我们仍然用单个卷积层,滑动窗口大小k假设为3,就是只接收三个输入单词,但是我们想捕获距离为5的特征,怎么做才行?显然,如果卷积核窗口仍然覆盖连续区域,这肯定是完不成任务的。提示一下:你玩过跳一跳是吧?能采取类似策略吗?对,你可以跳着覆盖呀,是吧?这就是Dilated 卷积的基本思想,确实也是一种解决方法。
第二种方法是把深度做起来。第一层卷积层,假设滑动窗口大小k是3,如果再往上叠一层卷积层,假设滑动窗口大小也是3,但是第二层窗口覆盖的是第一层窗口的输出特征,所以它其实能覆盖输入的距离达到了5。如果继续往上叠加卷积层,可以继续增大卷积核覆盖输入的长度。
上面是两种典型的解决CNN远距离特征捕获能力的方案,Dilated CNN偏技巧一些,而且叠加卷积层时超参如何设置有些学问,因为连续跳接可能会错过一些特征组合,所以需要精心调节参数搭配,保证所有可能组合都被覆盖到。相对而言,把CNN作深是主流发展方向。上面这个道理好理解,其实自从CNN一出现,人们就想各种办法试图把CNN的深度做起来,但是现实往往是无情的,发现怎么折腾,CNN做NLP问题就是做不深,做到2到3层卷积层就做不上去了,网络更深对任务效果没什么帮助(请不要拿CharCNN来做反例,后来研究表明使用单词的2层CNN效果超过CharCNN)。目前看来,还是深层网络参数优化手段不足导致的这个问题,而不是层深没有用。后来Resnet等图像领域的新技术出现后,很自然地,人们会考虑把Skip Connection及各种Norm等参数优化技术引入,这才能慢慢把CNN的网络深度做起来。
上面说的是Kim版本CNN的第一个问题,无法捕获远距离特征的问题,以及后面科研人员提出的主要解决方案。回头看Kim版本CNN还有一个问题,就是那个Max Pooling层,这块其实与CNN能否保持输入句子中单词的位置信息有关系。首先我想问个问题:RNN因为是线性序列结构,所以很自然它天然就会把位置信息编码进去;那么,CNN是否能够保留原始输入的相对位置信息呢?我们前面说过对于NLP问题来说,位置信息是很有用的。其实CNN的卷积核是能保留特征之间的相对位置的,道理很简单,滑动窗口从左到右滑动,捕获到的特征也是如此顺序排列,所以它在结构上已经记录了相对位置信息了。但是如果卷积层后面立即接上Pooling层的话,Max Pooling的操作逻辑是:从一个卷积核获得的特征向量里只选中并保留最强的那一个特征,所以到了Pooling层,位置信息就被扔掉了,这在NLP里其实是有信息损失的。所以在NLP领域里,目前CNN的一个发展趋势是抛弃Pooling层,靠全卷积层来叠加网络深度,这背后是有原因的(当然图像领域也是这个趋势)。
上图展示了在NLP领域能够施展身手的摩登CNN的主体结构,通常由1-D卷积层来叠加深度,使用Skip Connection来辅助优化,也可以引入Dilated CNN等手段。比如ConvS2S主体就是上图所示结构,Encoder包含 15个卷积层,卷积核kernel size=3,覆盖输入长度为25。当然对于ConvS2S来说,卷积核里引入GLU门控非线性函数也有重要帮助,限于篇幅,这里不展开说了,GLU貌似是NLP里CNN模型必备的构件,值得掌握。再比如TCN(论文:An Empirical Evaluation of Generic Convolutional and Recurrent Networks for Sequence Modeling),集成了几项技术:利用Dilated CNN拓展单层卷积层的输入覆盖长度,利用全卷积层堆叠层深,使用Skip Connection辅助优化,引入Casual CNN让网络结构看不到T时间步后的数据。不过TCN的实验做得有两个明显问题:一个问题是任务除了语言模型外都不是典型的NLP任务,而是合成数据任务,所以论文结论很难直接说就适合NLP领域;另外一点,它用来进行效果比较的对比方法,没有用当时效果很好的模型来对比,比较基准低。所以TCN的模型效果说服力不太够。其实它该引入的元素也基本引入了,实验说服力不够,我觉得可能是它命中缺GLU吧。
除此外,简单谈一下CNN的位置编码问题和并行计算能力问题。上面说了,CNN的卷积层其实是保留了相对位置信息的,只要你在设计模型的时候别手贱,中间层不要随手瞎插入Pooling层,问题就不大,不专门在输入部分对position进行编码也行。但是也可以类似ConvS2S那样,专门在输入部分给每个单词增加一个position embedding,将单词的position embedding和词向量embedding叠加起来形成单词输入,这样也可以,也是常规做法。
至于CNN的并行计算能力,那是非常强的,这其实很好理解。我们考虑单层卷积层,首先对于某个卷积核来说,每个滑动窗口位置之间没有依赖关系,所以完全可以并行计算;另外,不同的卷积核之间也没什么相互影响,所以也可以并行计算。CNN的并行度是非常自由也非常高的,这是CNN的一个非常好的优点。
以上内容介绍了怀旧版CNN是如何在NLP修罗场一步步通过自我进化生存到今天的。CNN的进化方向,如果千言万语一句话归纳的话,那就是:想方设法把CNN的深度做起来,随着深度的增加,很多看似无关的问题就随之解决了。就跟我们国家最近40年的主旋律是发展经济一样,经济发展好了,很多问题就不是问题了。最近几年之所以大家感到各方面很困难,症结就在于经济不行了,所以很多问题无法通过经济带动来解决,于是看似各种花样的困难就冒出来,这是一个道理。
那么介绍了这么多,摩登版CNN效果如何呢?与RNN及Transforme比起来怎样?别着急,后面会专门谈这个问题。
白衣骑士Transformer:盖世英雄站上舞台