研究问题
关于Convlutional Neural Networks(CNNs)在自然语言处理领域的应用。
这一次的研究主要是针对文本分类这一个问题所展开。
研究方法
- 了解卷积神经网络。
- 阅读paper,深入理解CNN在NLP中的模型细节。
- 类比CNN在CV中的应用,总结两者异同。
- 代码实现
附加:
类比信号与系统,理解卷积的真正意义,体会信号系统与图像处理中卷积模型所起的不同作用。
拟采用的方法
CNNs的定义
卷积神经网络本质上是由多个卷积层,经过激活函数后产生输出的网络。可以将其大致划分为输入层,卷积层,池化层,全连接层和输出层。
卷积层
其中卷积层的工作,是通过定义一系列卷积核,依次扫过图像中的每个区域,产生一个新的特征矩阵的过程。因此,卷积核在训练完成后,更像是一个特征过滤器,在扫过图像的过程中,忽略掉与特征不相符的部分,而将符合这种特征的部分放大化(通常是分配一个较大的权重)。
池化层
池化层通常会紧跟在卷积层之后,对卷积层所产生的特征矩阵进行子采样(可能是去平均值,或者取最大值),最终得到一个固定大小的矩阵。
池化层主要的作用有两方面。
第一,固定大小。经由卷积层产生的特征矩阵通常是大小不定的,所以我们无法将它直接投入到全连接层进行后续工作,而池化层在这里起到了“连接器”的作用,它将一个不定大小的矩阵转化成了固定大小的矩阵,随时可以喂食到后续神经网络。
第二,信息提取。池化层在降低输出维度的同时,保留了原始特征矩阵总最显著的信息,并不会因为维度的降低而导致信息的丢失。
全连接层
所谓全连接是指,本层所有的神经元都与下一层的神经元相连。因为相对简单,这里也不再赘述。
总结
通过观察下图我们可以发现,一个CNNs会由多个卷积层,多个池化层,经由全连接层产生输出。
全连接层起到的作用很容易理解,通常也只是分类,回归等任务。那么介于原始图像,和分类网络之间的卷积层和池化层究竟起了什么作用?
我认为,它们两层合起来可以用“特征提取层”来概括,也就是说,卷积层和池化层其实是一个特征抽取,数据加工的过程,将原始的图像经过一系列的处理,加工,才能产生易于分类的数据类型,然后进行分类工作。
CNNs在NLP中的模型
类比CNNs在图像处理中的应用,NLP中的模型实际上是将一个句子以图的概念理解。
initialize
在初始化特征矩阵时,句子中的每一个单词的word embeddings作为矩阵的一行,得到一个n*k的矩阵,其中n是句子中的单词数,k是词嵌入模型的维度,也可以是one-hot模型,但是考虑到样本的稀疏度,一般会采用低维的词嵌入模型。
filter
在CV中,一般我们的卷积核filters会扫过图像的局部像素,但在NLP中,通常会只改变filters的长度,而宽度始终等于词嵌入维度k,也就是说,filter的最小单位一整行,一个完整的word。而通常情况下,会使用一个长度为2~5的filter,这是因为实际情况中,很少会出现5个词以上的长组合短语。
pooling
CV中的池化操作通常会产生一个n * m的特征矩阵,但在NLP中,产生的特征矩阵会是一个n * 1的矩阵。
产生这种不同的原因是因为,在图像中,传递信息的是一个像素块,而在自然语言处理中,传递信息的是一个word,图像本身就是二维的,所以需要用二维去传递信息,但是对于词而言,第二个维度k实际上是词嵌入的维度,并不会传递有关于这个句子的信息。
channel
CV处理中,我们会遇到RGB图分为R,G,B三个channel分析的情况。在NLP中,似乎看上去一个channel就足够来做分析。
但是在原始paper中提到,NLP中的CNNs模型也可以有多个通道,一般是static
和fine-tuned
。
static
通道在backpropagation的过程中是静态的,保持不变的,但是fine-tuned
通道会随着训练发生变化。
除了这两个通道之外,我们也可以增加另外的通道,比如改变句子的语言,或者将句子替换成其他同义句等等。
实验
实验部分用tensorflow实现了CNN-rand模型,数据集采用原paper中的MR数据集,这是一个关于电影评价的数据集,数据集将评价分为了positive和negetive两个部分。
由于这个数据集本身较小,而且没有dev,因此抽取了其中的10%作为dev。
Embedding Layer
第一部分是embedding layer,将一个词映射成低维word embedding。这里没有采用google的word2vec
是因为这个模型本身较为简单,而且笔记本算力有限,所以自己构建了一个简单的embedding layer。
with tf.device('/cpu:0'), tf.name_scope("embedding"): self.W = tf.Variable( tf.random_uniform([vocab_size, embedding_size], -1.0, 1.0), name="W") self.embedded_chars = tf.nn.embedding_lookup(self.W, self.input_x) self.embedded_chars_expanded = tf.expand_dims(self.embedded_chars, -1)复制代码
W是一个随机均匀分布,维度为vocab_size * embedding_size
的初始化矩阵。
tf.nn.embedding_lookup
方法创建了一个词嵌入矩阵,它的维度和W一致,返回的类型为[None, sequence_length, embedding_size]
。
tf.expand_dims(self.embedded_chars, -1)
在返回值的最后一个维度插入1,变成[None, sequence_length, embedding_size, 1]
,这里的1代表的通道数,在后续con2d卷积方法中会使用。
Convolution and Max-Pooling Layer
卷积和池化模型的建立。
因为是CNN-rand模型,所以通道数为1,且采用窄卷积的方式进行卷积操作。
pooled_outputs = [] for i, filter_size in enumerate(filter_sizes): with tf.name_scope("conv-maxpool-%s" % filter_size): # Convolution Layer filter_shape = [filter_size, embedding_size, 1, num_filters] W = tf.Variable(tf.truncated_normal(filter_shape, stddev=0.1), name="W") b = tf.Variable(tf.constant(0.1, shape=[num_filters]), name="b") conv = tf.nn.conv2d( self.embedded_chars_expanded, W, strides=[1, 1, 1, 1], padding="VALID", name="conv") # Apply nonlinearity h = tf.nn.relu(tf.nn.bias_add(conv, b), name="relu") # Max-pooling pooled = tf.nn.max_pool( h, ksize=[1, sequence_length - filter_size + 1, 1, 1], strides=[1, 1, 1, 1], padding='VALID', name="pool") pooled_outputs.append(pooled) # Combine all the pooled features num_filters_total = num_filters * len(filter_sizes) self.h_pool = tf.concat(pooled_outputs, 3) self.h_pool_flat = tf.reshape(self.h_pool, [-1, num_filters_total])复制代码
这里的filter_size = "3,4,5"
,是采用长度分别为3,4,5的三种filter对词嵌入矩阵进行卷积。
W是卷积核,用截断正态分布进行初始化。b是偏置数,h为卷积输出经过激活函数以后的结果。
tf.nn.conv2d
中的strides
和padding
参数,分别表示步长和卷积方式。'VALID'是窄卷积,产生的输出类型为[1, sequence_length - filter_size +1, 1, 1]
。
卷积结果经过一个ReLU激活后,放入池化层。
池化层是一个Max-pooling,会将卷积输出的n * 1的矩阵中取出一个最大值。然后将同一个filter的Max-pooling结果连接起来,最终得到的类型为[filter_num, 1]
。
最后将所有filter的结果相连,得到最终经过Convolution和Pooling的矩阵。
讨论
和原paper中给的76%比较接近,误差产生的原因可能在于我没有采用交叉验证,数据集较小的情况下可能发生过拟合的情况。
通过这一次课题研究,我试图去寻找CV和NLP在使用CNNs时的共性,我个人认为,他们都是通过特征的部分提取去创造特征来进行最终的分类训练。
但是在CV中,卷积核最终的训练形态比较容易理解,应该是一个图像的局部特征,比如一个圆,一个三角形,或是一条线等等。但在NLP中,却很难去定义卷积核最终到底产生了什么特性,它提取出来的特征到底是什么,这一点是我做完这一次研究后所困扰的。
总体而言,CNNs在NLP中的应用是将文本以图像的表示形式进行处理,虽然可能在一些方面的概念比较模糊,但卷积操作的速度,也使得CNNs模型在文本分类这一方向取得了不小的进步。