CNN/RNN在NLP领域的对比

目录

1 RNN

1.1 Why hot?

1.2 RNN存在的问题

1.3 如何改造RNN并行计算能力

2 CNN

2.1 CNN存在的问题

2.2 解决方法


本篇主要总结下张俊林大神关于放弃幻想,全面拥抱Transformer:自然语言处理三大特征抽取器(CNN/RNN/TF)比较文章中关于CNN RNN部分的内容。

1 RNN

1.1 Why hot?

我们知道,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界如此流行的根本原因。

1.2 RNN存在的问题

(1)Transformer的出现

(2)不具备高效的并行计算能力,T时刻的计算依赖T-1时刻的隐层计算结果,而T-1时刻的计算依赖T-2时刻的隐层计算结果……..这样就形成了所谓的序列依赖关系。就是说只能先把第1时间步的算完,才能算第2时间步的结果,这就造成了RNN在这个角度上是无法并行计算的,只能老老实实地按着时间步一个单词一个单词往后走。

1.3 如何改造RNN并行计算能力

有两个大的思路来改进:一种是仍然保留任意连续时间步(T-1到T时刻)之间的隐层连接;而另外一种是部分地打断连续时间步(T-1到T时刻)之间的隐层连接 。

(1)

我们先来看第一种方法,现在我们的问题转化成了:我们仍然要保留任意连续时间步(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)

第二种改进典型的思路是:为了能够在不同时间步输入之间进行并行计算,那么只有一种做法,那就是打断隐层之间的连接,但是又不能全打断,因为这样基本就无法捕获组合特征了,所以唯一能选的策略就是部分打断,比如每隔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结构,这样无疑会拉慢速度,所以这是个两难的选择,与其这样不如直接换成其它模型;如果我们选择不打断,貌似只能在隐层神经元之间进行并行,而这样做的缺点是:一方面并行能力上限很低;另外一方面里面依然存在的序列依赖估计仍然是个问题。这是为何悲观的原因,主要是看不到大的希望。

2 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层之后连接全联接层神经网络,形成最后的分类过程。

2.1 CNN存在的问题

滑动窗口无法捕获远距离特征:卷积核覆盖的那个滑动窗口,CNN能捕获到的特征基本都体现在这个滑动窗口里了。大小为k的滑动窗口轻轻的穿过句子的一个个单词,荡起阵阵涟漪,那么它捕获了什么?其实它捕获到的是单词的k-gram片段信息,这些k-gram片段就是CNN捕获到的特征,k的大小决定了能捕获多远距离的特征。

2.2 解决方法

(1)Dilated Conv空洞卷积

(2)加深卷积层

第一层卷积层,假设滑动窗口大小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年的主旋律是发展经济一样,经济发展好了,很多问题就不是问题了。最近几年之所以大家感到各方面很困难,症结就在于经济不行了,所以很多问题无法通过经济带动来解决,于是看似各种花样的困难就冒出来,这是一个道理。

发布了74 篇原创文章 · 获赞 17 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/sinat_25394043/article/details/103988320