深度学习--RNN+Attention 文本分类

之前介绍过RNN的分类,本文介绍一下使用预训练词向量进行RNN+Attention的分类模型。

RNN-Attention文本分类

本文使用双向LSTM加上Attention,模型结构如图所示。从下往上看,w这一层表示一个时间序列(在文本分类指一个句子的长度,seq_length),里面的元素是一个个词汇,我们将词汇经过LSTM网络,使不具备重要性,也就是与分类无关的词语的语义给过滤掉或者保留较小的数值,从而留下较为重要的语义。使用双向LSTM,不仅要考虑句子的正向序列,还有反向序列,获得更完整的语义,将获得的结果经过拼接然后再输送到Attention层。Attention简单来说,就是赋予权重,给不同的词汇根据重要性赋值。最后将权重与LSTM层的结果进行线性求和,所得结果输出到全连接层,经过softmax得到分类结果。
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层。
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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容