关于Convlutional Neural Networks(CNNs)在天然语言处理领域的应用。python
这一次的研究主要是针对文本分类这一个问题所展开。网络
类比信号与系统,理解卷积的真正意义,体会信号系统与图像处理中卷积模型所起的不一样做用。app
卷积神经网络本质上是由多个卷积层,通过激活函数后产生输出的网络。能够将其大体划分为输入层,卷积层,池化层,全链接层和输出层。dom
其中卷积层的工做,是经过定义一系列卷积核,依次扫过图像中的每一个区域,产生一个新的特征矩阵的过程。所以,卷积核在训练完成后,更像是一个特征过滤器,在扫过图像的过程当中,忽略掉与特征不相符的部分,而将符合这种特征的部分放大化(一般是分配一个较大的权重)。ide
池化层一般会紧跟在卷积层以后,对卷积层所产生的特征矩阵进行子采样(多是去平均值,或者取最大值),最终获得一个固定大小的矩阵。函数
池化层主要的做用有两方面。google
第一,固定大小。经由卷积层产生的特征矩阵一般是大小不定的,因此咱们没法将它直接投入到全链接层进行后续工做,而池化层在这里起到了“链接器”的做用,它将一个不定大小的矩阵转化成了固定大小的矩阵,随时能够喂食到后续神经网络。spa
第二,信息提取。池化层在下降输出维度的同时,保留了原始特征矩阵总最显著的信息,并不会由于维度的下降而致使信息的丢失。3d
所谓全链接是指,本层全部的神经元都与下一层的神经元相连。由于相对简单,这里也再也不赘述。code
经过观察下图咱们能够发现,一个CNNs会由多个卷积层,多个池化层,经由全链接层产生输出。
全链接层起到的做用很容易理解,一般也只是分类,回归等任务。那么介于原始图像,和分类网络之间的卷积层和池化层究竟起了什么做用?
我认为,它们两层合起来能够用“特征提取层”来归纳,也就是说,卷积层和池化层实际上是一个特征抽取,数据加工的过程,将原始的图像通过一系列的处理,加工,才能产生易于分类的数据类型,而后进行分类工做。
类比CNNs在图像处理中的应用,NLP中的模型其实是将一个句子以图的概念理解。
在初始化特征矩阵时,句子中的每个单词的word embeddings做为矩阵的一行,获得一个n*k的矩阵,其中n是句子中的单词数,k是词嵌入模型的维度,也能够是one-hot模型,可是考虑到样本的稀疏度,通常会采用低维的词嵌入模型。
在CV中,通常咱们的卷积核filters会扫过图像的局部像素,但在NLP中,一般会只改变filters的长度,而宽度始终等于词嵌入维度k,也就是说,filter的最小单位一整行,一个完整的word。而一般状况下,会使用一个长度为2~5的filter,这是由于实际状况中,不多会出现5个词以上的长组合短语。
CV中的池化操做一般会产生一个n * m的特征矩阵,但在NLP中,产生的特征矩阵会是一个n * 1的矩阵。
产生这种不一样的缘由是由于,在图像中,传递信息的是一个像素块,而在天然语言处理中,传递信息的是一个word,图像自己就是二维的,因此须要用二维去传递信息,可是对于词而言,第二个维度k其实是词嵌入的维度,并不会传递有关于这个句子的信息。
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,将一个词映射成低维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卷积方法中会使用。
卷积和池化模型的创建。
由于是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模型在文本分类这一方向取得了不小的进步。