Understanding Convolutional Neural Networks for NLP

本文译自UNDERSTANDING CONVOLUTIONAL NEURAL NETWORKS FOR NLP

当我们听到CNN的时候,我们一下就会想到计算机视觉。CNNs在图像分类的重大突破上做出了巨大的贡献,从Facebook的自动图像标注到自动驾驶,CNNs当今大多数计算机视觉系统的核心。

最近,我们也开始应用CNNs解决NLP中遇到的问题,取得了一些有趣的结果。本文将尽力解释什么是CNNs,它怎么用于NLP。CNNs 背后的直觉让理解计算机视觉的应用案例更容易,因此我们将从这里开始,然后逐渐转向NLP。

What is convolution?

对于我来说理解卷积最简单的方法是把它想成一个应用到矩阵上的滑动窗口函数。看一下图像可能更清楚:

想象左边的矩阵表示一张黑白的图片。每一个输入表示一个像素,0表示黑色,1表示白色(对于灰度图来说,取值通常介于0~255)。滑动窗口叫做kernel, filter, 或者 feature detector。这里我们用一个 3x3 的filter,和原始矩阵做点乘,然后相加。为了得到所有的卷积,我们通过在整个矩阵上滑动filter对每个元素都这样做 (To get the full convolution we do this for each element by sliding the filter over the whole matrix.)。

你可能会想用这个你可以做什么?这里是一些直接的例子。
对每个像素,结合它的邻值做平均来模糊图像:
来源: http://www.wildml.com/2015/11/understanding-convolutional-neural-networks-for-nlp/
根据像素和它的近邻像素的差异来检测边缘:
(To understand this one intuitively, think about what happens in parts of the image that are smooth, where a pixel color equals that of its neighbors: The additions cancel and the resulting value is 0, or black. If there’s a sharp edge in intensity, a transition from white to black for example, you get a large difference and a resulting white value)
来源: http://www.wildml.com/2015/11/understanding-convolutional-neural-networks-for-nlp/
GIMP manual 有一些其它的例子。为了理解更多关于卷积是如何工作的,我推荐看一下Chris Olah’s post on the topic

What are convolutional neural networks?

现在你知道什么是卷积了,但是什么是CNNs?CNNs只是几层具有非线性激活函数,像 ReLU or tanh 的卷积层。在传统的前馈神经网络中,我们连接每个输入神经元到下一层的每个输出神经元。这也叫做全连接层,或者仿射层。在CNNs中,我们不这样做。相反,我们在输入层使用卷积来计算输出。这会得到一个局部连接,也就是输入的每个小区域都被连接到输出的每个神经元。每层应用不同的filters,可以是成百上千个filters,然后把它们的结果连接起来。这也叫做pooling (subsampling) layers,我们后面会讲。在训练阶段,根据你想要进行的任务,CNN会自动学习filters的值。比如在图像分类中,第一层CNN可能会从原始的像素中学习检测边缘,然后在第二层用边缘来检测简单的形状,用这些形状来得到更高层次的特征,比如在更深层的面部形状。最后一层是使用这些高阶特征的分类器。

该计算有两方面值得注意:Location InvarianceCompositionality。假如你想要区分图片上是否有大象🐘,因为你在整个图像上滑动你的filters,你并不在意大象出现在那里。在实践中,pooling给你提供 invariance 到 translation,rotation 和 scaling等等。第二个主要的方面是 (local) compositionality。每一个filter都把低阶特征的一个局部patch构成更高阶的表示,这就是为什么CNNs在计算机视觉中如此强大的原因。直觉上这是合理的,你从像素构建出边缘,从边缘构建出形状,从形状构建出更复杂的对象。

So, how does any of this apply to NLP?

和图像像素不同,大多数NLP任务的输入是表示成矩阵的句子或者文档。矩阵的每一行对应一个token,也就是一个word,但是它可以是一个字符。也就是说每一行是一个向量,它代表一个word。通常来说,这些向量是word embeddings(低维表示),像 word2vec 或者 GloVe,但是也可以是索引词到词汇表的one-hot编码的向量。对于一个使用100维embedding的包含10个word的句子,我们的输入是一个10*100的矩阵,这就是我们的“图像”。

在计算机视觉中,filters在图像的局部patches上滑动,但是在NLP领域,我们常常使用在矩阵的整行(words)上滑动的filters。也就是filters的宽和输入矩阵的宽度相同。高或者说区域的大小可能会变化,但是常用的滑动窗口一次包含2-5个words(花几分钟尝试理解一下这幅图,以及维度是如何计算的。现在你可以先忽略pooling,后续我们会解释):

我们在计算机视觉中有的良好的直觉是什么?Location Invariance 和 local Compositionality 在图像方面是合理的,但是在NLP中并不是。你可能很关心word到底出现在句子中的哪个地方?相邻的像素很可能是语义相关的(相同对象的一部分),但是这种关系对words并不总是成立。在许多语言中,短语的某些部分可能被几个其他words给分开。compositional 方面也不明显。很显然,words以某些方式组成在一起,比如,形容词修饰名词,但是这到底如何组成,高阶表示实际上说明了什么并不像计算机视觉中的应用案例那样明显。

考虑所有的这些,CNNs似乎并不是很适合NLP领域的任务。Recurrent Neural Networks在直觉上更合理。它们模仿我们怎么处理语言(或者我们认为我们如何处理语言): 从左往右按顺序读。幸运地是,这并不意味着CNNs不起作用。All models are wrong, but some are sueful。CNNs在NLP问题上表现地相当好。简单的词袋模型很显然过于简单,有着不正确的假设,但是尽管如此,这些年它都是标准的方法,并且也有不错的结果。

CNNs的一个特点是速度快,非常快。卷积是计算机图像学的一个核心部分,在基于GPUs的硬件层面上实现。和n-grams相比,CNNs就表示而言也是很高效的。对于很大的词汇表,计算超过 3-grams的任务的代价很快就非常昂贵。即使是Google也不提供超过5-grams的任务。Convolutional Filters自动学习好的表示,不需要表示整个词汇表。filters的大小超过5也是非常合理的。我可能会想,在第一层许多学习出来的filters是在学习类似(但不限于)n-grams的特征,只是用一种更压缩的方式来表示它们。

CNN Hyperparameters

在解释CNNs如何应用于NLP任务之前,我们先看一看构建CNN时,你需要做的一些选择。希望这会帮助你更好地理解这个领域的文献。

Narrow VS. Wide Convolution

当我在上面解释卷积的时候,我忽略了我们如何应用filter的一些细节。在矩阵中心应用一个3×3 的filter工作的很好,但是在边缘呢?你如何将filter 应用在矩阵的第一个元素,这个元素的上面和左边都没有相邻元素?你可以使用0填充。所有落在矩阵外面的元素都取零。这样做,你就可以把filter应用到输入矩阵的每一个元素上,然后得到一个更大或者相等大小的输出。加入零填充也叫做wide convolution,不使用零填充就是narrow convolution。一个1维的例子如下:

可以看到,当你用一个相对输入来说较大的filter时,wide convolution很有用的,甚至是必须的。在上图,narrow convolution 得到大小为 \((7-5)+1=3\) 的输出,wide convolution的输出大小为 \((7+2*4-5)+1=11\)。更一般地,输出大小的公式是 \(n_{out}=(n_{in}+2*n_{padding}-n_{filter})+1\)。

Stride size

and consecutive applications of the filter overlapped. A larger stride size leads to fewer applications of the filter and a smaller output size. The following from the Stanford cs231 website shows stride sizes of 1 and 2 applied to a one-dimensional input:
卷积的另一个超参是stride size,它定义每一步你的filter移动多少。在上面所有的例子中,stride size都是1。filter的连续作用是重叠的。更大的 stride size 会得到更少的filter作用和更小的输出。下图来自Stanford cs231 website,显示了stride size 1 和 stride size 2 分别作用在1维输入的情况:

在文献中,我们常常看到stride size为1,但是更大的stride size会让你构建出表现和Recursive Neural Network(看起来像树)相似的模型。

Pooling layers

Convolutional Neural Networks的一个很重要的方面是pooling层,常常应用在卷积层后面。Pooling层子抽样他们的输入。做pooling最常用的方法是在每一个filter的结果之上做一个\(max\)操作。你不需要在整个矩阵上做pool,你也可以在一个window上做pool。比如,下图显示了一个对于2x2 window的max pooling(在NLP中,我们常常应用pooling到整个输出,每个filter只获得一个数值):

为什么pooling?这里有几个原因。pooling的一个特性是提供了一个固定大小的输出矩阵,这常常是分类需要的。比如,如果你有1000个filters,然后你对每个都应用 max pooling,你会得到一个1000维的输出,和filters的大小,或者输入的大小无关。这让你可以使用可变长的句子,可变大小的filters,但是总是得到相同的输出维度,然后喂给分类器。

Pooling也会减少输出的维度,但是(希望)保持最重要的信息。你可以认为每个filter都是用来探测特定的特征的,比如探测句子是否包含像“not amazing”的否定词。如果这个短语出现在句子中的某个地方,应用filter到那个区域的结果是获得一个很大的值,但是在其他区域得到的是较小的值。通过进行max操作,你可以保持特征是否出现在句子中的信息,但是你丢失了它到底出现在哪的信息。但是关于位置的信息不是真的有用吗?是的,它没有用,这和n-grams的词袋模型做的事情有一点类似。你丢失了位置的全局信息(where in a sentence something happens),但是你保持了filters捕获的局部信息,比如“not amazing”和“amazing not”就很不同。

在图像识别中,pooling也提供了基本的invariance 到 translating (shifting) 和 rotation。当你在一个区域进行pooling,输出大致上将保持相同,即使你shift/rotate图像的一些像素,因为max操作会找到相同的值。

Channels

最后一个需要理解的概念是channels。比如,在图像识别中,你通常有RGB (red, green, blue)通道。你可以跨通道应用卷积,以不同的或者相同的权重。在NLP中,你也可以想象有多个通道:你可以对不同的word embeddings (比如word2vecGloVe)有一个单独的通道,或者对用不同的语言表示的相同的句子/不同形式的短语,你可以有一个通道。

Convolutional Neural Networks Applied to NLP

现在让我们看看CNNs在NLP领域的一些应用。我也会阐述一些研究结果,当然我可能会错过许多有趣的应用(请在评论中让我知道),但是我希望至少包含一些受欢迎的结论。

对于CNNs应用在NLP,最自然的似乎是分类任务,比如情感分类,垃圾过滤或者主题分类。卷积和pooling操作会丢失words的局部顺序的信息,因此如词性标注或者命名实体识别的序列标注较难应用到纯碎的CNN架构(但是不是不可能,你可以加入输入的位置特征)中。

[1] 在各种分类(主要由情感分析,主题分类任务组成)数据集上评价CNN架构。在跨数据集上,CNN架构取得了非常好的表现,甚至有一些非常好。令人惊讶地是这篇论文中使用的网络相当简单,输入层是由相连的word2vec word embeddings 组成的句子。然后是有多个filters的卷积层,max-pooling层,最后是一个softmax分类器。这篇论文也用两个不同的通道进行了实验,一个是静态的word embeddings,一个是动态的word embeddings,也就是在训练的时候,一个通道被调整,另一个不动。一个相似但更复杂的架构在[2]中更早被提出。[6]增加了一个额外的层对这个网络架构进行”语义聚类”。

4 Trains a CNN from scratch, without the need for for pre-trained word vectors like word2vec or GloVe. It applies convolutions directly to one-hot vectors. The author also proposes a space-efficient bag-of-words-like representation for the input data, reducing the number of parameters the network needs to learn. In 5 the author extends the model with an additional unsupervised “region embedding” that is learned using a CNN predicting the context of text regions. The approach in these papers seems to work well for long-form texts (like movie reviews), but their performance on short texts (like tweets) isn’t clear. Intuitively, it makes sense that using pre-trained word embeddings for short texts would yield larger gains than using them for long texts.

Building a CNN architecture means that there are many hyperparameters to choose from, some of which I presented above: Input represenations (word2vec, GloVe, one-hot), number and sizes of convolution filters, pooling strategies (max, average), and activation functions (ReLU, tanh). 7 performs an empirical evaluation on the effect of varying hyperparameters in CNN architectures, investigating their impact on performance and variance over multiple runs. If you are looking to implement your own CNN for text classification, using the results of this paper as a starting point would be an excellent idea. A few results that stand out are that max-pooling always beat average pooling, that the ideal filter sizes are important but task-dependent, and that regularization doesn’t seem to make a big different in the NLP tasks that were considered. A caveat of this research is that all the datasets were quite similar in terms of their document length, so the same guidelines may not apply to data that looks considerably different.

8 explores CNNs for Relation Extraction and Relation Classification tasks. In addition to the word vectors, the authors use the relative positions of words to the entities of interest as an input to the convolutional layer. This models assumes that the positions of the entities are given, and that each example input contains one relation. 9 and 10 have explored similar models.
Another interesting use case of CNNs in NLP can be found in 11 and 12, coming out of Microsoft Research. These papers describe how to learn semantically meaningful representations of sentences that can be used for Information Retrieval. The example given in the papers includes recommending potentially interesting documents to users based on what they are currently reading. The sentence representations are trained based on search engine log data.

Most CNN architectures learn embeddings (low-dimensional representations) for words and sentences in one way or another as part of their training procedure. Not all papers though focus on this aspect of training or investigate how meaningful the learned embeddings are. 13 presents a CNN architecture to predict hashtags for Facebook posts, while at the same time generating meaningful embeddings for words and sentences. These learned embeddings are then successfully applied to another task – recommending potentially interesting documents to users, trained based on clickstream data.

Character-level CNNS

目前为止,我们说的所有模型都是基于words的。但是也有研究把CNNs直接应用到characters的。[14] 学习了character-level embeddings,用预训练的word embeddings结合它们,然后用CNN进行词性标注。[15][16] 探索了CNNs的用法,直接从characters中学习,不需要任何预训练的embeddings。值得注意的是,作者用了一个相当深的网络,有9层,然后将它应用到情感分析和文本分类任务。结果显示直接从character-level的输入中学习在大数据集(百万样本)上表现地相当好,但是在较小的数据集(几百几千个样本)上的表现不如简单的模型。[17] 探索了character-level convolutions 在语言模型上的应用,在每一步,使用character-level CNN 的输出作为 LSTM 的输入。相同的模型应用在各种各样的语言。

令人兴奋的是上面的论文基本都是在过去的1-2年出版的。显然之前就已经有CNNs在NLP的优秀成果。比如Natural Language Processing (almost) from Scratch,但是新的研究成果和最先进的系统出版的步调很显然正在加速。