本文主要内容
- 简略介绍卷积神经网络(CNN, Convolutional Neural Network)处理文本信息的过程
- 使用CNN进行文本分类任务,并对代码进行注释
- 本文代码【https://github.com/540117253/Chinese-Text-Classification 】
一、CNN概述
CNN文本编码器的结构如图1所示。在第一层,词映射函数 f : M → R d f:M \rightarrow R^{d} f:M→Rd将评论的每个单词映射为 d d d维向量,然后将给定的评论文本转化为长度固定为 T T T的词嵌入矩阵(只截取评论文本中的前 T T T个单词,如果长度不足的文本则进行填充处理)。
词映射层后的是卷积层,其包含
m
m
m个神经元,每个神经元对应的卷积核
K
∈
R
t
×
d
K \in R^{t \times d}
K∈Rt×d用于对词向量进行卷积运算提取特征。假设
V
1
:
T
V_{1:T}
V1:T是文本长度为
T
T
T的词嵌入矩阵,第
j
j
j个神经元产生的特征为:
z
j
=
R
e
L
U
(
V
1
:
T
∗
K
j
+
b
j
)
z_j=ReLU(V_{1:T}*K_j + b_j)
zj=ReLU(V1:T∗Kj+bj)
其中
b
j
b_j
bj为偏倚项,
∗
*
∗表示卷积运算,
R
e
L
U
ReLU
ReLU是非线性激活函数。
最终在滑动窗口
t
t
t的作用下,第
j
j
j个神经元产生的特征为
z
1
,
z
2
,
.
.
.
,
z
j
T
−
t
+
1
z_1,z_2,...,z_j^{T-t+1}
z1,z2,...,zjT−t+1。将该特征进行max-pooling运算,其主要用于捕获拥有最大值的最重要的特征,其定义为:
o
j
=
m
a
x
(
z
1
,
z
2
,
.
.
.
,
z
j
T
−
t
+
1
)
o_j = max(z_1,z_2,...,z_j^{T-t+1})
oj=max(z1,z2,...,zjT−t+1)
最后卷积层的最终输出为
m
m
m个神经元输出的拼接结果,其定义为:
O
=
[
o
1
,
o
2
,
.
.
.
.
o
m
]
O=[o_1,o_2,....o_m]
O=[o1,o2,....om]
通常
O
O
O会接着送入全连接层,其中包含权重矩阵
W
∈
R
m
×
n
W \in R ^{m \times n}
W∈Rm×n和偏置项
g
∈
R
n
g \in R^n
g∈Rn,具体公式为:
X
=
R
e
L
U
(
W
O
+
g
)
X=ReLU(WO+g)
X=ReLU(WO+g)
整体上,CNN的卷积核大小一般为3或者5(即一次卷积运算仅计算3个单词或5个单词的信息),其仅采用一个卷积核就能通过滑动窗口的方式来扫描整个文本,因此整个文本可以看作是共享同一个卷积核的一组参数,能很好地节省内存空间。然而一次卷积运算仅能包含卷积窗口内的单词,当输入的文本越长,卷积窗口滑动到文本的尾部时所丢失掉的前文信息就越多。因此对于文本数据,一般采用循环神经网络RNN,其比CNN的文本信息提取能力更优秀。
二、CNN文本分类实例
2.1 数据集介绍
1. 下载地址:
【https://github.com/skdjfla/toutiao-text-classfication-dataset 】
2. 格式:
6552431613437805063_!_102_!_news_entertainment_!_谢娜为李浩菲澄清网络谣言,之后她的两个行为给自己加分_!_佟丽娅,网络谣言,快乐大本营,李浩菲,谢娜,观众们
每行为一条数据,以_!_分割的个字段,从前往后分别是 新闻ID,分类code(见下文),分类名称(见下文),新闻字符串(仅含标题),新闻关键词
分类code与名称:
100 民生 故事 news_story
101 文化 文化 news_culture
102 娱乐 娱乐 news_entertainment
103 体育 体育 news_sports
104 财经 财经 news_finance
106 房产 房产 news_house
107 汽车 汽车 news_car
108 教育 教育 news_edu
109 科技 科技 news_tech
110 军事 军事 news_military
112 旅游 旅游 news_travel
113 国际 国际 news_world
114 证券 股票 stock
115 农业 三农 news_agriculture
116 电竞 游戏 news_game
2.2 预训练词向量
预训练词向量使用的是,基于ACL-2018模型在百度百科训练的词向量。
下载地址:【 https://github.com/Embedding/Chinese-Word-Vectors 】
2.3 数据预处理
- 清除无用字符,并且进行分词处理
- 建立整个数据集的字典,key=word, value=词语的编号
- 对进行截断或补0处理,确保每条样本的长度为maxlen
- 序列化样本的标签,例如“体育类新闻”的类别编号为1,“娱乐类新闻”的类别编号为2
- 将处理好的数据转化为DataFrame格式,并保存到硬盘
2.4 CNN模型的定义
'''
Text => CNN => Fully_Connected => Softmax
参数:
filter_sizes:卷积核的大小
num_filters:卷积核的个数
embedded_size:词向量的维度
dict_size:数据集的单词个数
maxlen:每条样本的最大单词数
label_num:样本类别的数量
learning_rate:梯度优化器的初始学习率
'''
class CNN:
def __init__(self, filter_sizes, num_filters, embedded_size,
dict_size, maxlen, label_num, learning_rate):
# print('model_Name:', 'CNN')
self.droput_rate = 0.5
'''
Convulutional Neural Network
'''
def cnn (input_emb, filter_sizes, num_filters):
pooled_outputs = []
for i, filter_size in enumerate(filter_sizes):
filter_shape = [filter_size, embedded_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(
input_emb,
W,
strides=[1, 1, 1, 1],
padding="VALID",
name="conv") # shape(conv) = [None, sequence_length - filter_size + 1, 1, num_filters]
h = tf.nn.relu(tf.nn.bias_add(conv, b), name="relu")
word_num = input_emb.shape.as_list()[1]
pooled = tf.nn.max_pool(
h,
ksize=[1, word_num - filter_size + 1, 1, 1],
strides=[1, 1, 1, 1],
padding='VALID',
name="pool") # shape(pooled) = [None, 1, 1, num_filters]
pooled_outputs.append(pooled)
num_filters_total = num_filters * len(filter_sizes)
h_pool = tf.concat(pooled_outputs,3)
h_pool_flat = tf.reshape(h_pool, [-1, num_filters_total]) # shape = [None,num_filters_total]
cnn_fea = tf.nn.dropout(h_pool_flat, keep_prob = self.droput_rate)
return cnn_fea
self.X = tf.placeholder(tf.int32, [None, maxlen], name='input_x')
self.Y = tf.placeholder(tf.int64, [None])
self.encoder_embeddings = tf.Variable(tf.random_uniform([dict_size, embedded_size], -1, 1), trainable=False)
encoder_embedded = tf.nn.embedding_lookup(self.encoder_embeddings, self.X)
# 由于conv2d需要一个四维的输入数据,因此需要手动添加一个维度。
encoder_embedded = tf.expand_dims(encoder_embedded, -1) # shape(encoder_embedded) = [None, user_review_num*u_n_words, embedding_size, 1]
outputs = cnn(input_emb = encoder_embedded, filter_sizes = filter_sizes, num_filters = num_filters)
self.logits = keras.layers.Dense(label_num, use_bias=True)(outputs)
self.probability = tf.nn.softmax(self.logits, name='probability')
self.cost = tf.nn.sparse_softmax_cross_entropy_with_logits(
labels = self.Y,
logits = self.logits)
self.cost = tf.reduce_mean(self.cost)
self.optimizer = tf.train.AdamOptimizer(learning_rate = learning_rate).minimize(self.cost)
self.pre_y = tf.argmax(self.logits, 1, name='pre_y')
correct_pred = tf.equal(self.pre_y, self.Y)
self.accuracy = tf.reduce_mean(tf.cast(correct_pred, tf.float32))
2.5 训练模型
- 将预处理好的数据集切分为80%的训练集,10%作为验证集,10%作为测试集
- 每使用一次训练集进行训练后,就使用验证集进行测试。
- 当验证集的准确率连续下降5次,就停止步骤2,然后使用测试集的结果作为模型的最终性能。