Python使用循环神经网络解决文本分类问题的方法详解
本文实例讲述了Python使用循环神经网络解决文本分类问题的方法。分享给大家供大家参考,具体如下:
1、概念
1.1、循环神经网络
1.2、长短期记忆网络
#实现LSTM网络
#生成Cell网格所需参数
def_generate_paramas(x_size,h_size,b_size):
x_w=tf.get_variable('x_weight',x_size)
h_w=tf.get_variable('h_weight',h_size)
bias=tf.get_variable('bias',b_size,initializer=tf.constant_initializer(0.0))
returnx_w,h_w,bias
scale=1.0/math.sqrt(embedding_size+lstm_nodes[-1])/3.0
lstm_init=tf.random_uniform_initializer(-scale,scale)
withtf.variable_scope('lstm_nn',initializer=lstm_init):
#输入门参数
withtf.variable_scope('input'):
x_in,h_in,b_in=_generate_paramas(
x_size=[embedding_size,lstm_nodes[0]],
h_size=[lstm_nodes[0],lstm_nodes[0]],
b_size=[1,lstm_nodes[0]]
)
#输出门参数
withtf.variable_scope('output'):
x_out,h_out,b_out=_generate_paramas(
x_size=[embedding_size,lstm_nodes[0]],
h_size=[lstm_nodes[0],lstm_nodes[0]],
b_size=[1,lstm_nodes[0]]
)
#遗忘门参数
withtf.variable_scope('forget'):
x_f,h_f,b_f=_generate_paramas(
x_size=[embedding_size,lstm_nodes[0]],
h_size=[lstm_nodes[0],lstm_nodes[0]],
b_size=[1,lstm_nodes[0]]
)
#中间状态参数
withtf.variable_scope('mid_state'):
x_m,h_m,b_m=_generate_paramas(
x_size=[embedding_size,lstm_nodes[0]],
h_size=[lstm_nodes[0],lstm_nodes[0]],
b_size=[1,lstm_nodes[0]]
)
#两个初始化状态,隐含状态state和初始输入h
state=tf.Variable(tf.zeros([batch_size,lstm_nodes[0]]),trainable=False)
h=tf.Variable(tf.zeros([batch_size,lstm_nodes[0]]),trainable=False)
#遍历LSTM每轮循环,即每个词的输入过程
foriinrange(max_words):
#取出每轮输入,三维数组embedd_inputs的第二维代表训练的轮数
embedded_input=embedded_inputs[:,i,:]
#将取出的结果reshape为二维
embedded_input=tf.reshape(embedded_input,[batch_size,embedding_size])
#遗忘门计算
forget_gate=tf.sigmoid(tf.matmul(embedded_input,x_f)+tf.matmul(h,h_f)+b_f)
#输入门计算
input_gate=tf.sigmoid(tf.matmul(embedded_input,x_in)+tf.matmul(h,h_in)+b_in)
#输出门
output_gate=tf.sigmoid(tf.matmul(embedded_input,x_out)+tf.matmul(h,h_out)+b_out)
#中间状态
mid_state=tf.tanh(tf.matmul(embedded_input,x_m)+tf.matmul(h,h_m)+b_m)
#计算隐含状态state和输入h
state=state*forget_gate+input_gate*mid_state
h=output_gate+tf.tanh(state)
#最后遍历的结果就是LSTM的输出
last_output=h
1.3、文本分类
2、通过TextRNN进行文本分类
2.1、数据预处理
#将文件中的句子通过jieba库拆分为单个词
defsegment_word(input_file,output_file):
#循环遍历训练数据集的每一个文件
forroot,folders,filesinos.walk(input_file):
print('root:',root)
forfolderinfolders:
print('dir:',folder)
forfileinfiles:
file_dir=os.path.join(root,file)
withopen(file_dir,'rb')asin_file:
#读取文件中的文本
sentence=in_file.read()
#通过jieba函数库将句子拆分为单个词组
words=jieba.cut(sentence)
#文件夹路径最后两个字即为分类名
content=root[-2:]+'\t'
#去除词组中的空格,排除为空的词组
forwordinwords:
word=word.strip('')
ifword!='':
content+=word+''
#换行并将文本写入输出文件
content+='\n'
withopen(output_file,'a')asoutfile:
outfile.write(content.strip(''))
#统计每个词出现的频率
defget_list(segment_file,out_file):
#通过词典保存每个词组出现的频率
word_dict={}
withopen(segment_file,'r')asseg_file:
lines=seg_file.readlines()
#遍历文件的每一行
forlineinlines:
line=line.strip('\r\n')
#将一行按空格拆分为每个词,统计词典
forwordinline.split(''):
#如果这个词组没有在word_dict词典中出现过,则新建词典项并设为0
word_dict.setdefault(word,0)
#将词典word_dict中词组word对应的项计数加一
word_dict[word]+=1
#将词典中的列表排序,关键字为列表下标为1的项,且逆序
sorted_list=sorted(word_dict.items(),key=lambdad:d[1],reverse=True)
withopen(out_file,'w')asoutfile:
#将排序后的每条词典项写入文件
foriteminsorted_list:
outfile.write('%s\t%d\n'%(item[0],item[1]))
2.2、数据读入
#定义超参数 embedding_size=32#每个词组向量的长度 max_words=10#一个句子最大词组长度 lstm_layers=2#lstm网络层数 lstm_nodes=[64,64]#lstm每层结点数 fc_nodes=64#全连接层结点数 batch_size=100#每个批次样本数据 lstm_grads=1.0#lstm网络梯度 learning_rate=0.001#学习率 word_threshold=10#词表频率门限,低于该值的词语不统计 num_classes=4#最后的分类结果有4类
classWord_list:
def__init__(self,filename):
#用词典类型来保存需要统计的词组及其频率
self._word_dic={}
withopen(filename,'r',encoding='GB2312',errors='ignore')asf:
lines=f.readlines()
forlineinlines:
word,freq=line.strip('\r\n').split('\t')
freq=int(freq)
#如果词组的频率小于阈值,跳过不统计
iffreq
定义TextData类来完成数据的读入和管理,在__init__()函数中读取刚才处理好的train_segment.txt文件,根据制表符分割类别标记和句子词组,将类别和句子分别转化为数字id。如果句子的词组超过了最大阈值,则截去后面多余的,如果不够则用-1填充。定义类函数_shuffle_data()用于清洗数据,next_batch()用于按批次返回数据和标签,get_size()用于返回词组总条数。
classTextData:
def__init__(self,segment_file,word_list):
self.inputs=[]
self.labels=[]
#通过词典管理文本类别
self.label_dic={'体育':0,'校园':1,'女性':2,'出版':3}
self.index=0
withopen(segment_file,'r')asf:
lines=f.readlines()
forlineinlines:
#文本按制表符分割,前面为类别,后面为句子
label,content=line.strip('\r\n').split('\t')[0:2]
self.content_size=len(content)
#将类别转换为数字id
label_id=self.label_dic.get(label)
#将句子转化为embedding数组
content_id=word_list.sentence2id(content)
#如果句子的词组长超过最大值,截取max_words长度以内的id值
content_id=content_id[0:max_words]
#如果不够则填充-1,直到max_words长度
padding_num=max_words-len(content_id)
content_id=content_id+[-1foriinrange(padding_num)]
self.inputs.append(content_id)
self.labels.append(label_id)
self.inputs=np.asarray(self.inputs,dtype=np.int32)
self.labels=np.asarray(self.labels,dtype=np.int32)
self._shuffle_data()
#对数据按照(input,label)对来打乱顺序
def_shuffle_data(self):
r_index=np.random.permutation(len(self.inputs))
self.inputs=self.inputs[r_index]
self.labels=self.labels[r_index]
#返回一个批次的数据
defnext_batch(self,batch_size):
#当前索引+批次大小得到批次的结尾索引
end_index=self.index+batch_size
#如果结尾索引大于样本总数,则打乱所有样本从头开始
ifend_index>len(self.inputs):
self._shuffle_data()
self.index=0
end_index=batch_size
#按索引返回一个批次的数据
batch_inputs=self.inputs[self.index:end_index]
batch_labels=self.labels[self.index:end_index]
self.index=end_index
returnbatch_inputs,batch_labels
#获取词表数目
defget_size(self):
returnself.content_size
#训练数据集对象
train_set=TextData(train_segment_dir,train_list)
#print(data_set.next_batch(10))
#训练数据集词组条数
train_list_size=train_set.get_size()
2.3、构建计算图模型
定义函数create_model来实现计算图模型的构建。首先定义模型输入的占位符,分别为输入文本inputs、输出标签outputs、Dropout的比率keep_prob。
首先构建embedding层,将输入的inputs编码抽取出来拼接成一个矩阵,例如输入[1,8,3]则抽取embeding[1]、embedding[8]和embedding[3]拼接成一个矩阵
接下来构建LSTM网络,这里构建了两层网络,每层的结点数在之前的参数lstm_node[]数组中定义。每个cell的构建通过函数tf.contrib.rnn.BasicLSTMCell实现,之后经过Dropout操作。再将两个cell合并为一个LSTM网络,通过函数tf.nn.dynamic_rnn将输入embedded_inputs输入到LSTM网络中进行训练得到输出rnn_output。这是一个三维数组,第二维表示训练的步数,我们只取最后一维的结果,即下标值为-1.
接下来构建全连接层,通过tf.layers.dense函数定义全连接层,再经过一个dropout操作后将输出映射到类别上,类别的种类的参数num_classes,得到估计值logits
接下来就可以求损失、精确率等评估值了。计算算预测值logits与标签值outputs之间的交叉熵损失值,接下来通过arg_max计算预测值,进而求准确率
接下来定义训练方法,通过梯度裁剪应用到变量上以防止梯度消失。
最后将输入占位符、损失等评估值、其他训练参数返回到调用函数的外部。
#创建计算图模型
defcreate_model(list_size,num_classes):
#定义输入输出占位符
inputs=tf.placeholder(tf.int32,(batch_size,max_words))
outputs=tf.placeholder(tf.int32,(batch_size,))
#定义是否dropout的比率
keep_prob=tf.placeholder(tf.float32,name='keep_rate')
#记录训练的总次数
global_steps=tf.Variable(tf.zeros([],tf.float32),name='global_steps',trainable=False)
#将输入转化为embedding编码
withtf.variable_scope('embedding',
initializer=tf.random_normal_initializer(-1.0,1.0)):
embeddings=tf.get_variable('embedding',[list_size,embedding_size],tf.float32)
#将指定行的embedding数值抽取出来
embedded_inputs=tf.nn.embedding_lookup(embeddings,inputs)
#实现LSTM网络
scale=1.0/math.sqrt(embedding_size+lstm_nodes[-1])/3.0
lstm_init=tf.random_uniform_initializer(-scale,scale)
withtf.variable_scope('lstm_nn',initializer=lstm_init):
#构建两层的lstm,每层结点数为lstm_nodes[i]
cells=[]
foriinrange(lstm_layers):
cell=tf.contrib.rnn.BasicLSTMCell(lstm_nodes[i],state_is_tuple=True)
#实现Dropout操作
cell=tf.contrib.rnn.DropoutWrapper(cell,output_keep_prob=keep_prob)
cells.append(cell)
#合并两个lstm的cell
cell=tf.contrib.rnn.MultiRNNCell(cells)
#将embedded_inputs输入到RNN中进行训练
initial_state=cell.zero_state(batch_size,tf.float32)
#runn_output:[batch_size,num_timestep,lstm_outputs[-1]
rnn_output,_=tf.nn.dynamic_rnn(cell,embedded_inputs,initial_state=initial_state)
last_output=rnn_output[:,-1,:]
#构建全连接层
fc_init=tf.uniform_unit_scaling_initializer(factor=1.0)
withtf.variable_scope('fc',initializer=fc_init):
fc1=tf.layers.dense(last_output,fc_nodes,activation=tf.nn.relu,name='fc1')
fc1_drop=tf.contrib.layers.dropout(fc1,keep_prob)
logits=tf.layers.dense(fc1_drop,num_classes,name='fc2')
#定义评估指标
withtf.variable_scope('matrics'):
#计算损失值
softmax_loss=tf.nn.sparse_softmax_cross_entropy_with_logits(logits=logits,labels=outputs)
loss=tf.reduce_mean(softmax_loss)
#计算预测值,求第1维中最大值的下标,例如[1,1,5,3,2]argmax=>2
y_pred=tf.argmax(tf.nn.softmax(logits),1,output_type=tf.int32)
#求准确率
correct_prediction=tf.equal(outputs,y_pred)
accuracy=tf.reduce_mean(tf.cast(correct_prediction,tf.float32))
#定义训练方法
withtf.variable_scope('train_op'):
train_var=tf.trainable_variables()
#forvarintrain_var:
#print(var)
#对梯度进行裁剪防止梯度消失或者梯度爆炸
grads,_=tf.clip_by_global_norm(tf.gradients(loss,train_var),clip_norm=lstm_grads)
#将梯度应用到变量上去
optimizer=tf.train.AdamOptimizer(learning_rate=learning_rate)
train_op=optimizer.apply_gradients(zip(grads,train_var),global_steps)
#以元组的方式将结果返回
return((inputs,outputs,keep_prob),
(loss,accuracy),
(train_op,global_steps))
#调用构建函数,接收解析返回的参数
placeholders,matrics,others=create_model(train_list_size,num_classes)
inputs,outputs,keep_prob=placeholders
loss,accuracy=matrics
train_op,global_steps=others
2.4、进行训练
通过Session运行计算图模型,从train_set中按批次获取训练集数据并填充占位符,运行sess.run,获取损失值、准确率等中间值打印
#进行训练
init_op=tf.global_variables_initializer()
train_keep_prob=0.8#训练集的dropout比率
train_steps=10000
withtf.Session()assess:
sess.run(init_op)
foriinrange(train_steps):
#按批次获取训练集数据
batch_inputs,batch_labels=train_set.next_batch(batch_size)
#运行计算图
res=sess.run([loss,accuracy,train_op,global_steps],
feed_dict={inputs:batch_inputs,outputs:batch_labels,
keep_prob:train_keep_prob})
loss_val,acc_val,_,g_step_val=res
ifg_step_val%20==0:
print('第%d轮训练,损失:%3.3f,准确率:%3.5f'%(g_step_val,loss_val,acc_val))
在我的数据集进行一万轮训练后,训练集的准确率在90%左右徘徊
源代码及相关数据文件:https://github.com/SuperTory/MachineLearning/tree/master/TextRNN
更多关于Python相关内容感兴趣的读者可查看本站专题:《Python数据结构与算法教程》、《Python加密解密算法与技巧总结》、《Python编码操作技巧总结》、《Python函数使用技巧总结》、《Python字符串操作技巧汇总》及《Python入门与进阶经典教程》
希望本文所述对大家Python程序设计有所帮助。
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。