当我们听到卷积神经网络(Convolutional Neural Network, CNNs)时,往往会联想到计算机视觉。CNNs在图像分类领域做出了巨大贡献,也是当今绝大多数计算机视觉系统的核心技术,从Facebook的图像自动标签到自动驾驶汽车都在使用。
最近我们开始在自然语言处理(Natural Language Processing)领域应用CNNs,并取得了一些引人注目的成果。我将在本文中归纳什么是CNNs,怎样将它们应用于NLP。CNNs背后的直觉知识在计算机视觉的用例里更容易被理解,因此我就先从那里开始,然后慢慢过渡到自然语言处理。
对我来说,最容易的理解方式就是把卷积想象成作用于矩阵的一个滑动窗口函数。这么说有些拗口,但是用动画显示就很直观了。
3x3的滤波器做卷积运算。图片来源: http://deeplearning.stanford.edu/wiki/index.php/Feature_extraction_using_convolution
把左侧的矩阵想象成一幅黑白图像。每一个元素对应一个像素点,0表示黑点,1表示白点(灰度图的像素值一般是0~255)。移动窗口又称作核、滤波器或是特征检测器。这里我们使用3x3的滤波器,将滤波器与矩阵对应的部分逐元素相乘,然后求和。我们平移窗口,使其扫过矩阵的所有像素,对整幅图像做卷积运算。
你也许有些疑惑,刚才的操作究竟会有什么效果呢。我们就来看几个直观的例子。
用邻近点像素值的均值替换其原值,实现图像模糊的效果:
用邻近点像素值与自身的差值替换其原值,实现边缘检测的效果:
(为了直观地来理解,想想图像中平滑的那些部分,那些像素点与周围像素的颜色几乎一致:求和的结果趋近于0,相当于黑色。如果有一条明显的边缘线,比如黑白分界线,那么像素值的差值将会很大,相当于白色)
GIMP手册里还有一些其它的例子。想要深入了解卷积运算的原理,我推荐阅读Chris Olah写的专题博客。
现在你明白了什么是卷积运算了吧。那CNNs又是什么呢?CNNs本质上就是多层卷积运算,外加对每层的输出用非线性激活函数做转换,比如用ReLU和tanh。在传统的前馈神经网络中,我们把每个输入神经元与下一层的输出神经元相连接。这种方式也被称作是全连接层,或者仿射层。在CNNs中我们不这样做,而是用输入层的卷积结果来计算输出。这相当于是局部连接,每块局部的输入区域与输出的一个神经元相连接。对每一层应用不同的滤波器,往往是如上图所示成百上千个,然后汇总它们的结果。这里也涉及到池化层(降采样),我会在后文做解释。在训练阶段,CNN基于你想完成的任务自动学习滤波器的权重值。举个例子,在图像分类问题中,第一层CNN模型或许能学会从原始像素点检测到一些边缘线条,然后根据边缘线条在第二层检测出一些简单的形状,然后基于这些形状检测出更高级的特征,比如脸部轮廓等。最后一层是利用这些高级特征的一个分类器。
这种计算方式有两点值得我们注意:位置不变性和组合性。比如说你想对图片中是否包含大象做分类。因为滤波器是在全图范围内平移,所以并不用关心大象究竟在图片的什么位置。事实上,池化也有助于平移、旋转和缩放的不变性,它对克服缩放因素的效果尤其好。第二个关键因素是(局部)组合性。每个滤波器对一小块局部区域的低级特征组合形成更高级的特征表示。这也是CNNs对计算机视觉作用巨大的原因。我们可以很直观地理解,线条由像素点构成,基本形状又由线条构成,更复杂的物体又源自基本的形状。
NLP任务的输入不再是像素点了,大多数情况下是以矩阵表示的句子或者文档。矩阵的每一行对应于一个分词元素,一般是一个单词,也可以是一个字符。也就是说每一行是表示一个单词的向量。通常,这些向量都是word embeddings(一种底维度表示)的形式,如word2vec和GloVe,但是也可以用one-hot向量的形式,也即根据词在词表中的索引。若是用100维的词向量表示一句10个单词的句子,我们将得到一个10x100维的矩阵作为输入。这个矩阵相当于是一幅“图像”。
在计算机视觉的例子里,我们的滤波器每次只对图像的一小块区域运算,但在处理自然语言时滤波器通常覆盖上下几行(几个词)。因此,滤波器的宽度也就和输入矩阵的宽度相等了。尽管高度,或者区域大小可以随意调整,但一般滑动窗口的覆盖范围是2~5行。综上所述,处理自然语言的卷积神经网络结构是这样的(花几分钟时间理解这张图片,以及维度是如何变化的。你可以先暂时忽略池化操作,我们在稍后会解释它):
用于句子分类器的卷积神经网络(CNN)结构示意图。这里我们对滤波器设置了三种尺寸:2、3和4行,每种尺寸各有两种滤波器。每个滤波器对句子矩阵做卷积运算,得到(不同程度的)特征字典。然后对每个特征字典做最大值池化,也就是只记录每个特征字典的最大值。这样,就由六个字典生成了一串单变量特征向量(univariate feature vector),然后这六个特征拼接形成一个特征向量,传给网络的倒数第二层。最后的softmax层以这个特征向量作为输入,用其来对句子做分类;我们假设这里是二分类问题,因此得到两个可能的输出状态。来源:Zhang, Y., & Wallace, B. (2015). A Sensitivity Analysis of (and Practitioners’ Guide to) Convolutional Neural Networks for Sentence Classification.
计算机视觉完美的直观感受这里还存在吗?位置不变性和局部组合性对图像来说很直观,但对NLP却并非如此。你也许会很在意一个词在句子中出现的位置。相邻的像素点很有可能是相关联的(都是物体的同一部分),但单词并不总是如此。在很多种语言里,短语之间会被许多其它词所隔离。同样,组合性也不见得明显。单词显然是以某些方式组合的,比如形容词修饰名词,但若是想理解更高级特征真正要表达的含义是什么,并不像计算机视觉那么明显了。
由此看来,卷积神经网络似乎并不适合用来处理NLP任务。递归神经网络(Recurrent Neural Network)更直观一些。它们模仿我们人类处理语言的方式(至少是我们自己所认为的方式):从左到右的顺序阅读。庆幸的是,这并不意味着CNNs没有效果。所有的模型都是错的,只是一些能被利用。实际上CNNs对NLP问题的效果非常理想。正如词袋模型(Bag of Words model),它明显是基于错误假设的过于简化模型,但这不影响它多年来一直被作为NLP的标准方法,并且取得了不错的效果。
CNNs的主要特点在于速度快。非常的快。卷积运算是计算机图像的核心部分,在GPU级别的硬件层实现。相比于n-grams,CNNs表征方式的效率也更胜一筹。由于词典庞大,任何超过3-grams的计算开销就会非常的大。即使Google也最多不超过5-grams。卷积滤波器能自动学习好的表示方式,不需要用整个词表来表征。那么用尺寸大于5行的滤波器完全合情合理了。我个人认为许多在第一层学到的滤波器扑捉到的特征与n-grams非常相似(但不局限),但是以更紧凑的方式表征。
在解释如何将CNNs用于NLP任务之前,先来看一下构建CNN网络时需要面临的几个选择。希望这能帮助你更好地理解相关文献。
在上文中解释卷积运算的时候,我忽略了如何使用滤波器的一个小细节。在矩阵的中部使用3x3的滤波器没有问题,在矩阵的边缘该怎么办呢?左上角的元素没有顶部和左侧相邻的元素,该如何滤波呢?解决的办法是采用补零法(zero-padding)。所有落在矩阵范围之外的元素值都默认为0。这样就可以对输入矩阵的每一个元素做滤波了,输出一个同样大小或是更大的矩阵。补零法又被称为是宽卷积,不使用补零的方法则被称为窄卷积。1D的例子如图所示:
窄卷积 vs 宽卷积。滤波器长度为5,输入长度为7。来源:A Convolutional Neural Network for Modelling Sentences (2014)
当滤波器长度相对输入向量的长度较大时,你会发现宽卷积很有用,或者说很有必要。在上图中,窄卷积输出的长度是 (7-5)+1=3,宽卷积输出的长度是(7+2*4-5)+1=11。一般形式为
卷积运算的另一个超参数是步长,即每一次滤波器平移的距离。上面所有例子中的步长都是1,相邻两个滤波器有重叠。步长越大,则用到的滤波器越少,输出的值也越少。下图来自斯坦福的cs231课程网页,分别是步长为1和2的情况:
卷积步长。左侧:步长为1,右侧:步长为2。来源: http://cs231n.github.io/convolutional-networks/
在文献中我们常常见到的步长是1,但选择更大的步长会让模型更接近于递归神经网络,其结构就像是一棵树。
卷积神经网络的一个重要概念就是池化层,一般是在卷积层之后。池化层对输入做降采样。常用的池化做法是对每个滤波器的输出求最大值。我们并不需要对整个矩阵都做池化,可以只对某个窗口区间做池化。例如,下图所示的是2x2窗口的最大值池化(在NLP里,我们通常对整个输出做池化,每个滤波器只有一个输出值):
CNN的最大池化。来源: http://cs231n.github.io/convolutional-networks/#pool
池化的特点之一就是它输出一个固定大小的矩阵,这对分类问题很有必要。例如,如果你用了1000个滤波器,并对每个输出使用最大池化,那么无论滤波器的尺寸是多大,也无论输入数据的维度如何变化,你都将得到一个1000维的输出。这让你可以应用不同长度的句子和不同大小的滤波器,但总是得到一个相同维度的输出结果,传入下一层的分类器。
池化还能降低输出结果的维度,(理想情况下)却能保留显著的特征。你可以认为每个滤波器都是检测一种特定的特征,例如,检测句子是否包含诸如“not amazing”等否定意思。如果这个短语在句子中的某个位置出现,那么对应位置的滤波器的输出值将会非常大,而在其它位置的输出值非常小。通过采用取最大值的方式,能将某个特征是否出现在句子中的信息保留下来,但是无法确定它究竟在句子的哪个位置出现。这个信息出现的位置真的很重要吗?确实是的,它有点类似于一组n-grams模型的行为。尽管丢失了关于位置的全局信息(在句子中的大致位置),但是滤波器捕捉到的局部信息却被保留下来了,比如“not amazing”和“amazing not”的意思就大相径庭。
在图像识别领域,池化还能提供平移和旋转不变性。若对某个区域做了池化,即使图像平移/旋转几个像素,得到的输出值也基本一样,因为每次最大值运算得到的结果总是一样的。
我们需要了解的最后一个概念是通道。通道即是输入数据的不同“视角”。比如说,做图像识别时一般会用到RGB通道(红绿蓝)。你可以对每个通道做卷积运算,赋予相同或不同的权值。你也同样可以把NLP想象成有许多个通道:把不同类的词向量表征(例如word2vec和GloVe)看做是独立的通道,或是把不同语言版本的同一句话看作是一个通道。
我们接下来看看卷积神经网络模型在自然语言处理领域的实际应用。我试图去概括一些研究成果。希望至少能够涵盖大部分主流的成果,难免也会遗漏其它一些有意思的应用(请在评论区提醒我)。
最适合CNNs的莫过于分类任务,如语义分析、垃圾邮件检测和话题分类。卷积运算和池化会丢失局部区域某些单词的顺序信息,因此纯CNN的结构框架不太适用于PoS Tagging和Entity Extraction等顺序标签任务(也不是不可能,你可以尝试输入位置相关的特征)。
文献[1>在不同的分类数据集上评估CNN模型,主要是基于语义分析和话题分类任务。CNN模型在各个数据集上的表现非常出色,甚至有个别刷新了目前最好的结果。令人惊讶的是,这篇文章采用的网络结构非常简单,但效果相当棒。输入层是一个表示句子的矩阵,每一行是word2vec词向量。接着是由若干个滤波器组成的卷积层,然后是最大池化层,最后是softmax分类器。该论文也尝试了两种不同形式的通道,分别是静态和动态词向量,其中一个通道在训练时动态调整而另一个不变。文献[2]中提到了一个类似的结构,但更复杂一些。文献[6]在网络中又额外添加了一个层,用于语义聚类。
Kim, Y. (2014). 卷积神经网络用来语句分类
文献[4]从原始数据训练CNN模型,不需要预训练得到word2vec或GloVe等词向量表征。它直接对one-hot向量进行卷积运算。作者对输入数据采用了节省空间的类似词袋表征方式,以减少网络需要学习的参数个数。在文献[5]中作者用了CNN学习得到的非监督式“region embedding”来扩展模型,预测文字区域的上下文内容。这些论文中提到的方法对处理长文本(比如影评)非常有效,但对短文本(比如推特)的效果还不清楚。凭我的直觉,对短文本使用预训练的词向量应该能比长文本取得更好的效果。
搭建一个CNN模型结构需要选择许多个超参数,我在上文中已经提到了一些:输入表征(word2vec, GloVe, one-hot),卷积滤波器的数量和尺寸,池化策略(最大值、平均值),以及激活函数(ReLU, tanh)。文献[7]通过多次重复实验,比较了不同超参数对CNN模型结构在性能和稳定性方面的影响。如果你想自己实现一个CNN用于文本分类,可以借鉴该论文的结果。其主要的结论有最大池化效果总是好于平均池化;选择理想的滤波器尺寸很重要,但也根据任务而定需;正则化在NLP任务中的作用并不明显。需要注意的一点是该研究所用文本集里的文本长度都相近,因此若是要处理不同长度的文本,上述结论可能不具有指导意义。
文献[8]探索了CNNs在关系挖掘和关系分类任务中的应用。除了词向量表征之外,作者还把词与词的相对位置作为卷积层的输入值。这个模型假设了所有文本元素的位置已知,每个输入样本只包含一种关系。文献[9]和文献[10]使用的模型类似。
来自微软研究院的文献[11]和 [12]介绍了CNNs在NLP的另一种有趣的应用方式。这两篇论文介绍了如何学习将句子表示成包含语义的结构,它能被用来做信息检索。论文中给出的例子是基于用户当前的阅读内容,为其推荐其它感兴趣的文档。句子的表征是基于搜索引擎的日志数据训练得到的。
大多数CNN模型以这样或是那样的训练方式来学习单词和句子的词向量表征,它是训练过程的一部分。并不是所有论文都关注这一步训练过程,也并不在乎学到的表征意义有多大。文献[13]介绍了用CNN模型对Facebook的日志打标签。这些学到的词向量随后又被成功应用于另一个任务 —— 基于点击日志给用户推荐感兴趣的文章。
至此,所有的模型表征都是在单词的层面上。另外有一些团队则研究如何将CNNs模型直接用于字符。文献[14]学到了字符层面的向量表征,将它们与预训练的词向量结合,用来给语音打标签。文献[15]和[16]研究了直接用CNNs模型直接从字符学习,而不必预训练词向量了。值得注意的是,作者使用了一个相对较深的网络结构,共有9层,用来完成语义分析和文本分类任务。结果显示,用字符级输入直接在大规模数据集(百万级)上学习的效果非常好,但用简单模型在小数据集(十万级)上的学习效果一般。文献[17]是关于字符级卷积运算在语言建模方面的应用,将字符级CNN模型的输出作为LSTM模型每一步的输入。同一个模型用于不同的语言。
令人惊讶的是,上面所有论文几乎都是发表于近两年。显然CNNs模型在NLP领域已经有了出色的表现,新成果和顶级系统还在层出不穷地出现。
若有疑问或是反馈,请在评论区留言。谢谢阅读!
[1] Kim, Y. (2014). Convolutional Neural Networks for Sentence Classification. Proceedings
of the 2014 Conference on Empirical Methods in Natural Language Processing (EMNLP 2014), 1746–1751.
[2] Kalchbrenner, N., Grefenstette, E., & Blunsom, P. (2014). A Convolutional Neural
Network for Modelling Sentences. Acl, 655–665.
[3] Santos, C. N. dos, & Gatti, M. (2014). Deep Convolutional Neural Networks
for Sentiment Analysis of Short Texts. In COLING-2014 (pp. 69–78).
[4] Johnson, R., & Zhang, T. (2015). Effective Use of Word Order for Text
Categorization with Convolutional Neural Networks. To Appear: NAACL-2015, (2011).
[5] Johnson, R., & Zhang, T. (2015). Semi-supervised Convolutional Neural Networks for Text Categorization via Region Embedding.
[6] Wang, P., Xu, J., Xu, B., Liu, C., Zhang, H., Wang, F., & Hao, H. (2015). Semantic
Clustering and Convolutional Neural Network for Short Text Categorization. Proceedings ACL 2015, 352–357.
[7] Zhang, Y., & Wallace, B. (2015). A Sensitivity Analysis of (and Practitioners’ Guide to)
Convolutional Neural Networks for Sentence Classification,
[8]Nguyen, T. H., & Grishman, R. (2015). Relation Extraction: Perspective from Convolutional
Neural Networks. Workshop on Vector Modeling for NLP, 39–48.
[9] Sun, Y., Lin, L., Tang, D., Yang, N., Ji, Z., & Wang, X. (2015). Modeling Mention , Context
and Entity with Neural Networks for Entity Disambiguation, (Ijcai), 1333–1339.
[10] Zeng, D., Liu, K., Lai, S., Zhou, G., & Zhao, J. (2014). Relation Classification via
Convolutional Deep Neural Network. Coling, (2011), 2335–2344.
[11] Gao, J., Pantel, P., Gamon, M., He, X., & Deng, L. (2014). Modeling Interestingness with Deep Neural Networks.
[12]Shen, Y., He, X., Gao, J., Deng, L., & Mesnil, G. (2014). A Latent Semantic Model with Convolutional-Pooling Structure for Information Retrieval. Proceedings of the 23rd ACM International Conference on
Conference on Information and Knowledge Management – CIKM ’14, 101–110.
[13]Weston, J., & Adams, K. (2014). # T AG S PACE : Semantic Embeddings from Hashtags, 1822–1827.
[14] Santos, C., & Zadrozny, B. (2014). Learning Character-level Representations for Part-of-Speech Tagging. Proceedings of the 31st International Conference on Machine Learning, ICML-14(2011), 1818–1826.
[15] Zhang, X.Zhao, J., & LeCun, Y. (2015). Character-level Convolutional Networks for Text Classification, 1–9.
[16] Zhang, X., & LeCun, Y. (2015). Text Understanding from Scratch. arXiv E-Prints, 3, 011102.
[17] Kim, Y., Jernite, Y., Sontag, D., & Rush, A. M. (2015). Character-Aware Neural Language Models.
原文链接:Understanding Convolutional Neural Networks for NLP(译者/赵屹华 审核/刘翔宇、朱正贵 责编/周建丁 原创/翻译投稿请联系:[email protected],微信号:jianding_zhou)
译者简介:赵屹华,计算广告工程师@搜狗,前生物医学工程师,关注推荐算法、机器学习领域。