TextCNN textcnn 新闻文本分类实战
目录
项目介绍:
数据示例:
textcnn原理:
导入库包 :
数据预处理:
参数设定:
构建模型:
模型训练函数:
模型训练与预测:
---------------------------------------------------------------------------------------------------------------------------------
项目介绍:
本项目采用的是THUCNews数据集(缩减版),支持财经、房产等10个领域的问题快速分类,并覆盖教育类信息检索系统所需的关键场景。经过优化后的新模型在测试集上的准确率达到87.7%。
数据示例:

textcnn原理:

输入层:
文本矩阵。
处理其中的词语时, 首先构建一个大型词汇表, 然后通过使用Word2Vec模型进行训练, 生成相应的词向量, 将这些向量组合起来就能形成整个文本的矩阵表示。
注意点:
特征:这里用的是词向量表示方式
数据量较大:便于随机初始化embeddings,并通过模型网络利用语料库进行训练以实现embeddings的更新与优化。
在数据规模较小时,可以通过外部语料进行词向量的fine-tuning(pre-training)。这些词向量会被输入至Embedding层,并使用预训练的词向量矩阵来进行初始化。
(1)static(静态词向量)
使用预训练的词向量,即利用word2vec、fastText或者Glove等词向量工具,在开放领域数据上进行无监督的学习,获得词汇的具体词向量表示方式,拿来直接作为输入层的输入,并且在TextCNN模型训练过程中不再调整词向量, 这属于迁移学习在NLP领域的一种具体的应用。
(2)non-static(非静态词向量)
在训练过程中对embeddings进行更新和微调(fine tune),能加速收敛。(通过设置trainable=True)。预训练的词向量+ 动态调整 , 即拿word2vec训练好的词向量初始化, 训练过程中再对词向量进行微调。
(3)multiple channel(多通道)
借鉴图像中的RGB三通道的思想, 这里也可以用 static 与 non-static 两种词向量初始化方式来搭建两个通道。
(4)CNN-rand(随机初始化)
指定词向量的维度embedding_size后,文本分类模型对不同单词的向量作随机初始化, 后续有监督学习过程中,通过BP的方向更新输入层的各个词汇对应的词向量。
卷积层:采用不同类型的卷积核进行计算,在设计时确保所有卷积核所对应的词向量维度保持一致,并使每一个特定类型的卷积核能够生成一个特征图。
Max-pooling: 每个feature map在经过 max-pooling操作后都会产生一个特征值,并且这一操作不仅简化了数据处理流程,并且能够有效适应不同长度的输入文本
全连接层:
该一层的输入是一维向量其来源为池化操作后的结果经过激活函数处理后再引入Dropout机制以防止模型过拟合并在此基础上在全连接层中加入l2正则项作为正则化手段。
Softmax:
通过应用softmax函数来处理全连接层的输出结果,在计算每个样本分配至不同类别中的概率。
导入库包 :
#导入库包
import tensorflow.keras as keras
import numpy as np
import os
from config import Config
from sklearn import metrics
from keras.models import Sequential
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Embedding,Dropout,Conv1D,ReLU,GlobalMaxPool1D,InputLayer
import tensorflow as tf
gpus = tf.config.list_physical_devices("GPU")
if gpus:
gpu0 = gpus[0] #如果有多个GPU,仅使用第0个GPU
tf.config.experimental.set_memory_growth(gpu0, True) #设置GPU显存用量按需使用
tf.config.set_visible_devices([gpu0],"GPU")
代码解读
数据预处理:
trainingSet_path = "cnews.train.txt"
valSet_path = "cnews.val.txt"
model_save_path = "CNN_model.h5"
testingSet_path = "cnews.test.txt"
#创建 文本处理类:preprocesser
class preprocesser(object):
def __init__(self):
self.config = Config()
# 读取文本txt 函数
def read_txt(self, txt_path):
with open(txt_path, "r", encoding='utf-8') as f:
data = f.readlines()
labels = []
contents = []
for line in data:
label, content = line.strip().split('\t')
labels.append(label)
contents.append(content)
return labels, contents
# 读取分词文档
def get_vocab_id(self):
vocab_path = "cnews.vocab.txt"
with open(vocab_path, "r", encoding="utf-8") as f:
infile = f.readlines()
vocabs = list([word.replace("\n", "") for word in infile])
vocabs_dict = dict(zip(vocabs, range(len(vocabs))))
return vocabs, vocabs_dict
# 获取新闻属性id 函数
def get_category_id(self):
categories = ["体育", "财经", "房产", "家居", "教育", "科技", "时尚", "时政", "游戏", "娱乐"]
cates_dict = dict(zip(categories, range(len(categories))))
return cates_dict
#将语料中各文本转换成固定max_length后返回各文本的标签与文本tokens
def word2idx(self, txt_path, max_length):
# vocabs:分词词汇表
# vocabs_dict:各分词的索引
vocabs, vocabs_dict = self.get_vocab_id()
# cates_dict:各分类的索引
cates_dict = self.get_category_id()
# 读取语料
labels, contents = self.read_txt(txt_path)
# labels_idx:用来存放语料中的分类
labels_idx = []
# contents_idx:用来存放语料中各样本的索引
contents_idx = []
# 遍历语料
for idx in range(len(contents)):
# tmp:存放当前语句index
tmp = []
# 将该idx(样本)的标签加入至labels_idx中
labels_idx.append(cates_dict[labels[idx]])
# contents[idx]:为该语料中的样本遍历项
# 遍历contents中各词并将其转换为索引后加入contents_idx中
for word in contents[idx]:
if word in vocabs:
tmp.append(vocabs_dict[word])
else:
# 第5000位设置为未知字符
tmp.append(5000)
# 将该样本index后结果存入contents_idx作为结果等待传回
contents_idx.append(tmp)
# 将各样本长度pad至max_length
x_pad = keras.preprocessing.sequence.pad_sequences(contents_idx, max_length)
y_pad = keras.utils.to_categorical(labels_idx, num_classes=len(cates_dict))
return x_pad, y_pad
def word2idx_for_sample(self, sentence, max_length):
# vocabs:分词词汇表
# vocabs_dict:各分词的索引
vocabs, vocabs_dict = self.get_vocab_id()
result = []
# 遍历语料
for word in sentence:
# tmp:存放当前语句index
if word in vocabs:
result.append(vocabs_dict[word])
else:
# 第5000位设置为未知字符,实际中为vocabs_dict[5000],使得vocabs_dict长度变成len(vocabs_dict+1)
result.append(5000)
x_pad = keras.preprocessing.sequence.pad_sequences([result], max_length)
return x_pad
pre = preprocesser() # 实例化preprocesser()类
代码解读
参数设定:
num_classes = 10 # 类别数
vocab_size = 5000 #语料词大小
seq_length = 600 #词长度
conv1_num_filters = 128 # 第一层输入卷积维数
conv1_kernel_size = 1 # 卷积核数
conv2_num_filters = 64 # 第二层输入卷维数
conv2_kernel_size = 1 # 卷积核数
hidden_dim = 128 # 隐藏层维度
dropout_keep_prob = 0.5 # dropout层丢弃0.5
batch_size = 64 # 每次训练批次数
代码解读
构建模型:
def TextCNN():
#创建模型序列
model = Sequential()
model.add(InputLayer((seq_length,)))
model.add(Embedding(vocab_size+1, 256, input_length=seq_length))
model.add(Conv1D(conv1_num_filters, conv1_kernel_size, padding="SAME"))
model.add(Conv1D(conv2_num_filters, conv2_kernel_size, padding="SAME"))
model.add(GlobalMaxPool1D())
model.add(Dense(hidden_dim))
model.add(Dropout(dropout_keep_prob))
model.add(ReLU())
model.add(Dense(num_classes, activation="softmax"))
model.compile(loss="categorical_crossentropy",
optimizer="adam",
metrics=["acc"])
return model
代码解读

模型训练函数:
def train(epochs):
model = TextCNN()
model.summary()
x_train, y_train = pre.word2idx(trainingSet_path, max_length=seq_length)
x_val, y_val = pre.word2idx(valSet_path, max_length=seq_length)
model.fit(x_train, y_train,batch_size=batch_size,epochs=epochs,validation_data=(x_val, y_val))
model.save(model_save_path, overwrite=True)
代码解读
模型训练与预测:
if __name__ == '__main__':
train(20) # 训练模型
model = keras.models.load_model(model_save_path)
print("-----model loaded-----")
model.summary()
test = preprocesser()
# 测试文本
x_test = '5月6日,上海莘庄基地田径特许赛在第二体育运动学校鸣枪开赛。男子110米栏决赛,19岁崇明小囡秦伟搏以13.35秒的成绩夺冠,创造本赛季亚洲最佳。谢文骏迎来赛季首秀,以13.38秒获得亚军'
x_test = test.word2idx_for_sample(x_test, 600)
categories = ["体育", "财经", "房产", "家居", "教育", "科技", "时尚", "时政", "游戏", "娱乐"]
pre_test = model.predict(x_test)
index = int(np.argmax(pre_test, axis=1)[0])
print('该新闻为:', categories[index])
代码解读
Epoch 1/20
782/782 [==============================] - 6s 4ms/step - loss: 0.7376 - acc: 0.7666 - val_loss: 0.4742 - val_acc: 0.8552
Epoch 2/20
782/782 [==============================] - 3s 4ms/step - loss: 0.3939 - acc: 0.8814 - val_loss: 0.4187 - val_acc: 0.8702
......
Epoch 19/20
782/782 [==============================] - 3s 4ms/step - loss: 0.1658 - acc: 0.9455 - val_loss: 0.5052 - val_acc: 0.8692
Epoch 20/20
782/782 [==============================] - 3s 4ms/step - loss: 0.1647 - acc: 0.9454 - val_loss: 0.5027 - val_acc: 0.8768
该新闻为: 教育
代码解读
