深度学习-使用TextCNN网络来分析与疫情相关的数据
有关文本分类问题就可以使用TextCNN网络来解决
下面仅展示构建TextCNN网络的编码逻辑,具体过程请自行百度
深度学习模型简单框架–Tensorflow
创建python工程并创建data文件夹加入相关数据
数据举例
微博id 微博发布时间 发布人账号 微博中文内容 微博图片 微博视频 情感倾向
4.45607E+15 01月01日 23:47 千年空城的西窗 哎,新年第一天发烧感冒,也是够够的,头都没开好,希望新的一年顺顺利利的??????? [] [] 1
4.45607E+15 01月01日 23:31 空心少年丶Ivens 在家的时候发烧还会坚持上网,上学的时候打个喷嚏都会觉得是癌症晚期。? [] [] -1
4.45607E+15 01月01日 23:38 Yv_sann 现在是北京时间11点38分我发了一个晚上的烧,难受死了,去给自己弄个冷毛巾吧,哇第一次发烧发得这么厉害,我快死了,不要祭奠我行了朋友们.我已经灭绝了眼睛都睁不开了现在睡吧.晚安分享单曲KShapeOfMyHeart(电影《这个杀手不太冷》片尾曲)(@网易云音乐)? [] [] -1
4.45605E+15 01月01日 22:28 FerrariChen-YQ 2020第一天就发烧,今年怕是可以过呢红红火火了。2芒市? [] [] 1
4.45607E+15 01月01日 23:44 Ffuhyun 新年第一天在发烧当中过去了? [] [] 0
4.45607E+15 01月01日 23:33 竺倪娜 2020第一天就发烧很丧? [] [] -1
4.45598E+15 01月01日 17:28 想吃草莓味 ??//@朴素善xi:是的,冷暖自知//@·飛行島嶼·:他们年少相识一起成长一起出道互相照顾经历了我看不到的岁月和曲折发烧感冒不是你第一时间知道快乐喜悦不是和你第一时间分享我算什么你又算什么有什么资格去点评他们的感情 "['https://ww1.sinaimg.cn/thumb150/913d36c3gy1gah33rk4p6j20hs0mrjtn.jpg', 'https://ww4.sinaimg.cn/orj360/913d36c3gy1gah2wewr7jj20sg0sgwht.jpg', 'https://ww1.sinaimg.cn/orj360/913d36c3gy1gah2wdup3kj20sg0sgtbn.jpg']" [] 1
4.45607E+15 01月01日 23:43 用户7364909443 可怜的大宝,今天咳嗽发烧,还被我训,被我打,为什么要乱扔东西呢?为什么要打人呢?妈妈骂你打你但是也很心疼你,希望你明天病情好转,妈妈好担心你2榆林·府谷县? [] [] 1
构建CNN网络
import numpy as np
import tensorflow as tf
# text_CNN网络模型
class Text_CNN_Network(object):
def __init__(self,vocab_size,batch_size,num_class=2,region_size=[2,3,4],num_filter=2,embedding_dimensions=128):
# 类别个数
self.num_class = num_class
# 卷积输出的大小|卷积核的深度
self.num_filter = num_filter
# 区域的大小(卷积核的行)
self.region_size = region_size
# 给定批次大小,批次大小指的是包含句子的个数
self.batch_size = batch_size
# 词向量维度
self.embedding_dimensions = embedding_dimensions
# 词典长度
self.vocab_size = vocab_size
# 用于计算模型的损失
self.logists = None
#模型最终计算出来属于类别的概率
self.probability = None
# 模型最终预测的结果[batch_size]出现的是每句话得出的类别
self.predict = None
# x的值
self.inputs = None
# 模型的持久化参数
self.saver_parameters = {}
self.saver = None
self.interface()
pass
# 前向网络的构建
def interface(self):
# TextCNN网络
with tf.name_scope("Text_CNN"):
# 模型的输入
with tf.variable_scope('input'):
#[batch_size,lens] lens:句子中最多有多少个单词
# self.inputs 做成类的一个inputs属性
self.inputs = tf.placeholder(dtype=tf.int32,shape=[None,None],name='input')
# y的真实值 [0,2,1,1...]
self.target = tf.placeholder(dtype=tf.int32,shape=[None],names='target')
pass
# 词向量的转换
with tf.variable_scope('embedding'):
# [词典长度,词向量维度]
self.embedding_table = tf.get_variable(names='w',dtype=tf.float32,shape=[self.vocab_size,self.embedding_dimensions])
# 在词典中选择句子出现过的单词 把这个单词变为词向量
# self.embedding_table 词库 self.inputs 句子
# embedding_lookup函数就是将句子里每个单词对应词典的序号一一对应并转换为词向量矩阵
embedding_input = tf.nn.embedding_lookup(self.embedding_table,self.inputs)
# embedding_input[batch_size,lens,词向量维度]
# 把embedding_input变为四维数据 [batch_size,lens,词向量维度+1] axis:0表示列/1表示行
expand_embedding_input = tf.expand_dims(embedding_input,axis=-1)
pass
# 卷积
outputs = []
for index,region_size in enumerate(self.region_size):
with tf.variable_scope('conv_{}'.format(index)):
# 卷积核
filter = tf.get_variable(
name='w',
# 控制一个卷积核的大小[卷积核行,卷积核列,1,卷积核深度(输出的大小)]
# 输出的卷积核:卷积核的深度为2(可以理解为做了两次,所以会有深度为2.也就是说有两个"两行四列深度1"的长方体组成的"两行四列深度为2"的卷积核)
shape=[region_size,self.embedding_dimensions,1,self.num_filter]
)
# 卷积(一个卷积核)
net = tf.nn.conv2d(
input=expand_embedding_input,
filter=filter,
# 控制卷积核的移动 [1,1,1,1]
strides=[1,1,1,1],
padding='VALID'
)
# net [batch_size,
# 按照1,2维度求最大值
pool = tf.reduce_max(net,axis=[1,2],keep_dims=True)
# 压缩通道[beach_size,卷积核深度]
output_pool = tf.squeeze(pool,axis=[1,2])
outputs.append(output_pool)
pass
pass
# 合并高阶特征
with tf.variable_scope('merge_feature'):
# 把outputs按照最后一列进行合并 feature:[batch_size,卷积核深度*卷积核个数]
features = tf.concat(outputs,axis=-1)
pass
# 神经网络(全连接)
with tf.variable_scope('neuralnetwork'):
w = tf.get_variable(name='w',shape=[self.num_filter*len(self.region_size),self.num_class],dtype=tf.float32)
# 全链接计算
out = tf.matmul(features,w)
pass
# 输出
with tf.variable_scope('output'):
# 在tf中给out个名字
self.logists = tf.identity(out,'logists')
# 对out做一个softmax 归一化---多分类相加概率为1
self.probability = tf.nn.softmax(self.logists,name='probability')
# argmax 模型预测概率 axis 1|-1都代表行(水平方向) 0代表垂直方向
self.predict = tf.argmax(self.probability,axis=-1,name='predict')
pass
pass
# 构建损失函数
def losses(self):
with tf.name_scope('loss'):
# y的真实值 y的真实值:self.logists 每个句子的损失
loss = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=None,logits=self.logists)
# 模型的损失 求均值
loss = tf.reduce_mean(loss)
# 模型的可视化
tf.losses.add_loss(loss)
total_loss = tf.losses.get_total_loss(name='total_loss')
tf.summary.scalar('loss',loss)
tf.summary.scalar('total_loss',total_loss)
pass
return loss
# 优化损失
def optimizer(self,loss):
with tf.name_scope('train'):
# 选择优化器
optimizer = tf.train.AdamOptimizer(learning_rate=0.0001)
# 优化损失
train_op = optimizer.minimize(loss=loss)
pass
return train_op
# 模型评估
def metrics(self):
# 准确率
with tf.name_scope('accuracy'):
# 真实值
target = self.target
# 预测值
predict = self.predict
# target和predict类型一致
if target.dtype != predict.dtype:
# cast类型转换
predict = tf.cast(predict, target.dtype)
# true和false变为float类型
acc = tf.to_float(tf.equal(predict, target))
acc = tf.reduce_mean(acc)
return acc
# 模型的保存
def save(self,session,save_path):
if self.saver is None:
self.saver = tf.train.Saver(**self.saver_parameters)
# 模型的保存
self.saver.save(sess=session,save_path=save_path)
tf.logging.info('模型持久化完成')
pass
# 模型的恢复 下次再运行的时候,直接从上一次保存的模型中读取
def restore(self,checkpoint_dir,session):
if self.saver is None:
self.saver = tf.train.Saver(**self.saver_parameters)
# 检查持久化文件是否存在
ckpt = tf.train.get_checkpoint_state(checkpoint_dir)
# 拿到持久化对象进行判断
if ckpt and ckpt.model_checkpoint_path:
tf.logging.info('开始模型')
# 模型的参数恢复
self.saver.restore(sess=session,save_path=ckpt.model_checkpoint_path)
else:
tf.logging.warn('没有找到训练好的模型,不能进行恢复操作')
pass
pass
创建数据预处理工具类data_utils.py
import numpy as np
import pandas as pd
import re
from utils.vocab_utils import VocabUtils
data_max_len = 0
# 数据的清洗
def clean_str(string):
"""
Tokenization/string cleaning for all datasets except for SST.
Every dataset is lower cased except for TREC
# https://github.com/yoonkim/CNN_sentence/blob/master/process_data.py
"""
# 强制转换str
string = str(string)
string = re.sub(r"[^\u4e00-\u9fa5A-Za-z0-9(),,。?!!?\'\`]", "", string)
string = re.sub(r"\'s", " \'s", string)
string = re.sub(r"\'ve", " \'ve", string)
string = re.sub(r"n\'t", " n\'t", string)
string = re.sub(r"\'re", " \'re", string)
string = re.sub(r"\'d", " \'d", string)
string = re.sub(r"\'ll", " \'ll", string)
string = re.sub(r",", " , ", string)
string = re.sub(r"!", " ! ", string)
string = re.sub(r"\(", " \( ", string)
string = re.sub(r"\)", " \) ", string)
string = re.sub(r"\?", " \? ", string)
string = re.sub(r"\s{2,}", " ", string)
# 计算文本最大长度
global data_max_len
if len(string) > data_max_len:
data_max_len = len(string)
return string.strip().lower()
#读取数据返回x与y
def get_x_y(path):
names = ['微博中文内容','情感倾向']
# 读取数据
data = pd.read_csv(path,sep='\t')
# 选择需要的列
data = data[names]
# 删除nan的数据
data = data.dropna(how='any')
# 发现空数据 object
data['情感倾向'] = data['情感倾向'].replace('·','0')
data['情感倾向'] = data['情感倾向'].replace('-', '0')
data['情感倾向'] = data['情感倾向'].replace('10', '1')
data['情感倾向'] = data['情感倾向'].replace('-2','-1')
data['情感倾向'] = data['情感倾向'].replace('9', '1')
data['情感倾向'] = data['情感倾向'].replace('4', '1')
data['情感倾向'] = data['情感倾向'].replace('-1', '2')
data['微博中文内容'] = data['微博中文内容'].apply(clean_str)
# 518
# print("文本最大长度", data_max_len)
return data['微博中文内容'].values, data['情感倾向'].values
# MBGD 将数量巨多的数据分批成一小块 一小块来执行
# ,batch_size批次大小 num_epochs表示完整训练多少次train
def batch_iter(data,batch_size,num_epochs):
data = np.array(data)
data_size = len(data)
# 一个epochs里有多少个batch_size
num_batches_per_epoch = int(len(data)/batch_size)+1
for epoch in range(num_epochs):
# 一个大批次中有多少个小批次
for batch_num in range(num_batches_per_epoch):
start_index = batch_num * batch_size
end_index = min((batch_num+1)*batch_size,data_size)
yield data[start_index:end_index]
pass
pass
#读取最终预测的数据返回x
def get_x(path):
names = ['微博中文内容']
# 读取数据
data = pd.read_csv(path)
# 选择需要的列
data = data[names]
# 发现空数据 object
data['微博中文内容'] = data['微博中文内容'].apply(clean_str)
return data['微博中文内容'].values
# model = VocabUtils.load_model('../model/vocab.pkl')
# 大约15万个
# print('词典大小',len(model.vocabulary_))
# result = model.transform(['今天天气真好'])
#x,y = get_x_y('../data/data_train_utf8.txt')
#VocabUtils.build_model(x)
创建词典工具类vocab_utils.py
使用工具类的build_model方法先生成vocab.pkl词典
import jieba
from tensorflow.contrib.learn import preprocessing
# 进行分词
def split_fn(document):
return [jieba.lcut(sentencs) for sentencs in document]
class VocabUtils(object):
# 生成词典
@staticmethod
def build_model(document):
# 词典初始化
model = preprocessing.VocabularyProcessor(
max_document_length=512,
tokenizer_fn=split_fn
)
# 分词并创建词典
model.fit(raw_documents=document)
# 词典保存
model.save('../model/vocab.pkl')
pass
@staticmethod
def load_model(path):
# 词典的恢复
model = preprocessing.VocabularyProcessor.restore(path)
return model
pass
train.py
import tensorflow as tf
from utils.data_utils import get_x_y
from net.Test_CNN import Text_CNN_Network
from utils.vocab_utils import VocabUtils
from sklearn.model_selection import train_test_split
from utils.data_utils import batch_iter
import numpy as np
def main(_):
# 读取数据
x,y = get_x_y("./data/data_train_utf8.txt")
#读取词典pkl
vocab_model = VocabUtils.load_model('./model/vocab.pkl')
# 创建图
with tf.Graph().as_default():
#创建模型
model = Text_CNN_Network(vocab_size=len(vocab_model.vocabulary_),num_class=3);
# 计算损失
train_loss = model.losses()
# 优化损失
train_op = model.optimizer(train_loss)
# 模型评估
acc = model.metrics()
# 可视化
summary_op = tf.summary.merge_all()
# 把图写入固定文件
summary_writer = tf.summary.FileWriter(logdir='./graph',graph=tf.get_default_graph())
# 开始执行图
with tf.Session() as session:
# 模型的参数初始化
session.run(tf.global_variables_initializer())
print('模型初始化完成等待训练数据')
# 模型的恢复
model.restore(checkpoint_dir='./model',session=session)
# 喂给模型数据
# 将文本转换为id
x = np.asarray(list(vocab_model.transform(x)))
# 切割训练集和测试集
x_train,x_test,y_train,y_test = train_test_split(x,y,test_size=0.1,random_state=28)
# 把y变为int类型 用于计算
y_test = [int(i) for i in y_test]
y_train = [int(i) for i in y_train]
batches = batch_iter(
data = list(zip(x_train,y_train)),
batch_size=2000,
num_epochs=10
)
step = 0
# 模型训练
for batch in batches:
step += 1;
# 从batches中提取训练集合的 x与y
x_batch,y_batch = zip(*batch)
_,loss_,acc_,summary_ = session.run([train_op,train_loss,acc,summary_op],feed_dict={
# 传入数据
model.inputs:x_batch,
model.target:y_batch
})
# 添加模型的可视化
summary_writer.add_summary(summary_)
print('第{}次训练,模型损失为{},准确率为{}'.format(step,loss_,acc_))
# 模型的保存
if step%50 == 0:
model.save(session=session,save_path='./model/text_cnn.ckpt')
pass
# 模型的评估
if step % 100 == 0:
print("\nEvaluation:")
dev_batches = batch_iter(
data=list(zip(x_test, y_test)),
batch_size=200,
num_epochs=1
)
for dev_batch in dev_batches:
# 从dev_batch中提取测试集合的x。y
dev_x_batch, dev_y_batch = zip(*dev_batch)
_, dev_loss_, dev_acc_ = session.run([train_op, train_loss, acc], feed_dict={
model.inputs: dev_x_batch,
model.target: dev_y_batch
})
print('模型损失为{},准确率为{}'.format(dev_loss_, dev_acc_))
pass
pass
pass
pass
if __name__ == '__main__':
tf.app.run()
通过训练出的模型预测新数据eval.py
import tensorflow as tf
import pandas as pd
import numpy as np
from utils.vocab_utils import VocabUtils
from utils.data_utils import get_x
from utils.data_utils import batch_iter
def main(_):
with tf.Graph().as_default():
graph = tf.get_default_graph()
with tf.Session() as session:
# 模型的恢复
ckpt = tf.train.get_checkpoint_state(checkpoint_dir='./model')
if not (ckpt and ckpt.model_checkpoint_path):
raise Exception("不存在模型")
tf.logging.info('正在进行模型的恢复')
saver = tf.train.import_meta_graph('{}.meta'.format(ckpt.model_checkpoint_path))
saver.restore(sess=session,save_path=ckpt.model_checkpoint_path)
# 加载词典做文本id转换,给模型输入
vocab_model_path = './model/vocab.pkl'
if not tf.gfile.Exists(vocab_model_path):
raise Exception('词典不存在')
vocab_model = VocabUtils.load_model(vocab_model_path)
# 拿到测试集合中的x
data_x = get_x('./data/data_test.txt')
# 模型的输入
texts = np.asarray(list(vocab_model.transform(data_x)))
# 获取tensor对象
inputs = graph.get_tensor_by_name('Text_CNN/input/input:0')
predictions = graph.get_tensor_by_name('Text_CNN/output/predict:0')
# 构建批次
batches = batch_iter(
data=list(texts),
batch_size=200,
num_epochs=1
)
# 模型的预测
all_prediction = []
for x_batch in batches:
batch_predictions = session.run(predictions,feed_dict={inputs:x_batch})
# 拼接一次一次的预测
all_prediction = np.concatenate([all_prediction,batch_predictions])
pass
all_prediction = np.asarray(all_prediction,dtype=np.int32).reshape(-1)
# 保存到文件
with open('./predict/prediction.txt','w',encoding='utf-8') as file:
for temp in all_prediction:
file.write(str(temp)+'\n')
pass
pass
if __name__ == '__main__':
tf.app.run()