一文读懂CNN如何用于NLP

8,666 阅读16分钟

原文:一文读懂CNN如何用于NLP

作者:Denny Britz

翻译:Kaiser


当我们听到“卷积神经网络”(CNN,当然,不是特朗普说Fake News那个CNN),通常会想到计算机视觉。图像分类的重大突破,以及当下大多数计算机视觉系统的核心,都要归功于CNN,从{:facebook:}的自动照片打标签到自动驾驶汽车不外如是。


关于卷积处理图像相关问题,之前已有若干文章,感兴趣的读者可查看:

最近我们也开始把CNN应用在自然语言处理问题上,并且搞出一些挺有意思的结果。本文我会总结CNN是什么,以及如何用于NLP。CNN背后的原理从计算机视觉角度可能更好理解,所以我会先从CV开始讲,然后过渡到NLP。


什么是卷积?

理解卷积最简单的方式,就是把它想象成一个扫过矩阵的滑动窗口函数。听起来还是有点迷糊,但是可以化之后就清楚多了:

Convolution with 3×3 Filter. Source: http://deeplearning.stanford.edu/wiki/index.php/Feature_extraction_using_convolution


想象左边的矩阵表示黑白图片。每个元素对应一个像素,0是黑,1是白(一般来说是0-255之间的数字,表示灰度图片)。滑动窗口是卷积核(kernel),滤波器(filter),或者“特征探测器”(feature detector)。

上图用的是3x3的卷积核,与原矩阵对应元素相乘(注意不是矩阵乘法)然后求和,让滤波器扫遍整个矩阵得到最终的完整卷积。你可能会奇怪这有毛用,以下是几个直观的例子。


图像模糊:求每个像素与邻近像素地平均值


边缘检测:求像素与相邻像素地差

如果一张图片是纯平没有任何色差的,那么每一个像素减去周围像素的结果都将是0,或者黑色。如果有一个明显的边缘(两侧差别很大),那经过卷积之后就会留下白色。

GIMP manual有几个其他的示例,如果还想深入了解卷积,推荐阅读Chris Olah’s post on the topic




什么是卷积神经网络?

现在你已经知道什么是卷积了,那CNN呢?CNN其实就是很多层卷积加上非线性激活函数(non-linear activation functions)比如ReLUtanh

在传统的前馈神经网络里,我们把每个神经元都连接到下一层的每个神经元上。这就是全连接层(fully connected layer)或仿射层(affine layer)。CNN里我们不这么干,而是对输入层进行卷积得到输出,这就不是“全连接”而是“局部连接”(local connection)————输入的一片区域连接到单个神经元上。每一层都应用不同的卷积核,像上面看到的那种,来上几百甚至上千个,再把结果组合起来。这就是池化(pooling,下采样)层,不过后面还会细说。


在训练过程中,根据任务设置,CNN会自动学习滤波器的值。比如在图像分类当中 ,CNN可能会从第一层的原始图片中学到边缘检测,然后在第二层通过边缘学习简单的形状,最后用形状检测出人脸这样的高级特征、最后一层就是使用高级特征的分类器。


这个计算过程中还有两个点值得注意:位置不变性(location invariance)和组合性(compositionality)。比方说你想判断图里有没有大象,其实你并不关心大象具体在哪。

第二个关键点就是(局部)组合性。每个卷积核都提取出了一批低级特征,用作之后的高级表达,这也是为什么CNN在计算机视觉上特别好使。就像点动成线,线动成面。


然后呢?怎么用在NLP上?

回到我们的主题——自然语言处理上面,多数任务的输入都不再是图片像素,而是以矩阵表示的句子或者文档。矩阵的每一行对应一个token,一般是一个单词或者字符。也即每行代表一个词向量,通常是像word2vecGloVe词嵌入(word embedding,低维表示),但这些只能是独热码向量(one-hot vector),10个单词的句子用100维嵌入,那输入矩阵就是10x100,这就是我们的“图像”。

在视觉问题中,卷积核滑过的是图像的一“块”区域,但在自然语言领域里我们一般用卷积核滑过矩阵的一“行”(单词)。然后卷积核的“宽度”就是输入矩阵的宽度,“高度”可能会变,但一般是每次扫过2-5个单词。

综上所述,一个用于NLP的卷积神经网络看起来大概是这样:

Illustration of a Convolutional Neural Network (CNN) architecture for sentence classification. Here we depict three filter region sizes: 2, 3 and 4, each of which has 2 filters. Every filter performs convolution on the sentence matrix and generates (variable-length) feature maps. Then 1-max pooling is performed over each map, i.e., the largest number from each feature map is recorded. Thus a univariate feature vector is generated from all six maps, and these 6 features are concatenated to form a feature vector for the penultimate layer. The final softmax layer then receives this feature vector as input and uses it to classify the sentence; here we assume binary classification and hence depict two possible output states. Source: Zhang, Y., & Wallace, B. (2015). A Sensitivity Analysis of (and Practitioners’ Guide to) Convolutional Neural Networks for Sentence Classification.


前面提到的“位置不变性”和“局部组合性”对于图像而言很直观,但在NLP里就有点隐晦了。你关心的可能是一个单词出现在句子的什么位置。相邻的图像像素在语义上也是有关联的(都属于同一个物体),但对单词来讲却不是。在很多语言中,一个短语可能被好几个别的单词分割开。

组合性也不是很明显,虽然我们都知道意思是经过词汇组合表达出来的,比如形容词修饰名词,但是低级表征如何组成高级表征并不像图像那样是简单的空间几何关系。


这样看来CNN可能不是很适合做NLP?循环神经网络, Recurrent Neural Networks似乎更合适。RNN很像我们人类处理语言(至少是我们自认为处理语言):从左到右顺序阅读。

所幸,这并不是说CNN就不能用,俗话说得好,All models are wrong, but some are useful.。实际上CNN在NLP问题上表现得还非常好,一个简单Bag of Words model看起来显然过于简单了,而且理论前提就很不靠谱,但却已经是多年来的标准模式,而且产生的结果也很好。

CNN的一大优势在于快,非常快,卷积是计算机图形学的核心部分,已经可以通过GPU硬件级实现。与n-grams等方法相比,CNN在表征方面也更加高效。随着词汇量越来越大,计算任何多于3-grams的东西都会非常昂贵。即便是{:google:}都没有提供出超过5-grams。卷积滤波器自动学习表征,不需要表示所有词汇,而大于5的卷积核是很平常的事。我觉得第一层的很多卷积核都是捕捉了与n-grams相似的特征,但是以更简洁的形式表达了出来。




卷积神经网络超参数

在解释CNN如何用于NLP任务之前,先来看看你在搭建CNN时都需要做哪些选择,希望这些能够帮助你更好的理解。


卷积:宽vs.窄

之前在解释卷积的时候我省略了应用核的一个小细节。3x3的核在图像中心没问题,那如果是边缘呢?对于矩阵的第一个元素,左方和上方没有临近元素怎么办?我们可以使用zero-padding。所有(其实并不存在)掉在矩阵之外的元素都用0填充。这样就可以对输入矩阵的所有元素使用卷积核,得到更大或同样大小的输出。zero-padding也叫宽卷积,如果不用zero-padding就是窄卷积。1D的例子如下图:

Narrow vs. Wide Convolution. Filter size 5, input size 7. Source: A Convolutional Neural Network for Modelling Sentences (2014)


如果你有一个挺大的卷积核(相对于输入尺寸而言),那宽卷积还是很有效、甚至必要的。在上图中,窄卷积输出尺寸是(7-5)+1=3,而宽卷积输出的是(7+2*4-5)+1=11。输出尺寸的一般计算公式是:n_{out}=(n_{in}+2*n_{padding}-n_{filter})+1


步幅(stride size)

卷积核的另一个超参数是步幅,也就是每一步移动卷积核的距离。之前的例子中,步幅都是1,相连的卷积核互相重叠。步幅越大,卷积计算次数越少,输出尺寸越小。来自Stanford cs231 website的下图表示1D输入中,步幅分别为1和2的效果。

Convolution Stride Size. Left: Stride size 1. Right: Stride size 2. Source: http://cs231n.github.io/convolutional-networks/

1是最常见的步幅,而更大的步幅可以用来搭建类似Recursive Neural Network的模型,如同树状。


池化层

为什么要池化?原因有多重。一是池化可以保持输出尺寸固定,对分类来说这是必要的。比如你有1000个卷积核,分别作max pooling,不论输入层或滤波器多大,得到的都是1000维的输出。

池化层(pooling layer)也是卷积神经网络里的一个核心概念,在卷积层之后应用。池化层对他的输入进行“下采样”,最常用的方法是max操作,也就是只保留最大值,当然不是非要在整个矩阵上进行,也可以是经过分割窗口。比如下面的例子展示的是2x2窗口的最大池化(但在NLP中我们一般对整个矩阵进行池化,每个卷积核只保留一个数):

Max pooling in CNN. Source: http://cs231n.github.io/convolutional-networks/#pool

池化也减小了输出维度但(希望能够)保留最显著的信息。你可以认为每个卷积核的使命是发现一种特征,比如探测一句话是否含有"not amazing",如果这个短语在句子中出现,卷积在这一部分的结果数值就会比较大,其他部分较小。而执行了max操作之后,得到的只有这一特征是否存在,具体的位置信息便丢失了。那么这个位置信息重要吗?其实没那么重要,就像是n-grams模型做的那样。你失去了关于位置的全局信息(在句子的哪个地方发生),但是保留了滤波器得到的局部信息,也就是"not amazing"出没出现,这跟"amazing not"意义完全不同。

在图像识别当中,池化也提供了平移和旋转的不变性。当你对一片区域做池化时,平移还是旋转几个像素的影响不大,因为max还是会选择原来的值。


通道

最后一个我们需要了解的概念是通道(channel)。通道就是输入数据的不同“视图”,比如在图像识别里,通道经常是RGB(red, green, blue)通道,可以对每个通道分别进行卷积操作,会得到不同的权重。

在NLP当中也可以想象我们拥有多个通道:不同的词嵌入(word2vec和GloVe)有不同的通道,或者同一句话用不同语言表述形成多个通道,或者是一个意思的多种表达方式。




用于自然语言处理的卷积神经网络

来看看CNN在NLP中的应用,我会先总结一些研究成果。当然很多有意思的应用这里讲不到,但是我希望能至少涵盖比较热门的几个。

最天然适合于CNN的应该是分类任务,比如情感分析(Sentiment Analysis),垃圾检测(Spam Detection)和主题分类(Topic Categorization)。卷积和池化丢失了词的局部顺序信息,所以单纯用CNN做序列标注比如 PoS Tagging 或 Entity Extraction 就稍微难点儿(但也不是不行,可以把位置特征也加入到输入里)。

[1]在多个分类数据集上评估了CNN架构,这些数据集主要是情感分析和主题分类任务。CNN取得了很好的成果,甚至在个别数据集上达到了先进水平。特别是,这篇论文中所用到的CNN结构非常简单,却很强力。输入层是由word2vec词嵌入组成的语句,然后是若干个卷积核,再来最大池化,最后 softmax 分类。论文还试验了两种不同的通道:静态与动态词嵌入,一个通道随着训练不断调整,另一个保持不变。[2]提到了一个类似但稍微复杂点的架构,[6]又在网络里加了一层“语义聚类”。

Kim, Y. (2014). Convolutional Neural Networks for Sentence Classification


[4]从零训练了一个卷积神经网络,不需要预训练词向量比如word2vec或GloVe,而是直接对独热码向量做卷积。作者还提出了一种类似词袋模型的空间高效表征方式用于输入数据,减少了网络需要学习的参数数量。[5]的作者拓展了这个模型,加入了无监督的“区域嵌入”,用CNN预测上下文。这些论文中的方法都在长文章上表现良好(比如{:video-camera:}影评),但是对短文章(比如{:twitter:}tweets)尚不清楚。根据直觉来看,引入预训练词嵌入对模型在短文数据集上的表现帮助会更大。

搭建CNN架构意味着有很多超参数要选择,有些是之前提到过的:

  • 输入表征(word2vec, GloVe, one-hot)
  • 卷积核的数量、尺寸
  • 池化策略(max, average)
  • 激活函数(ReLU, tanh)

[7]对CNN的不同超参数做了个经验评估,调研它们的表现和多次运行之间的变化,如果你想部署自己的CNN用作文本分类,这篇论文可以当做起始参考。有些结果很显著,比如最大池化总是好于平均池化,理想的卷积核尺寸很重要,但是每个任务却都不一样,正则化在NLP里起到的作用不大。这些数据在文档长度上都比较接近,所以对于文本量差异很大的数据,这里的经验未必适用。

[8]把CNN用于关系提取(Relation Extraction)和关系分类(Relation Classification)任务。除了词向量以外,作者还用了词的相对位置作为卷积层输入。这个模型假设词的位置确定之后,每个样例就会含有一种关系。[9]和[10]也探索了类似模型。

另一个把CNN用在NLP上的有趣实例可见于[11]和[12],来自微软研究院。这些论文描述了句子的语义表征可用于信息检索(Information Retrieval)。论文中给出的例子是根据用户正在阅读的材料,推荐他们可能感兴趣的文档。句子表征基于搜索引擎日志数据训练。

大多数CNN架构学习的是在训练过程当中的嵌入(低维表征),但并非所有论文都集中在训练或研究学习出来的嵌入有多少意义。[13]用CNN预测Facebook posts的标签,同时为词句生成有意义的嵌入。这些学得的嵌入还可以用在其他任务——基于点击流数据的兴趣推荐。


字符级卷积神经网络

至此,大部分介绍过的模型都是基于词汇的。但是还有些研究将CNN直接用到字符层面上。[14]学习出了字符级的前途,把他们加入到词嵌入当中,然后把CNN用到语音标签当中。[15][16]让CNN直接从字符里学习,连预训练嵌入都不用。注意,作者用了相对深得网络(9层),然后用于情感分析和文本分类。结果显示在大型数据集上还不错(上百万样本),但在小数据集上反而不如简单模型(十万级样本)。[17]用字符级CNN的输出当作LSTM每个时间步的输入,这个模型也用在了多种语言上。

好消息是,上面我们提到的论文都是近1-2年发布的(现在要在往前推一年了),虽然之前已经有了一些很赞的成果比如Natural Language Processing (almost) from Scratch,但新成果的发表和系统应用仍在持续加速发展。


推荐阅读

“女生科技体验节” TensorFlow Workshop

这评论有毒!——文本分类的一般套路

我做了一个叫“瑟曦”的机器人,可是她动不动就想让格雷果爵士弄死我。



参考文献