前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >CNN中文文本分类-基于TensorFlow实现

CNN中文文本分类-基于TensorFlow实现

作者头像
Gaussic
发布2018-08-17 15:33:35
1.1K0
发布2018-08-17 15:33:35
举报
文章被收录于专栏:GaussicGaussic

代码地址:Github

转载请注明出处:Gaussic - 写干净的代码

基于CNN的文本分类问题已经有了一定的研究成果,CNN做句子分类的论文可以参看: Convolutional Neural Networks for Sentence Classification

在网上也有了一些开源的实现,例如比较著名的dennybritz大牛的博客Implementing a CNN for Text Classification in TensorFlow基于早期TensorFlow的一个实现版本。

如今,TensorFlow大版本已经升级到了1.3,对很多的网络层实现了更高层次的封装和实现,甚至还整合了如Keras这样优秀的一些高层次框架,使得其易用性大大提升。相比早起的底层代码,如今的实现更加简洁和优雅。

本章的目的是基于TensorFlow的API来重新实现一个在中文文本上的分类器。如果你觉得对你有些许帮助或者疑惑,欢迎star和交流。

数据集

本文采用了清华NLP组提供的THUCNews新闻文本分类数据集的一个子集(原始的数据集大约74万篇文档,训练起来需要花较长的时间)。数据集请自行到THUCTC:一个高效的中文文本分类工具包下载,请遵循数据提供方的开源协议。

本次训练使用了其中的10个分类,每个分类6500条,总共65000条新闻数据。

类别如下:

代码语言:javascript
复制
体育, 财经, 房产, 家居, 教育, 科技, 时尚, 时政, 游戏, 娱乐

数据集划分如下:

  • 训练集: 5000*10
  • 验证集: 500*10
  • 测试集: 1000*10

从原数据集生成子集的过程请参看helper下的两个脚本。其中,copy_data.sh用于从每个分类拷贝6500个文件,cnews_group.py用于将多个文件整合到一个文件中。执行该文件后,得到三个数据文件:

  • cnews.train.txt: 训练集(50000条)
  • cnews.val.txt: 验证集(5000条)
  • cnews.test.txt: 测试集(10000条)

预处理

data/cnews_loader.py为数据的预处理文件。

  • read_file():读取上一部分生成的数据文件,将内容和标签分开返回;
  • _build_vocab(): 构建词汇表,这里不需要对文档进行分词,单字的效果已经很好,这一函数会将词汇表存储下来,避免每一次重复处理;
  • _read_vocab(): 读取上一步存储的词汇表,转换为{词:id}表示;
  • _read_category(): 将分类目录固定,转换为{类别: id}表示;
  • _file_to_ids(): 基于上面定义的函数,将数据集从文字转换为id表示;
  • to_words(): 将一条由id表示的数据重新转换为文字;
  • preocess_file(): 一次性处理所有的数据并返回;
  • batch_iter(): 为神经网络的训练准备批次的数据。

经过数据预处理,数据的格式如下:

输入图片说明
输入图片说明

配置项

可配置的参数如下所示,在model.py的上部。

代码语言:javascript
复制
class TCNNConfig(object):
    """配置参数"""

    # 模型参数
    embedding_dim = 64      # 词向量维度
    seq_length = 600        # 序列长度
    num_classes = 10        # 类别数
    num_filters = 256       # 卷积核数目
    kernel_size = 5         # 卷积核尺寸
    vocab_size = 5000       # 词汇表达小

    hidden_dim = 128        # 全链接层神经元

    dropout_keep_prob = 0.8 # dropout保留比例
    learning_rate = 1e-3    # 学习率

    batch_size = 128         # 每批训练大小
    num_epochs = 10          # 总迭代轮次

模型

原始的模型如下图所示:

输入图片说明
输入图片说明

可看到它使用了多个不同宽度的卷积核然后将它们做了一个max over time pooling转换为一个长的特征向量,再使用softmax进行分类。

实验发现,简单的cnn也能达到较好的效果。

因此在这里使用的是简化版的结构,具体参看model.py

首先在初始化时,需要定义两个placeholder作为输入输出占位符。

代码语言:javascript
复制
def __init__(self, config):
      self.config = config

      self.input_x = tf.placeholder(tf.int32,
          [None, self.config.seq_length], name='input_x')
      self.input_y = tf.placeholder(tf.float32,
          [None, self.config.num_classes], name='input_y')

      self.cnn()

词嵌入将词的id映射为词向量表示,embedding层会在训练时更新。

代码语言:javascript
复制
def input_embedding(self):
    """词嵌入"""
    with tf.device('/cpu:0'):
        embedding = tf.get_variable('embedding',
            [self.config.vocab_size, self.config.embedding_dim])
        _inputs = tf.nn.embedding_lookup(embedding, self.input_x)
    return _inputs

cnn模型中,首先定义一个一维卷积层,再使用tf.reduce_max实现global max pooling。再接两个dense层分别做映射和分类。使用交叉熵损失函数,Adam优化器,并且计算准确率。这里有许多参数可调,大部分可以通过调整TCNNConfig类即可。

代码语言:javascript
复制
def cnn(self):
      """cnnc模型"""
      embedding_inputs = self.input_embedding()

      with tf.name_scope("cnn"):
          # cnn 与全局最大池化
          conv = tf.layers.conv1d(embedding_inputs,
              self.config.num_filters,
              self.config.kernel_size, name='conv')

          # global max pooling
          gmp = tf.reduce_max(conv, reduction_indices=[1], name='gmp')

      with tf.name_scope("score"):
          # 全连接层,后面接dropout以及relu激活
          fc = tf.layers.dense(gmp, self.config.hidden_dim, name='fc1')
          fc = tf.contrib.layers.dropout(fc,
              self.config.dropout_keep_prob)
          fc = tf.nn.relu(fc)

          # 分类器
          self.logits = tf.layers.dense(fc, self.config.num_classes,
              name='fc2')
          self.pred_y = tf.nn.softmax(self.logits)

      with tf.name_scope("loss"):
          # 损失函数,交叉熵
          cross_entropy = tf.nn.softmax_cross_entropy_with_logits(
              logits=self.logits, labels=self.input_y)
          self.loss = tf.reduce_mean(cross_entropy)

      with tf.name_scope("optimize"):
          # 优化器
          optimizer = tf.train.AdamOptimizer(
              learning_rate=self.config.learning_rate)
          self.optim = optimizer.minimize(self.loss)

      with tf.name_scope("accuracy"):
          # 准确率
          correct_pred = tf.equal(tf.argmax(self.input_y, 1),
              tf.argmax(self.pred_y, 1))
          self.acc = tf.reduce_mean(tf.cast(correct_pred, tf.float32))

训练与验证

这一部分详见代码,已经做了许多的注释,浅显易懂,具体不在此叙述。

在设定迭代轮次为5的时候,测试集达到了95.72%的准确率,可见效果还是很理想的。

代码语言:javascript
复制
Loading data...
Time usage: 0:00:16
Constructing Model...
Training and evaluating...
Iter:      1, Train Loss:    2.3, Train Acc:  10.94%, Val Loss:    2.3, Val Acc:  10.06%, Time: 0:00:01
Iter:    201, Train Loss:   0.37, Train Acc:  87.50%, Val Loss:   0.58, Val Acc:  81.70%, Time: 0:00:06
Iter:    401, Train Loss:   0.22, Train Acc:  90.62%, Val Loss:   0.34, Val Acc:  91.16%, Time: 0:00:11
Iter:    601, Train Loss:   0.17, Train Acc:  95.31%, Val Loss:   0.28, Val Acc:  92.16%, Time: 0:00:16
Iter:    801, Train Loss:   0.18, Train Acc:  95.31%, Val Loss:   0.25, Val Acc:  93.12%, Time: 0:00:21
Iter:   1001, Train Loss:   0.12, Train Acc:  95.31%, Val Loss:   0.28, Val Acc:  91.52%, Time: 0:00:26
Iter:   1201, Train Loss:  0.085, Train Acc:  96.88%, Val Loss:   0.24, Val Acc:  92.92%, Time: 0:00:31
Iter:   1401, Train Loss:  0.098, Train Acc:  95.31%, Val Loss:   0.22, Val Acc:  93.40%, Time: 0:00:36
Iter:   1601, Train Loss:  0.042, Train Acc:  98.44%, Val Loss:   0.19, Val Acc:  94.70%, Time: 0:00:41
Iter:   1801, Train Loss:  0.035, Train Acc: 100.00%, Val Loss:   0.19, Val Acc:  94.88%, Time: 0:00:46
Iter:   2001, Train Loss:  0.011, Train Acc: 100.00%, Val Loss:    0.2, Val Acc:  94.38%, Time: 0:00:51
Iter:   2201, Train Loss:    0.1, Train Acc:  96.88%, Val Loss:    0.2, Val Acc:  94.60%, Time: 0:00:56
Iter:   2401, Train Loss:  0.015, Train Acc: 100.00%, Val Loss:   0.19, Val Acc:  94.88%, Time: 0:01:01
Iter:   2601, Train Loss:  0.017, Train Acc: 100.00%, Val Loss:   0.21, Val Acc:  94.24%, Time: 0:01:06
Iter:   2801, Train Loss: 0.0014, Train Acc: 100.00%, Val Loss:   0.17, Val Acc:  95.36%, Time: 0:01:11
Iter:   3001, Train Loss:  0.074, Train Acc:  98.44%, Val Loss:   0.23, Val Acc:  94.02%, Time: 0:01:17
Iter:   3201, Train Loss:  0.033, Train Acc:  98.44%, Val Loss:   0.15, Val Acc:  96.26%, Time: 0:01:22
Iter:   3401, Train Loss: 0.0087, Train Acc: 100.00%, Val Loss:    0.2, Val Acc:  94.38%, Time: 0:01:27
Iter:   3601, Train Loss: 0.0088, Train Acc: 100.00%, Val Loss:   0.23, Val Acc:  94.16%, Time: 0:01:32
Iter:   3801, Train Loss:  0.015, Train Acc: 100.00%, Val Loss:   0.26, Val Acc:  93.08%, Time: 0:01:37
Test Loss:   0.17, Test Acc:  95.72%

我的博客即将搬运同步至腾讯云+社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=1nbzb7lxkx56n

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2017/08/30 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 数据集
  • 预处理
  • 配置项
  • 模型
  • 训练与验证
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档