将神经网络应用于大图像时,输入可能有上百万个维度,如果输入层和隐含层进行“全连接”,需要训练的参数将会非常多。如果构建一个“部分联通”网络,每个隐含单元仅仅只能连接输入单元的一部分,参数数量会显著下降。卷积神经网络就是基于这个原理而构建的。这其中的思想就是,降维或者说是特征选择,通过前面的卷积层或者池化层将重要的特征选取出来,然后全连接进行分类。特征是最重要的。

论文所提出的模型结构如下图所示:

1,这里的输入层显示有两个channel,其实我们可以看作是一个,因为后文中说到这两个channel分别是static和non-static,即使用的词向量是否随着训练发生变化。non-static就是词向量随着模型训练变化(Fine tune),这样的好处是词向量可以根据数据集做适当调整,但是CS224d课程里也说过当数据集较小时不推荐此操作,否则容易产生过拟合现象。static就是直接使用word2vec训练好的词向量即可。此外,由图可知,输入层是将一个句子所有单词(padding)的词向量进行拼接成一个矩阵,每一行代表一个词。每个句子固定20个词,如果不够的补padding。

2,卷积层,不做过多解释。每个卷积核的大小为filter_size*embedding_size。filter_size代表卷积核纵向上包含单词个数,即认为相邻几个词之间有词序关系,代码里使用的是[3,4,5]。embedding_size就是词向量的维数。每个卷积核计算完成之后我们就得到了1个列向量,代表着该卷积核从句子中提取出来的特征。有多少和卷积核就能提取出多少种特征,即图中在纵深方向上channel的数量。

3,池化层。文中提到pooling操作就是将卷积得到的列向量的最大值提取出来。这样pooling操作之后我们会获得一个num_filters维的行向量,即将每个卷积核的最大值连接起来。这样做还有一个好处就是,如果我们之前没有对句子进行padding操作,那么句子的长度是不同的,卷积之后得到的列向量维度也是不同的,可以通过pooling来消除句子之间长度不同的差异。

4,全连接层,为了将pooling层输出的向量转化为我们想要的预测结果,加上一个softmax层即可。针对电影评价的分类任务,就是将其转化为正面、负面两个结果。文中还提到了过拟合的问题,因为实验中所使用的数据集相对较小,很容易就会发生过拟合现象,在实验过程中也会发现当迭代3000多轮的时候准确率就会接近1。所以这里引如dropout来减少过拟合现象。此外还可以考虑L2正则化等方法实现防止过拟合的功能。

到这里其实对论文模型的

数据获取和准备

在本博客中,我们使用的数据集是 Movie Review data from Rotten Tomatoes ,这也是论文中使用的其中一个数据集。这个数据集包含 10662 个评论样本,其中一半是正向评论,一半是负向评论。这个数据集大约有2万个词。注意,因为这个数据集很小,所以如果我们使用很复杂的模型,那么容易造成过拟合。并且,这个数据没有帮我们分离训练数据集和测试数据集。因此,我们需要自己去预处理。在这里,我们把10%的数据作为交叉验证集。在原始的论文中,作者使用十折交叉验证(10-fold cross validation)。

数据预处理从原始数据文件中,导入正样本和负样本数据。数据清理,使用和论文中相同的代码。

将每个句子填充到最大句子长度,也就是数据集中最长的那个句子的长度,这里是20。我们填充的特殊标记是 ,将句子填充到相同长度是非常有用的,因为它能帮助我们进行有效的批处理,因为在批处理中的每个例子都必须有相同的长度。

构建词汇索引表,将每个单词映射到 0 ~ 18765 之间(18765是词汇量大小),那么每个句子就变成了一个整数的向量。

准备单词的embeding向量,这里采用训练好的256的Word2vector向量。

初始化textcnn模型

为了允许各种的超参数配置,我们把我们的代码放到一个TextCNN类中,并且在 init 函数中生成模型图。

import tensorflow as tf

import numpy as np

class TextCNN(object):

"""A CNN for text classification.Uses an embedding layer, followed by a convolutional, max-pooling and softmax layer."""

def __init__(

self, sequence_length, num_classes, vocab_size,

embedding_size, filter_sizes, num_filters, l2_reg_lambda=0.0):

# Implementation ...

为了实例化类,我们需要传递以下参数到类中:sequence_length - 句子的长度。请注意,我们通过添加特殊标记,使得所欲的句子都拥有了相同的长度(我们的数据集是20)。

num_classes - 最后一层分类的数目,在这里我们是进行二分类(正向评论和负向评论)。

vocab_size - 词汇量的大小。这个参数是为了确定我们词向量嵌入层的大小,最终的总词向量维度是 [vocabulary_size, embedding_size] 。

embeddign_size - 每个单词的词向量的长度128或者256。

filter_sizes - 这个参数确定我们希望我们的卷积核每次覆盖几个单词。对于每个卷积核,我们都将有 num_filters 个。比如,filter_sizes = [3, 4, 5] , 这就意味着,卷积核一共有三种类型,分别是每次覆盖3个单词的卷积核,每次覆盖4个单词的卷积核和每次覆盖5个单词的卷积核。卷积核一共的数量是 3 * num_filters 个。

num_filters - 每个卷积核的数量(参考 filter_sizes 参数的介绍)。

输入占位符

我们首先定义需要输入到模型中的数据。

# Placeholders for input, output and dropout

self.input_x = tf.placeholder(tf.int32, [None, sequence_length], name="input_x")

self.input_y = tf.placeholder(tf.float32, [None, num_classes], name="input_y")

self.dropout_keep_prob = tf.placeholder(tf.float32, name="dropout_keep_prob")

tf.placeholder 创建了一个占位符变量,当我们在训练阶段或者测试阶段时,都可以使用它向我们的模型输入数据。第二个参数是输入张量的形状。None 的意思是,该维度的长度可以是任何值。在我们的模型中,第一个维度是批处理大小,而使用 None 来表示这个值,说明网络允许处理任意大小的批次。

在 dropout 层中,我们使用 dropout_keep_prob 参数来控制神经元的激活程度。但这个参数,我们只在训练的时候开启,在测试的时候禁止它。(后续文章会深入介绍)

嵌入层

我们定义的第一个网络层是嵌入层,这一层的作用是将词汇索引映射到低维度的词向量进行表示。它本质是一个我们从数据中学习得到的词汇向量表。

with tf.device('/cpu:0'), tf.name_scope("embedding"):

W = tf.Variable(

tf.random_uniform([vocab_size, embedding_size], -1.0, 1.0),

name="W")

self.embedded_chars = tf.nn.embedding_lookup(W, self.input_x)

self.embedded_chars_expanded = tf.expand_dims(self.embedded_chars, -1)

在这里,我们又使用了一些新功能,让我们来学习一下它们:tf.device("/cpu:0") 强制代码在CPU上面执行操作。因为默认情况下,TensorFlow会尝试将操作放在GPU上面进行运行(如果存在GPU),但是嵌入层的操作目前还不支持GPU运行,所以如果你不指定CPU进行运行,那么程序会报错。

tf.name_scope 创建了一个称之为"embedding"的新的名称范围,该范围将所有的操作都添加到这个"embedding"节点下面。以便在TensorBoard中获得良好的层次结构,有利于可视化。

W 是我们的嵌入矩阵,这个矩阵是我们从数据训练过程中得到的。最开始,我们使用一个随机均匀分布来进行初始化。tf.nn.embedding_lookup 创建实际的嵌入读取操作,这个嵌入操作返回的数据维度是三维张量 [None, sequence_length, embedding_size] 。

TensorFlow 的卷积操作 conv2d 需要一个四维的输入数据,对应的维度分别是批处理大小,宽度,高度和通道数。在我们嵌入层得到的数据中不包含通道数,所以我们需要手动添加它,所以最终的数据维度是 [None, sequence_length, embedding_size, 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 over the outputs 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(3, pooled_outputs)

self.h_pool_flat = tf.reshape(self.h_pool, [-1, num_filters_total])

代码中,W 表示不同的卷积核,h 表示对经过卷积得到的输出结果进行非线性处理之后的结果。每个卷积核会覆盖整个词向量长度,但是滑动覆盖几个单词就是不同的了。VALID 填充意味着,我们的卷积核只在我们的单词上面滑动,而不填充边缘,是执行窄卷积,所有最后输出的维度是 [1, sequence_length - filter_size + 1, 1, 1] 。对经过特定卷积的输出,我们做最大池化操作,使得我们得到的张量维度是 [batch_size, 1, 1, num_filters]。这实质上就是一个特征向量,其中最后一个维度就是对应于我们的特征。一旦我们拥有了来自各个卷积核的输出向量,那么我们就可以把它们合并成一个长的特征向量,该向量的维度是 [batch_size, num_filters_total] 。在 tf.reshape 中使用 -1,就是告诉 TensorFlow 在可能的情况下,将维度进行展平。

上面部分最好花点时间看明白,去弄明白每个操作输出的维度是什么。如果你不是很了解,也可以再去参考这篇博客 Understanding Convolutional Neural Networks for NLP,获得一些灵感。下图是TensorBoard可视化的结果,你可以发现三个卷积核组成了三个不同的网络层。

Dropout层

一定要用 dropout:有两种情况可以不用:数据量特别小,或者你用了更好的正则方法,比如bn。实际中我们尝试了不同参数的dropout,最好的还是0.5,所以如果你的计算资源很有限,默认0.5是一个很好的选择。

Dropout 也许是最流行的方法来正则化卷积神经网络。Dropout 的思想非常简单,就是按照一定的概率来“禁用”一些神经元的发放。这种方法可以防止神经元共同适应一个特征,而迫使它们单独学习有用的特征。神经元激活的概率,我们从参数 dropout_keep_prob 中得到。我们在训练阶段将其设置为 0.5,在测试阶段将其设置为 1.0(即所有神经元都被激活)。

# Add dropout with tf.name_scope("dropout"):

self.h_drop = tf.nn.dropout(self.h_pool_flat, self.dropout_keep_prob)

分数和预测

我们使用来自池化层的特征向量(经过Dropout),然后通过全连接层,得到一个分数最高的类别。我们还可以应用softmax函数来将原始分数转换成归一化概率,但这个操作是保护会改变我们的最终预测。

with tf.name_scope("output"):

W = tf.Variable(tf.truncated_normal([num_filters_total, num_classes], stddev=0.1), name="W")

b = tf.Variable(tf.constant(0.1, shape=[num_classes]), name="b")

self.scores = tf.nn.xw_plus_b(self.h_drop, W, b, name="scores")

self.predictions = tf.argmax(self.scores, 1, name="predictions")

上面代码中,tf.nn.xw_plus_b是一个很方便的函数,实现 Wx + b 操作。

损失函数和正确率

使用我们上面求得的分数,我们可以定义损失函数。损失值是对模型所造成的误差的度量,我们的目标是最小化这个损失值。分类问题的标准损失函数是交叉熵损失函数。

# Calculate mean cross-entropy loss with tf.name_scope("loss"):

losses = tf.nn.softmax_cross_entropy_with_logits(self.scores, self.input_y)

self.loss = tf.reduce_mean(losses)

这里,tf.nn.softmax_cross_entropy_with_logits 是一个方便的函数,用来计算每个类别的交叉损失熵,对于我们给定的分数和输入的正确标签。然后,我们计算损失值的平均值。当然,我们也可以对它们进行求和,但是这会对不同批大小的损失值衡量非常困难,尤其是在训练阶段和测试阶段。

我们还定义了一个正确率的函数,它的作用就是在训练阶段和测试阶段来跟踪模型的性能。

# Calculate Accuracy

with tf.name_scope("accuracy"):

correct_predictions = tf.equal(self.predictions, tf.argmax(self.input_y, 1))

self.accuracy = tf.reduce_mean(tf.cast(correct_predictions, "float"), name="accuracy")

基于深度学习技术的文本分类技术比起传统的文本分类模型,例如 LR,SVM 等,有什么优势呢?

首先,最明显的优势,深度学习不需要人工手动的提取文本的特征,它可以自动的获取基础特征并组合为高级的特征,训练模型获得文本特征与目标分类之间的关系,省去了使用TF-IDF等提取句子的关键词构建特征工程的过程。

其次,相比传统的N-gram模型而言,深度学习中可以更好的利用词序的特征,CNN的文本分类模型中的filter的size的大小可以当做是一种类似于N-gram的方式,而RNN(LSTM)则可以利用更长的词序,配合Attention机制则可以通过加权体矩阵体现句子中的核心词汇部位,attention最早是用于自动翻译中实现对应词汇对齐及可视化的功能。

作者:李良

cnn文本分类python实现_CNN文本分类相关推荐

  1. libsvm多分类python,SVM实现多分类的三种方案

    转载自:http://www.cnblogs.com/CheeseZH/p/5265959.html SVM本身是一个二值分类器 SVM算法最初是为二值分类问题设计的,当处理多类问题时,就需要构造合适 ...

  2. 用深度学习(CNN RNN Attention)解决大规模文本分类问题 - 综述和实践

    https://zhuanlan.zhihu.com/p/25928551 近来在同时做一个应用深度学习解决淘宝商品的类目预测问题的项目,恰好硕士毕业时论文题目便是文本分类问题,趁此机会总结下文本分类 ...

  3. python文本分类_手把手教你在Python中实现文本分类.pdf

    手把手教你在Python 中实现文本分类(附代码.数 据集) 引言 文本分类是商业问题中常见的自然语言处理任务,目标是自动将文本文件分到一个 或多个已定义好的类别中.文本分类的一些例子如下: • 分析 ...

  4. python 文本分类卡方检验_中文文本分类:你需要了解的10项关键内容

    文本分类指的是计算机通过算法对输入的文本按照一定的类目体系进行自动化归类的过程.在人工智能浪潮席卷全球的今天,文本分类技术已经被广泛地应用在文本审核.广告过滤.情感分析和反黄识别等NLP领域.本文从达 ...

  5. 基于多种分类方式的新闻文本种类预测[2021论文附代码]

    很典型的多个特征维度的新闻种类分类,包含了例如文本信息如何转换成数字特征,如何设置停用词,如何去掉符号转大小,如何解决内存不足等等许多问题的解决.包括使用各种分类方法测试最终选择最优的模型.      ...

  6. 深度学习文本分类在支付宝投诉文本模型上的应用

    摘要: 小蚂蚁说: 随着深度学习的快速发展,以及在图像.语音领域取得的不错成果,基于深度学习的自然语言处理技术也日益受到人们的关注.计算机是怎么理解人类的语言的呢? 传统机器学习的应用,常常是利用上述 ...

  7. 中文新闻分类 数据集_NLP-新闻文本分类实战

    一.赛题理解 赛题名称:零基础入门NLP之新闻文本分类 赛题目标:通过这道赛题可以引导大家走入自然语言处理的世界,带大家接触NLP的预处理.模型构建和模型训练等知识点. 赛题任务:赛题以自然语言处理为 ...

  8. 机器学习-文本处理之电影评论多分类情感分析

    一.背景 文本处理是许多ML应用程序中最常见的任务之一.以下是此类应用的一些示例 语言翻译:将句子从一种语言翻译成另一种语言 情绪分析:从文本语料库中确定对任何主题或产品等的情绪是积极的.消极的还是中 ...

  9. 干货解析|深度学习文本分类在支付宝投诉文本模型上的应用

    小蚂蚁说: 随着深度学习的快速发展,以及在图像.语音领域取得的不错成果,基于深度学习的自然语言处理技术也日益受到人们的关注.计算机是怎么理解人类的语言的呢? 传统机器学习的应用,常常是利用上述人工总结 ...

最新文章

  1. 仿桌面通知pnotify插件
  2. centos 7 jenkins githup测试
  3. l298n电机驱动模块_带DRV8825驱动器模块和Arduino的控制步进电机
  4. 用Unity开发AR创意礼物:会动的照片
  5. Google浏览器 — 取出图片颜色值
  6. 开发者生态与双引擎:华为的雄心壮志!
  7. 沸腾新十年 | 中国语音产业江湖和科大讯飞的前半生
  8. 关于HDS的高端存储设备USPV
  9. 金三角图形c语言,升哥学堂 | 实战均线形态——“金三角”
  10. 三层交换机转发原理与实验(三层交换技术原理,MLS条目,虚接口详解与配置)
  11. 电脑开机时按F几重装系统
  12. xshell个人免费版
  13. RV1126笔记二十三:Nginx及cgi移植
  14. WebSocket的JavaScript例子
  15. VMware Workstation Pro 修改显示语言
  16. 苹果xr十大隐藏功能_别再说苹果“悬浮球”功能不好用,隐藏的实用小技巧,每天用得上...
  17. 使用go实现Aes加解密
  18. Qt编写的项目作品2-控件属性设计器(组态)
  19. 腾讯百度阿里,三巨头谁最开放?
  20. python超市收银程序_4超市收银程序(格式化字符串,input)

热门文章

  1. 01数据库、DBMS和SQL
  2. 强烈推荐:大神总结的超系统的前端提升路径
  3. admin.php为什么是乱码,phpadmin和MySQL中文乱码问题的剖析
  4. python 密码学计算_python 密码学示例——理解哈希(Hash)算法
  5. ubuntu16.04源码安装python3.7
  6. 北大出版社继续送书 | 附上周4位中奖的朋友信息
  7. scatter函数_散点图、箱线图、核密度函数……数据分析必备的9种可视化图表
  8. 2.3基本算法之递归变递推 1188 菲波那契数列(2)
  9. php node.js django,Vue.js和Django搭建前后端分离项目示例详解
  10. python运势预测程序_Python 爬虫系列之一——每日星座运势