THUCNews学习(CNN模型)

THUCNews

0、概述

THUCNews是根据新浪新闻RSS订阅频道2005~2011年间的历史数据筛选过滤生成,包含74万篇新闻文档(2.19 GB),均为UTF-8纯文本格式。由于担心数据量太过庞大,故减少训练数目。本文利用CNN对文本数据进行分类。

1、项目目标

根据数据集对文本数据进行分类,学习整个文本分类流程,熟悉TensorFlow框架,利用深度学习做一些实际工程,与实际接轨。

2、数据说明

类别如下:

体育, 财经, 房产, 家居, 教育, 科技, 时尚, 时政, 游戏, 娱乐

数据集划分如下:

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

由于数据存在有序排列在最后运行前需要将数据完全打乱。在放入模型中训练,优点在于提升泛化性并且节省时间(要不就先讲数据打乱再热编码,或者热编码后就不变直接打乱)

:~~==*高亮部分表示提升模型的猜想*==~~

3、数据预处理

3.1 获得词汇表

此处构建词汇表是统计了包括训练集、验证集和测试集在内的所有文本数据当中出现频率最高的5000个词(不知道选择5000以上的词汇表结果是否更优),当然也包括了标点符号(不知道去除标点符号结果会不会提高)。在词汇表的表头第0个位置放着《pad》表示低频词汇(可以理解为补0)

from collections import Counter

# 定义词汇表
def getVocabularyText(content_list, size):
    size = size - 1
    allContent = ''.join(content_list)
    #将内容列表中的所有文章合并起来变成字符串str形式
    counter = Counter(allContent)
    #将Counter对象实例化并传入字符串形式的内容
    vocabulary = []
    vocabulary.append('<PAD>')
    for i in counter.most_common(size):
        vocabulary.append(i[0])
    with open('vocabulary.txt', 'w', encoding='utf8') as file:
        for vocab in vocabulary:
            file.write(vocab + '\n')

            
#读取数据            
with open('E:/cnews/cnews.train.txt', encoding='utf8') as file:
    line_list = [k.strip() for k in file.readlines()]
    #读取每行
    train_label_list = [k.split()[0] for k in line_list]
    #将标签依次取出
    train_content_list = [k.split(maxsplit=1)[1] for k in line_list]
    #将内容依次取出,此处注意split()选择最大分割次数为1,否则句子被打断.
with open('E://cnews/cnews.test.txt', encoding='utf8') as file:
    line_list = [k.strip() for k in file.readlines()]
    test_label_list = [k.split()[0] for k in line_list]
    test_content_list = [k.split(maxsplit=1)[1] for k in line_list]
with open('E://cnews/cnews.val.txt', encoding='utf8') as file:
    line_list = [k.strip() for k in file.readlines()]
    val_label_list = [k.split()[0] for k in line_list]
    val_content_list = [k.split(maxsplit=1)[1] for k in line_list]
# 获得词汇表    
content_list=train_content_list+test_content_list+val_content_list
getVocabularyText(content_list, 5000)

3.2 句子向量化

由于一个基于整篇文本单词的向量维度十分大,在构造句子向量的时候就不会选择单词的向量拼接,而是选择单词对应词汇表的id(行号)拼接,这样可以有效避开了句子向量所造成的空间开销巨大问题。

vocab_size = 5000  # 词汇表达小
seq_length = 600  # 句子序列长度
num_classes = 10  # 类别数

一个句子的向量长度就是词的总数词向量的维度了。这样一乘发现维度就特别大,而且每个句子的长度不一,对于CNN, 输入与输出都是固定的,所以句子有长有短就没法按训练样本来训练了。所以规定,就是每个句子长度为seq_length(这个序列长度越大训练越慢,但是可能准确率会更好一些?)。如果句子长度不够就补0(有池化层所有补0对于结果没有任何影响),如果句子长度太长的话就去掉多余的部分。

利用kera进行规范

import tensorflow.contrib.keras as kr
train_X = kr.preprocessing.sequence.pad_sequences(train_vector_list,600)
test_X = kr.preprocessing.sequence.pad_sequences(test_vector_list,600)
  • kera中的pad_sequences是从右往左数,够600截断,不够的补0.

4、建模过程

本次项目暂时选用的模型为embedding+CNN的神经网络模型。

大致结构如下:

images_cnn_architecture

embedding_dim = 128  # 词向量维度
num_filters = 256  # 卷积核数目
kernel_size = 8  # 卷积核尺寸
hidden_dim = 128  # 全连接层神经元
dropout_keep_prob = 0.8  # dropout保留比例
learning_rate = 1e-3  # 学习率
batch_size = 64  # 每批训练大小

4.1 embedding

word embedding(词嵌入):由于one-hot 编码的向量会过于稀疏,这样的稀疏向量表达一个词效率并不高。为了解决这个问题,利用embeding层通过训练,将每个词都映射到一个较短的词向量上(这样就解决了每个词向量的维度过大问题),所有的词向量构成了词汇表的每个词。

词汇表有5000个,词向量维度设定为128(一般即为128或者300)

流程

  • 输入层:

    为词汇表中某一个词,采用one-hot编码 长度为1X5000

  • 隐藏层:

    从输入层到隐藏层的权重矩阵W_v*n就是5000行128列的矩阵,其中每一行就代表一个词向量。这样词汇表中所有的词都会从5000维的one-hot code转变成为128维的词向量。

  • 输出层:

    经过神经网络隐层的计算,这个输入的词就会变为1X128的向量,再被输入到输出层。(输出层就是一个sotfmax回归分类器。它的每个结点将会输出一个0-1的概率,所有结点的值之和为1,我们就会取最大概率位置检测是否为输入样本x对应的y。

本次处理是直接将embedding的结果输出到CNN层中并没有像word2vec进行单独的词向量训练。(提前训练好词向量对结果是否有所提高?

import tensorflow as tf

X_holder = tf.placeholder(tf.int32,[None,seq_length])
#X的占位符由于句子是由id组成的向量,而id为int类型,
#所以定义x的传入数据为tf.int32类型,
#None表示可以传任意组X,seq_length表示词向量维度。
embedding = tf.get_variable('embedding', [5000, 128])
#embedding字典维度为5000*128,128为词向量维度
embedding_outputs = tf.nn.embedding_lookup(embedding, X_holder)
#embedding_outputs的维度为(batch_size)64*600*128

最终经过embedding输出的样本维度为64 x 600 x 128

4.2 CNN

本次建模使用了一层卷积层,一层池化层,一层全连接层。

4.2.1 卷积层

卷积层实际上就是利用内核(卷积核),通过扫描图片来进行重新计算,计算方法为矩阵相乘后并将各个元素相加,得到新的像素值。

演示:

kernel

这里写图片描述

标准卷积计算:

  • 输入文本的尺寸统一为600 x 128 (高度 x 宽度 )

  • 本层一共具有256个卷积核,

  • 每个卷积核的尺寸都是 8 x 128

  • 已知 stride = 1, padding = 0(stride为每次移动的步长,padding为填充的数值)

  • batch_size = 64(每次训练样本)

  • 输出矩阵的高度和宽度(height, width)这两个维度的尺寸由输入矩阵、卷积核、扫描方式所共同决定。计算公式如下。

    img

batchHeightWidthin Depthout Depth
input64600128
hidden8128256
output645931256

通过卷积层输出的是一个64 x 593 x 1 x 256的样本维度

4.2.2 池化层

pooling层(池化层)的输入一般来源于上一个卷积层,主要有以下几个作用:

  1. 保留主要的特征,同时减少下一层的参数和计算量,防止过拟合;
  2. 保持某种不变性,包括translation(平移),rotation(旋转),scale(尺度),常用的有mean-pooling和max-pooling。

mean-pooling:(2 x 2区域取均值)

max-pooling:(2 x 2取最大值)

本文选用的是max-pooling(由于有些文本进行了补0,用mean-pooling影响结果太多)。
在每个维度取最大值,最终取得的维度为64 x 256

4.2.3 全连接层

全连接层(fully connected layers,FC)在整个卷积神经网络中起到“分类器”的作用。如果说卷积层、池化层和激活函数层等操作是将原始数据映射到隐层特征空间的话,全连接层则起到将学到的“分布式特征表示”映射到样本标记空间的作用。(简单来说就是提纯特征,对卷积的结果做一个加权求和)

对于非线性问题我们一般采用两层以上的全连接层

第一层全连接(rule的激励函数):

运用128个神经元(可以理解为:128个256*1的的卷积核),取得的维度为64 x 128

第二层全连接(softmax的激励函数):

运用和最终分类一样数目的神经元,得出每个样本最终分类的概率。(由于样本均衡,概率最大即为某个类),得出的维度为64 x 10

在上面全连阶层输出的基础上再加上一个dropout来增加模型的泛化能力,防止过拟合,此处选择0.8的保留率。(对dropout的调整对结果是否具有一定的影响

conv1 = tf.layers.conv1d(inputs=embedding_inputs,
filters=num_filters,kernel_size=kernel_size)
max_pool = tf.reduce_max(conv1,reduction_indices=[1])
full_connect = tf.layers.dense(max_pool,hidden_dim)
full_connect_dropout = tf.contrib.layers.dropout(full_connect,keep_prob=0.8)
full_connect_activate = tf.nn.relu(full_connect_dropout)
full_connect_last = tf.layers.dense(full_connect_activate,num_classes)
predict_y = tf.nn.softmax(full_connect_last) ​      

5、结论

5.1 结构显示

_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
embedding_1 (Embedding)      (None, 600, 128)          640000    
_________________________________________________________________
conv1d_1 (Conv1D)            (None, 593, 256)          262400    
_________________________________________________________________
max_pooling1d_1 (MaxPooling1 (None, 1, 256)            0         
_________________________________________________________________
flatten_1 (Flatten)          (None, 256)               0         
_________________________________________________________________
dense_1 (Dense)              (None, 128)               32896     
_________________________________________________________________
dropout_1 (Dropout)          (None, 128)               0         
_________________________________________________________________
activation_1 (Activation)    (None, 128)               0         
_________________________________________________________________
dense_2 (Dense)              (None, 10)                1290      
_________________________________________________________________
activation_2 (Activation)    (None, 10)                0        
=================================================================
Total params: 936,586
Trainable params: 936,586
Non-trainable params: 0
_________________________________________________________________    

5.2 结果显示

目标函数:多分类的对数损失函数,与softmax分类器相对应的损失函数(合理科学)

优化器: 选择Adam优化器(一般情况Adam优化器的结果都比较好并且迭代速度较快)

评价指标:多分类准确率(计算在所有预测值上的平均正确率)

cross_entry = tf.nn.softmax_cross_entropy_with_logits_v2(labels=Y_holder,logits=full_connect_last)
loss = tf.reduce_mean(cross_entry)
optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate)
train = optimizer.minimize(loss)

每次投入64个样本,每一百次训练后放入测试集测试结果,循环5000次。
在这里插入图片描述

LabelPrecisionRecallF1Support
0体育0.98500.9989860.9919441000
1娱乐0.97500.9701490.9725691000
2家居0.86300.9773500.9166221000
3房产0.99200.9989930.9954841000
4教育0.92300.9380080.9304441000
5时尚0.98300.9264840.9539061000
6时政0.93300.9481710.9405241000
7游戏0.97500.9779340.9764651000
8科技0.96000.9866390.9731371000
9财经0.99600.8783070.9334581000
10总体0.95850.9601020.95845510000

结论:和原作者95.35%的分类结果基本吻合。从分类结果来看,家居、教育、时政的分类结果一般,家居会误分类入时尚和财经类,教育会误分类入时政和财经类,时政会误分类教育和财经。(或许可能是这三种文章的内容较短,而这里基于所有文章同样长度来做训练,导致了预测不佳的情况。)

6、自我改进

6.1 改进的内容

  1. 利用文本数据先对词汇进行预训练(success)
  2. 调整激活函数的种类(Failed)
  3. 加入BatchNormalization层(Failed,why?)
  4. 调整权重初始化的方法:
    1. MSRA初始化(Failed)
    2. Xavier初始化(success)
  5. dropout的调整(调试几次还是0.2最好)

6.2 思维导图

在这里插入图片描述

6.3 结果展示

在这里插入图片描述

LabelPrecisionRecallF1Support
0体育0.9970030.9980.9975011000
1娱乐0.9968940.9630.9796541000
2家居0.9748950.9320.9529651000
3房产0.9820000.9820.9820001000
4教育0.9651390.9690.9670661000
5时尚0.9466160.9930.9692531000
6时政0.9875260.9500.9684001000
7游戏0.9697560.9940.9817281000
8科技0.9633660.9730.9681591000
9财经0.9698150.9960.9827331000
999总体0.9753010.9750.97494610000

结论:

现阶段通过多次调整的最优结果是word2vec预训练+Xavier+CNN,准确率达到了97.5%提升了1.7%,其实1.2%是由于词汇表的预训练得来的,剩下的是调整初始化权重得来的。

6.4 后续调整

  • word2vec的预训练比较粗糙,里面很多参数和算法没有详细理解,另外如果能用最新的BERT或者GPT.2模型进行预训练应该会得到更为出色的结果
  • 换textcnn、RNN等模型调试结果(但我浏览了网上大牛的文章基本的说法是,对于这类文本分类提升的效果不是很明显主要还是超参数的设置)
  • 调整mini_batch的大小
  • 调整正则化参数的大小
  • 调整神经元的数量
  • 能否引入attention机制
  • ……
  • 2
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值