卷积神经网络文本分类

前言

卷积神经网络(CNN)在图像处理领域取得了很大的成绩,它的卷积和池化结构能很好提取图像的信息,而在 NLP 领域循环神经网络(RNN)则使用的更多,RNN 及其各类变种因拥有记忆功能使得它们更擅长处理上下文。但 NLP 领域不少方面使用 CNN 取得了出色的效果,好比语义分析、查询检索、文本分类等任务。这篇文章看看如何用 CNN 进行文本分类。git

模型结构

模型结构能够经过下图一层层来看,总共由4部分组成,包括了输入层、卷积层、池化层和全链接层。 github

这里写图片描述

输入层

图中最左边的部分即为输入层,总的来讲输入层就是句子对应的矩阵。通常不会使用 ont-hot 向量来表示单词,而是使用 k 维的分布式词向量。那么对于一个长度为 n 的句子,则构成一个 n × k 的矩阵。bash

因此,能够设 x_i 为句子的第 i 个单词,它为 k 维向量。那么一个句子为 x_{1:n}=x_1 \bigoplus x_2 \bigoplus ...\bigoplus x_n,其中\bigoplus表示串联的意思。网络

另外,根据对词向量的做用能够分为两种模式:静态和非静态。静态模式的意思就是我直接使用第三方发布的词向量或者本身训练的词向量来初始化矩阵,而且在每次训练的过程当中不对反向偏差传播产生做用,不改变词向量,整个训练过程词向量都是固定不变的。而非静态模式则不一样,一样是使用词向量来初始化矩阵后,在此后的每次训练过程当中,根据反向偏差传播会对词向量进行微调,整个训练过程词向量都会更新。并发

卷积层

图中第二部分为卷积层,卷积层的做用就是用于提取句子的特征。主要是经过一个 h × k 的卷积核 w 在输入层从上到下进行滑动进行卷积操做,经过该卷积操做获得一个 feature map。feature map 的列为1,行为 (n-h+1),即 c=(c_1,c_2,...c_{n-h+1}),其中 c_i=f(w \cdot x_{i:i+h-1}+b)app

上图中输入层上红色框框就是卷积操做的卷积核,能够看到它是 2 × k 维的,运算后变为 feature map 的一个元素。除此以外,还能够将 h 定为3,此时卷积核变为 3 × k 维,如图中黄色框框部分。相同维度的能够有若干个参数不一样的卷积核,因此最终在每种维度下均可以获得若干个 feature map。dom

卷积操做的意义是什么?能够看到它实际上是根据 h 大小不一样提取不一样长度相邻单词的特征,这个其实能够跟 n-gram 语言模型对应起来。机器学习

池化层

图中第三部分为池化层,池化层的做用是对特征作进一步提取,将最重要的特征提取出来。这里使用的是 max-over-time pooling 操做,即取出 feature map 中的最大值做为最重要的特征,即\widehat{c} = max\{c\}。因此最终对于每一个 feature map 池化后都获得一个一维向量,取最大值做为特征也解决了不一样句子长短的问题,尽管短的句子会用 0 进行填充,但经过取最大值消除了该问题。分布式

前面的经过卷积层的多个不一样卷积核操做获得若干 feature map,而再通过池化层处理后获得若干个一维向量。ide

全链接层

图中最后部分为全链接层,全链接层经过使用 softmax 分类器获得各个分类的几率。前面的池化层的输出以全链接的形式连到 softmax 层,softmax 层定义好分类。

防止过拟合

为了防止过拟合,训练过程当中在倒数第二层使用 dropout 技术,它将随机丢弃隐含层的某些节点使其不 work。具体作法能够在网络向前传输时将一些节点设置为0,好比倒数第二层上,z=[\widehat{c}_1,\widehat{c}_2...,\widehat{c}_m],这里假设咱们有 m 个卷积核。经过 dropout 后为,y=w\cdot(z\circ r)+b,其中\circ和 r 实现了掩码的功能,即 r 是大小与 z 相同的一个向量,它的值为随机的0或1,0对应的节点为丢弃的节点。

同时,还能够在全链接层使用L2正则化来约束权值向量w。

主要实现代码

构建图

首先,构建须要的占位符和常量,其中占位符包括输入占位符、标签占位符和 dropout 占位符,L2 正则损失常量。

train_inputs = tf.placeholder(tf.int32, [None, sequence_length])
train_labels = tf.placeholder(tf.float32, [None, classes_num])
keep_prob = tf.placeholder(tf.float32)
l2_loss = tf.constant(0.0)
复制代码

接着咱们会须要一个嵌入层将词汇嵌入到指定的维度空间上,维度由 embedding_size 指定。同时 vocabulary_size 为词汇大小,这样就能够将全部单词都映射到指定的维数空间上。嵌入层经过tf.nn.embedding_lookup就能找到输入对应的词空间向量了,这里稍微解释下embedding_lookup操做,它会从词汇中取到 inputs 每一个元素对应的词向量,inputs 为2维的话,经过该操做后变为3维,由于已经将词用 embedding_size 维向量表示了。此外,因为要调用卷积操做,这里将结果扩展了一维。

with tf.device('/cpu:0'):
    embeddings = tf.Variable(tf.random_uniform([vocabulary_size, embedding_size], -1.0, 1.0))
    embed = tf.nn.embedding_lookup(embeddings, train_inputs)
    conv_inputs = tf.expand_dims(embed, -1)
复制代码

接着开始作卷积操做及池化操做。因为咱们会定义若干个卷积核,并且每一个高度的都有若干个卷积核,因此咱们会获得不少不一样的 feature map,而后对这些 feature map 作 max-over-time pooling 操做,最终获得池化后的 feature。

features_pooled = []
for filter_height, filter_num in zip(filters_height, filter_num_per_height):
    conv_filter = tf.Variable(tf.truncated_normal([filter_height, embedding_size, 1, filter_num], stddev=0.1))
    conv = tf.nn.conv2d(conv_inputs, conv_filter, strides=[1, 1, 1, 1], padding="VALID")
    bias = tf.Variable(tf.constant(0.1, shape=[filter_num]))
    feature_map = tf.nn.relu(tf.nn.bias_add(conv, bias))
    feature_pooled = tf.nn.max_pool(feature_map, ksize=[1, sequence_length - filter_height + 1, 1, 1],
                                        strides=[1, 1, 1, 1],
                                        padding='VALID')
    features_pooled.append(feature_pooled)
复制代码

如今网络就剩下全链接层了,其中要先进行 dropout 操做来暂时使得一些节点失效,接着作线性计算获得分数,从而获得预测。

filter_num_total = sum(filter_num_per_height)
features_pooled_flat = tf.reshape(tf.concat(features_pooled, 3), [-1, filter_num_total])
features_pooled_flat_drop = tf.nn.dropout(features_pooled_flat, keep_prob)
W = tf.get_variable("W", shape=[filter_num_total, classes_num], initializer=tf.contrib.layers.xavier_initializer())
b = tf.Variable(tf.constant(0.1, shape=[classes_num]))
scores = tf.nn.xw_plus_b(features_pooled_flat_drop, W, b)
复制代码

最后计算损失,一个是L2正则损失一个是交叉熵损失,经过二者求得总的损失。而且计算获得准确率。

l2_loss += tf.nn.l2_loss(W)
l2_loss += tf.nn.l2_loss(b)
losses = tf.nn.softmax_cross_entropy_with_logits(logits=scores, labels=train_labels)
loss = tf.reduce_mean(losses) + l2_lambda * l2_loss
predictions = tf.argmax(scores, 1)
correct_predictions = tf.equal(predictions, tf.argmax(train_labels, 1))
accuracy = tf.reduce_mean(tf.cast(correct_predictions, "float"))
复制代码

github

github附上完整代码。

https://github.com/sea-boat/nlp_lab/tree/master/cnn_text_classify

reference

Convolutional Neural Networks for Sentence Classification

-------------推荐阅读------------

个人2017文章汇总——机器学习篇

个人2017文章汇总——Java及中间件

个人2017文章汇总——深度学习篇

个人2017文章汇总——JDK源码篇

个人2017文章汇总——天然语言处理篇

个人2017文章汇总——Java并发篇

------------------广告时间----------------

知识星球:远洋号

公众号的菜单已分为“分布式”、“机器学习”、“深度学习”、“NLP”、“Java深度”、“Java并发核心”、“JDK源码”、“Tomcat内核”等,可能有一款适合你的胃口。

为何写《Tomcat内核设计剖析》

欢迎关注:

这里写图片描述
相关文章
相关标签/搜索