之前介绍过RNN的分类,本文介绍一下使用预训练词向量进行RNN+Attention的分类模型。
下面来正式开始,RNN+Attention在tensorflow中的实现。
运行环境
python 3.6
tensorflow 1.2
本文GITHUB
正文
1.数据预处理
2.模型构建
3.模型训练与测试
4.模型验证
5.总结
1 数据预处理
本实验是使用THUCNews的一个子集进行训练与测试,文本类别涉及10个类别:categories = ['体育', '财经', '房产', '家居', '教育', '科技', '时尚', '时政', '游戏', '娱乐']。这一步的主要作用是将标签与文本分开,然后将文本与标签分别转化为模型所需要的数据格式。例如:文本:[我 爱 体育],标签:体育。转换成:文本[3 4 20],标签[1 0 0 0 0 0 0 0 0 0]。
然后是padding,按照动态RNN的做法应该是根据不同batch里面句子的长度对不同batch进行padding,但本实验所用素材来自新闻,每个句子其实就是一篇报道,特别的长,故分配指定长度,长度为250,故按照这个长度来padding。最后是生成batch的过程。
2 模型构建
模型采用的是动态双向LSTM,对于输出结果,采用拼接的方式,输入词向量的shape是[batch_size,seq_length,embedding_dim],输出为[batch_size,seq_length,2*hidden_dim]。
with tf.name_scope('Cell'):
cell_fw = tf.contrib.rnn.BasicLSTMCell(pm.hidden_dim)
Cell_fw = tf.contrib.rnn.DropoutWrapper(cell_fw, self.keep_pro)
cell_bw = tf.contrib.rnn.BasicLSTMCell(pm.hidden_dim)
Cell_bw = tf.contrib.rnn.DropoutWrapper(cell_bw, self.keep_pro)
with tf.device('/cpu:0'), tf.name_scope('embedding'):
self.embedding = tf.get_variable('embedding', shape=[pm.vocab_size, pm.embedding_dim],
initializer=tf.constant_initializer(pm.pre_trianing))
self.embedding_input = tf.nn.embedding_lookup(self.embedding, self.input_x)
with tf.name_scope('biRNN'):
output, _ = tf.nn.bidirectional_dynamic_rnn(cell_fw=Cell_fw, cell_bw=Cell_bw, inputs=self.embedding_input,
sequence_length=self.seq_length, dtype=tf.float32)
output = tf.concat(output, 2) #[batch_size, seq_length, 2*hidden_dim]
接下来,就是Attention层。我们设置三个变量,分别是w,b,u,下标分别是小写的w,公式2是一个softmax过程,公式3是线性加权求和。在实际模型中,是需要对参数进行很多变型的,我们结合代码看一下。
with tf.name_scope('attention'):
u_list = []
seq_size = output.shape[1].value
hidden_size = output.shape[2].value #[2*hidden_dim]
attention_w = tf.Variable(tf.truncated_normal([hidden_size, pm.attention_size], stddev=0.1), name='attention_w')
attention_u = tf.Variable(tf.truncated_normal([pm.attention_size, 1], stddev=0.1), name='attention_u')
attention_b = tf.Variable(tf.constant(0.1, shape=[pm.attention_size]), name='attention_b')
for t in range(seq_size):
#u_t:[batch,attention]
u_t = tf.tanh(tf.matmul(output[:, t, :], attention_w) + tf.reshape(attention_b, [1, -1]))
u = tf.matmul(u_t, attention_u)
u_list.append(u)
logit = tf.concat(u_list, axis=1)
#[batch:seq_size]
weights = tf.nn.softmax(logit, name='attention_weights')
#weight:[batch:seq_size]
out_final = tf.reduce_sum(output * tf.reshape(weights, [-1, seq_size, 1]), 1)
#out_final:[batch,hidden_size]
为了给每个词赋予权重,那么权重的shape应该是[batch,seq_length]。
为了得到每个词的权重,在程序中,对句子序列做了一个for循环,一个循环,得到的是同一个batch中不同序列同一步词的语义。
例如:batch=64,t=1,也就是64个句子,每个句子第一个词的语义。所以每一步取出的output形状是[batch,hidden_size]。
那么u_t输出为[batch,attention];
下一步,u输出得到的结果是[batch,1];
u_list是将seq_length个u的结果存入列表中;
将列表按照第一维进行拼接,得到结果logit的形状[batch,seq_length]--以上是公式一的步骤;
经过softmax,得到weights,shape依旧为[batch,seq_length]--公式二步骤;
将weigths经过变形,[bacth,seq_length,1],然后使用点乘“*”,表示对应数值相乘;然后将第一维的值进行相加,也就是seq_length这一维度。这个步骤的意思:对于每一个句子,该句词汇与对应权重相乘,然后得到的和再相加,最后输出的形状[batch,hidden_size]。
模型的最后,是全连+softmax层。这一步相对来说比较简单。
with tf.name_scope('dropout'):
self.out_drop = tf.nn.dropout(out_final, keep_prob=self.keep_pro)
with tf.name_scope('output'):
w = tf.Variable(tf.truncated_normal([hidden_size, pm.num_classes], stddev=0.1), name='w')
b = tf.Variable(tf.zeros([pm.num_classes]), name='b')
self.logits = tf.matmul(self.out_drop, w) + b
self.predict = tf.argmax(tf.nn.softmax(self.logits), 1, name='predict')
3 模型训练与测试
训练时,每迭代一次,将训练得到的结果,保存到checkpoints;
loss,accuracy的情况,保留到tensorboard中;
每100个batch,输出此时的训练结果与测试结果。
for epoch in range(pm.num_epochs):
print('Epoch:', epoch+1)
num_batchs = int((len(x_train) - 1) / pm.batch_size) + 1
batch_train = batch_iter(x_train, y_train, batch_size=pm.batch_size)
for x_batch, y_batch in batch_train:
seq_len = sequence(x_batch)
feed_dict = model.feed_data(x_batch, y_batch, seq_len, pm.keep_prob)
_, global_step, _summary, train_loss, train_accuracy = session.run([model.optimizer, model.global_step, merged_summary,
model.loss, model.accuracy],feed_dict=feed_dict)
if global_step % 100 == 0:
test_loss, test_accuracy = model.evaluate(session, x_test, y_test)
print('global_step:', global_step, 'train_loss:', train_loss, 'train_accuracy:', train_accuracy,
'test_loss:', test_loss, 'test_accuracy:', test_accuracy)
if global_step % num_batchs == 0:
print('Saving Model...')
saver.save(session, save_path, global_step=global_step)
训练与测试的情况如图所示,训练结果表明,模型收敛情况良好,准确度也较为理想。4 模型验证
验证集数据量是500*10,使用最后保存的模型对验证集进行预测,并计算准确率。从验证的情况来看,准确率达到了96.5%,从预测的前10项情况来看,模型较为理想。
5 总结
相对与之前的两层LSTM模型对文本进行分类,本文采用的双向LSTM+Attention模型。都进行了3次迭代,从结果上看,两层LSTM模型,准确率略高一点;但本文并没有用两层LSTM+Attention模型,所以对于这两次的结果不好做比较,有兴趣的可以下载github代码,把代码改成两层的结构。但从训练时间上来说,RNN网络越深,时间耗费的越久。
加上之前,共介绍了4种文本分类的深度学习模型,基本可以满足一般的情况,这几种模型不仅可以用来进行文本分类,对于其他分类情况,稍微变动下,也是适应的,比如情感分类。
对于深度学习分类的介绍,到此为止。以后会介绍深度学习、传统机器学习在中文分词,词性标注等应用的模型。
参考
https://blog.csdn.net/thriving_fcl/article/details/73381217
https://zhuanlan.zhihu.com/p/46313756