https://github.com/rockingdingo/deepnlp/tree/master/deepnlp/textsum
简介
这篇文章中我们将基于Tensorflow的Seq2Seq+Attention模型,介绍如何训练一个中文的自动生成新闻标题的模型。自动总结(Automatic Summarization)类型的模型一直是研究热点。 直接抽出重要的句子的抽取式方法较为简单,有如textrank之类的算法,而生成式(重新生成新句子)较为复杂,效果也不尽如人意。目前比较流行的Seq2Seq模型,由 Sutskever等人提出,基于一个Encoder-Decoder的结构将source句子先Encode成一个固定维度d的向量,然后通过Decoder部分一个字符一个字符生成Target句子。添加入了Attention注意力分配机制后,使得Decoder在生成新的Target Sequence时,能得到之前Encoder编码阶段每个字符的隐藏层的信息向量Hidden State,使得生成新序列的准确度提高。
数据准备和预处理
我们选择公开的“搜狐新闻数据(SogouCS)”的语料,包含2012年6月—7月期间的新闻数据,超过1M的语料数据,包含新闻标题和正文的信息。数据集可以从搜狗lab下载。 http://www.sogou.com/labs/resource/cs.php
数据的预处理阶段极为重要,因为在Encoder编码阶段处理那些信息,直接影响到整个模型的效果。我们主要对下列信息进行替换和处理:
- 特殊字符:去除特殊字符,如:“「,」,¥,…”;
- 括号内的内容:如表情符,【嘻嘻】,【哈哈】
- 日期:替换日期标签为TAG_DATE,如:***年*月*日,****年*月,等等
- 超链接URL:替换为标签TAG_URL;
- 删除全角的英文:替换为标签TAG_NAME_EN;
- 替换数字:TAG_NUMBER;
在对文本进行了预处理后,准备训练语料: 我们的Source序列,是新闻的正文,待预测的Target序列是新闻的标题。 我们截取正文的分词个数到MAX_LENGTH_ENC=120个词,是为了训练的效果正文部分不宜过长。标题部分截取到MIN_LENGTH_ENC = 30,即生成标题不超过30个词。
在data_util.py类中,生成训练数据时做了下列事情:
- create_vocabulary()方法创建词典;
- data_to_token_ids()方法把训练数据(content-train.txt)转化为对应的词ID的表示;
两个文件的格式:
- # 数据1 正文 content-train.txt
- 世间 本 没有 歧视 TAG_NAME_EN 歧视 源自于 人 的 内心 活动 TAG_NAME_EN “ 以爱 之 名 ” TAG_DATE 中国 艾滋病 反歧视 主题 创意 大赛 开幕 TAG_NAME_EN 让 “ 爱 ” 在 高校 流动 。 TAG_NAME_EN 详细 TAG_NAME_EN
- 济慈 之 家 小朋友 感受 爱心 椅子 TAG_DATE TAG_NAME_EN 思源 焦点 公益 基金 向 盲童 孤儿院 “ 济慈 之 家 ” 提供 了 首 笔 物资 捐赠 。 这 笔 价值 近 万 元 的 物资 为 曲 美 家具 向 思源 · 焦点 公益 基金 提供 的 儿童 休闲椅 TAG_NAME_EN 将 用于 济慈 之 家 的 小孩子们 日常 使用 。
- ...
- # 数据2 标题 title-train.txt
- 艾滋病 反歧视 创意 大赛
- 思源 焦点 公益 基金 联手 曲 美 家具 共 献 爱心
- ...
训练模型
- #代码1
- python headline.py
预测
运行predict.py, 交互地输入分好词的文本, 得到textsum的结果
- #代码2-1
- python predict.py
- # 输入和输出
- #> 中央 气象台 TAG_DATE TAG_NUMBER 时 继续 发布 暴雨 蓝色 预警 TAG_NAME_EN 预计 TAG_DATE TAG_NUMBER 时至 TAG_DATE TAG_NUMBER 时 TAG_NAME_EN 内蒙古 东北部 、 山西 中 北部 、 河北 中部 和 东北部 、 京津 地区 、 辽宁 西南部 、 吉林 中部 、 黑龙江 中部 偏南 等 地 的 部分 地区 有 大雨 或 暴雨 。
- #current bucket id0
- #中央 气象台 发布 暴雨 蓝色 预警
- #>
我们尝试输入下列分好词的新闻正文,一些挑选过的自动生成的中文标题如下:
| ID | 新闻正文 | 新闻标题 | textsum自动生成标题 |
|---|---|---|---|
| 469 | 中央 气象台 TAG_DATE TAG_NUMBER 时 继续 发布 暴雨 蓝色 预警 TAG_NAME_EN 预计 TAG_DATE TAG_NUMBER 时至 TAG_DATE TAG_NUMBER 时 TAG_NAME_EN 内蒙古 东北部 、 山西 中 北部 、 河北 中部 和 东北部 、 京津 地区 、 辽宁 西南部 、 吉林 中部 、 黑龙江 中部 偏南 等 地 的 部分 地区 有 大雨 或 暴雨 。 | 中央 气象台 继续 发布 暴雨 预警 北京 等 地 有 大雨 | 中央 气象台 发布 暴雨 蓝色 预警 |
| 552 | 美国 科罗拉多州 山林 大火 持续 肆虐 TAG_NAME_EN 当地 时间 TAG_DATE 横扫 州 内 第二 大 城市 科罗拉多斯 普林斯 一 处 居民区 TAG_NAME_EN 迫使 超过 TAG_NUMBER TAG_NAME_EN TAG_NUMBER 万 人 紧急 撤离 。 美国 正 值 山火 多发 季 TAG_NAME_EN 现有 TAG_NUMBER 场 山火 处于 活跃 状态 。 | 山火 横扫 美 西部 TAG_NUMBER 州 奥 巴马 将 赴 灾区 视察 联邦 调查局 介入 查 原因 | 美国 科罗拉多州 山火 致 TAG_NUMBER 人 死亡 |
| 917 | 埃及 选举 委员会 昨天 宣布 TAG_NAME_EN 穆斯林 兄弟会 下属 自由 与 正义党 主席 穆尔西 获得 TAG_NUMBER TAG_NAME_EN TAG_NUMBER TAG_NAME_EN 的 选票 TAG_NAME_EN 以 微弱 优势 击败 前 总理 沙 菲克 赢得 选举 TAG_NAME_EN 成为 新任 埃及 总统 。 媒体 称 其 理念 获 下层 民众 支持 。 | 埃及 大选 昨晚 结束 新 总统 穆尔西 被 认为 具有 改革 魄力 | 埃及 总统 选举 结果 |
| 920 | 上 周 TAG_NAME_EN 广东 华兴 银行 在 央行 宣布 降息 和 调整 存贷款 波幅 的 第二 天 TAG_NAME_EN 立即 宣布 首 套 房贷 利率 最低 执行 七 折 优惠 。 一 石 激起 千层 浪 TAG_NAME_EN 随之 而 起 的 “ 房贷 七 折 利率 重 出 江湖 ” 和 “ 房地产 调控 松绑 ” 的 谣言 四起 。 | 房贷 “ 七 折 利率 ” 真相 调查 TAG_NAME_EN 符合 条件 的 几乎 为零 | 银监会 否认 房贷 房贷 利率 |
预测并计算ROUGE评估
运行predict.py, 同时调用eval.py 中的 evaluate(X, Y, method = "rouge_n", n = 2) 方法计算ROUGE分
- #代码2-2 linux shell
- folder_path=`pwd`
- input_dir=${folder_path}/news/test/content-test.txt
- reference_dir=${folder_path}/news/test/title-test.txt
- summary_dir=${folder_path}/news/test/summary.txt
- python predict.py $input_dir $reference_dir $summary_dir
- # 输出:
- # 中央 气象台 发布 暴雨 蓝色 预警
- # Evaludated Rouge-2 score is 0.1818
- # ...
下面我们将具体介绍tensorflow的seq2seq模型如何实现,首先先简单回顾模型的结构。
Seq2Seq+Attention模型回顾
Seq2Seq模型有效地建模了基于输入序列,预测未知输出序列的问题。模型有两部分构成,一个编码阶段的”Encoder”和一个解码阶段的”Decoder”。如下图的简单结构所示,Encoder的RNN每次输入一个字符代表的embedding向量,如依次输入A,B,C, 及终止标志,将输入序列编码成一个固定长度的向量;之后解码阶段的RNN会一个一个字符地解码, 如预测为X, 之后在训练阶段会强制将前一步解码的输出作为下一步解码的输入,如X会作为下一步预测Y时的输入。
定义输入序列 ,由Tx个固定长度为d的向量构成;
输出序列为
,由Ty个固定长度为d的向量构成;
定义输入序Encoder阶段的RNN隐藏层为 hj, Decoder阶段的RNN隐藏层为 Si
Attention注意力分配机制
LSTM模型虽然具有记忆性,但是当Encoder阶段输入序列过长时,解码阶段的LSTM也无法很好地针对最早的输入序列解码。基于此,Attention注意力分配的机制被提出,就是为了解决这个问题。在Decoder阶段每一步解码,都能够有一个输入,对输入序列所有隐藏层的信息h_1,h_2,…h_Tx进行加权求和。打个比方就是每次在预测下一个词时都会把所有输入序列的隐藏层信息都看一遍,决定预测当前词时和输入序列的那些词最相关。
Attention机制代表了在解码Decoder阶段,每次都会输入一个Context上下文的向量Ci, 隐藏层的新状态Si根据上一步的状态Si-1, Yi, Ci 三者的一个非线性函数得出。
Context向量在解码的每一步都会重新计算,根据一个MLP模型计算出输出序列i对每个输入序列j的隐含层的对应权重aij,并对所有隐含层加权平均。文章中说的Alignment Model就是代表这种把输入序列位置j和输出序列位置i建立关系的模型。
aij 即可以理解为Decoder解码输出序列的第i步,对输入序列第j步分配的注意力权重。
eij为一个简单的MLP模型**的输出;aij的计算是对eij做softmax归一化后的结果。
Soft Attention和Hard Attention区别
Soft Attention通常是指以上我们描述的这种全连接(如MLP计算Attention 权重),对每一层都可以计算梯度和后向传播的模型;不同于Soft attention那样每一步都对输入序列的所有隐藏层hj(j=1….Tx) 计算权重再加权平均的方法,Hard Attention是一种随机过程,每次以一定概率抽样,以一定概率选择某一个隐藏层 hj*,在估计梯度时也采用蒙特卡罗抽样Monte Carlo sampling的方法。
模型实现
我们对Tensorflow基本教程里的translate英语法语翻译例子里的seq2seq_model.py类稍加修改,就能够符合我们textsum例子使用,另外我们还会分析针对英文的textsum教程中构建双向Bi-LSTM的Encoder-Decoder的例子。
1.Seq2Seq模型文件: seq2Seq_model.py
单向LSTM的Encoder-Decoder结构
教程中的例子很长,但是将实例代码分解来看不是那么复杂,下面将分三段来介绍官方tutorial里的如何构建seq2seq模型。
定义基本单元: 多层LSTM cell
- #代码3-1
- # Create the internal multi-layer cell for our RNN.
- single_cell = tf.nn.rnn_cell.GRUCell(size) # default use GRU
- if use_lstm:
- single_cell = tf.nn.rnn_cell.BasicLSTMCell(size, state_is_tuple=True)
- cell = single_cell
- if num_layers > 1:
- cell = tf.nn.rnn_cell.MultiRNNCell([single_cell] * num_layers, state_is_tuple=True)
定义前向过程的 seq2seq_f 函数,利用tf.nn.seq2seq.embedding_attention_seq2seq 返回output
- # 代码3-2
- # The seq2seq function: we use embedding for the input and attention.
- def seq2seq_f(encoder_inputs, decoder_inputs, do_decode):
- return tf.nn.seq2seq.embedding_attention_seq2seq(
- encoder_inputs,
- decoder_inputs,
- cell,
- num_encoder_symbols=source_vocab_size,
- num_decoder_symbols=target_vocab_size,
- embedding_size=size,
- output_projection=output_projection,
- feed_previous=do_decode,
- dtype=tf.float32)
桶Bucket的机制: 应用Bucket机制,核心的思想是把输入序列的句子按照长度的相似程度分到不同的固定长度的Bucket里面,长度不够的都添加PAD字符。之所以有Bucket的原因是工程效率:”RNN在数学上是可以处理任意长度的数据的。我们在TensorFlow中使用bucket的原因主要是为了工程实现的效率” (摘自知乎JQY的回答https://www.zhihu.com/question/42057513)
- # 代码3-3
- # Training outputs and losses.
- if forward_only:
- self.outputs, self.losses = tf.nn.seq2seq.model_with_buckets(
- self.encoder_inputs, self.decoder_inputs, targets,
- self.target_weights, buckets, lambda x, y: seq2seq_f(x, y, True),
- softmax_loss_function=softmax_loss_function)
- # If we use output projection, we need to project outputs for decoding.
- if output_projection is not None:
- for b in xrange(len(buckets)):
- self.outputs[b] = [
- tf.matmul(output, output_projection[0]) + output_projection[1]
- for output in self.outputs[b]
- ]
- else:
- self.outputs, self.losses = tf.nn.seq2seq.model_with_buckets(
- self.encoder_inputs, self.decoder_inputs, targets,
- self.target_weights, buckets, lambda x, y: seq2seq_f(x, y, False),
- softmax_loss_function=softmax_loss_function)
tf.nn.seq2seq.model_with_buckets()结果返回output序列;’ buckets = [(120, 30), (200, 35), (300, 40), (400, 40), (500, 40)]
https://github.com/tensorflow/tensorflow/blob/64edd34ce69b4a8033af5d217cb8894105297d8a/tensorflow/contrib/legacy_seq2seq/python/ops/seq2seq.py双向LSTM的Encoder-Decoder结构
Encoder阶段自己定义了Bi-LSTM结构
- # 代码4-1
- # URL: https://github.com/tensorflow/models/tree/master/textsum
- # Encoder: Multi-Layer LSTM, Output: encoder_outputs
- for layer_i in xrange(hps.enc_layers):
- with tf.variable_scope('encoder%d'%layer_i), tf.device(
- self._next_device()):
- cell_fw = tf.nn.rnn_cell.LSTMCell(
- hps.num_hidden,
- initializer=tf.random_uniform_initializer(-0.1, 0.1, seed=123),
- state_is_tuple=False)
- cell_bw = tf.nn.rnn_cell.LSTMCell(
- hps.num_hidden,
- initializer=tf.random_uniform_initializer(-0.1, 0.1, seed=113),
- state_is_tuple=False)
- (emb_encoder_inputs, fw_state, _) = tf.nn.bidirectional_rnn(
- cell_fw, cell_bw, emb_encoder_inputs, dtype=tf.float32,
- sequence_length=article_lens)
- encoder_outputs = emb_encoder_inputs
- with tf.variable_scope('output_projection'):
- w = tf.get_variable(
- 'w', [hps.num_hidden, vsize], dtype=tf.float32,
- initializer=tf.truncated_normal_initializer(stddev=1e-4))
- w_t = tf.transpose(w)
- v = tf.get_variable(
- 'v', [vsize], dtype=tf.float32,
- initializer=tf.truncated_normal_initializer(stddev=1e-4))
- Decoder阶段,利用内置的tf.nn.seq2seq.attention_decoder()函数返回output
- # 代码4-2
- with tf.variable_scope('decoder'), tf.device(self._next_device()):
- # When decoding, use model output from the previous step
- # for the next step.
- loop_function = None
- if hps.mode == 'decode':
- loop_function = _extract_argmax_and_embed(
- embedding, (w, v), update_embedding=False)
- cell = tf.nn.rnn_cell.LSTMCell(
- hps.num_hidden,
- initializer=tf.random_uniform_initializer(-0.1, 0.1, seed=113),
- state_is_tuple=False)
- encoder_outputs = [tf.reshape(x, [hps.batch_size, 1, 2*hps.num_hidden])
- for x in encoder_outputs]
- self._enc_top_states = tf.concat(1, encoder_outputs)
- self._dec_in_state = fw_state
- # During decoding, follow up _dec_in_state are fed from beam_search.
- # dec_out_state are stored by beam_search for next step feeding.
- initial_state_attention = (hps.mode == 'decode')
- decoder_outputs, self._dec_out_state = tf.nn.seq2seq.attention_decoder(
- emb_decoder_inputs, self._dec_in_state, self._enc_top_states,
- cell, num_heads=1, loop_function=loop_function,
- initial_state_attention=initial_state_attention)
Attention注意力矩阵的可视化
Attention 注意力分配机制的权重矩阵[Aij]可以反映在Decoder阶段第i个输出字符对Encoder阶段的第j个字符的注意力分配的权重aij。 我们可以通过绘制Heatmap来可视化seq2seq模型中Decoder的Y对Encoder的X每个字符的权重。
获取attention_mask的值
attention_mask 即为我们感兴趣的注意力权重分配的tensor,我们首先来看tensorflow的源码seq2seq.py这个ops的实现, 容易发现,计算attention_mask 变量a的代码出现在 attention_decoder()函数内的attention()函数体下, a = nn_ops.softmax(s) 这句。 我们把该变量添加到return语句中的返回值,同时也修改所有调用了attention_decoder()的上层的函数,为了最终能够在主函数中将attn_mask这个变量抽取出。 具体需要修改的脚本参考textsum项目下的seq2seq_attn.py这个文件。 之后我们在主函数中利用attn_out = session.run(self.attn_masks[bucket_id], input_feed) ,对变量进行session.run() 就可以获得当前这个样本的attention矩阵的值。
- # 代码5-1
- # URL: https://github.com/rockingdingo/deepnlp/blob/master/deepnlp/textsum/seq2seq_attn.py
- def attention_decoder():
- ## some code
- def attention(query):
- """Put attention masks on hidden using hidden_features and query."""
- ds = []
- if nest.is_sequence(query):
- query_list = nest.flatten(query)
- for q in query_list:
- ndims = q.get_shape().ndims
- if ndims:
- assert ndims == 2
- query = array_ops.concat_v2(query_list, 1)
- for a in xrange(num_heads):
- with variable_scope.variable_scope("Attention_%d" % a):
- y = linear(query, attention_vec_size, True)
- y = array_ops.reshape(y, [-1, 1, 1, attention_vec_size])
- # Attention mask is a softmax of v^T * tanh(...).
- s = math_ops.reduce_sum(v[a] * math_ops.tanh(hidden_features[a] + y),
- [2, 3])
- # Tensor a 即为我们需要抽取的attention_mask
- a = nn_ops.softmax(s)
- d = math_ops.reduce_sum(
- array_ops.reshape(a, [-1, attn_length, 1, 1]) * hidden, [1, 2])
- ds.append(array_ops.reshape(d, [-1, attn_size]))
- return ds, a
利用matplotlib可视化
我们利用matplotlib包中绘制heatmap的函数,可以简单地将上一步抽取出的attn_matrix可视化。在eval.py模块中我们整合了一个eval.plot_attention(data, X_label=None, Y_label=None) 函数来简单绘制attention权重矩阵。 运行 predict_attn.py 脚本,输入分好词的待分析新闻文本,然后自动生成的jpg图片就保存在./img目录下。
- # 代码5-2
- # 输入文本, 查看Attention的heatmap:
- # > 中央 气象台 TAG_DATE TAG_NUMBER 时 继续 发布 暴雨 蓝色 预警 TAG_NAME_EN 预计 TAG_DATE TAG_NUMBER 时至 TAG_DATE TAG_NUMBER 时 TAG_NAME_EN 内蒙古 东北部 、 山西 中 北部 、 河北 中部 和 东北部 、 京津 地区 、 辽宁 西南部 、 吉林 中部 、 黑龙江 中部 偏南 等 地 的 部分 地区 有 大雨 或 暴雨 。
- python predict_attn.py
可视化部分代码参考项目中的 predict_attn.py seq2seq_attn.py seq2seq_model_attn.py 这三个文件。
预测阶段Decode策略: 贪心算法和Beam Search
贪心算法 Beam Search拓展阅读
- deepnlp Python包
-
http://www.deepnlp.org/
- Github源码textsum
- https://github.com/rockingdingo/deepnlp/tree/master/deepnlp/textsum
- Tensorflow seq2seq.py
-
https://github.com/tensorflow/tensorflow/blob/64edd34ce69b4a8033af5d217cb8894105297d8a/tensorflow/contrib/legacy_seq2seq/python/ops/seq2seq.py
- Tensorflow Bi-LSTM textsum examples
- https://github.com/tensorflow/models/tree/master/textsum
- ATTENTION MECHANISM
- https://blog.heuritech.com/2016/01/20/attention-mechanism/
对tensorflow 中的attention encoder-decoder模型调试分析
1 #-*-coding:utf8-*- 2 3 __author = "buyizhiyou" 4 __date = "2017-11-21" 5 6 7 import random, time, os, decoder 8 from PIL import Image 9 import numpy as np 10 import tensorflow as tf 11 import pdb 12 import decoder 13 import random 14 15 ''' 16 在汉字ocr项目中,利用基于attention的encoder-decoder(seq2seq)模型进行端对端的训练 17 单步调试,追踪tensorflow 对 attention-seq2seq模型的实现方式 18 python 中seq2seq.py的接口:tf.nn.seq2seq.embedding_attention_seq2seq() 19 把用到的部分取出来单独调试 20 ''' 21 22 batch_size = 16 23 dec_seq_len = 8#图片对应的汉字数8 24 enc_lstm_dim = 256 25 dec_lstm_dim = 512 26 vocab_size = 1002 27 embedding_size = 100 28 lr = 0.01 29 global_step = tf.Variable(0) 30 31 cnn = tf.truncated_normal([16,10,35,64],mean=0,stddev=1.0,dtype=tf.float32)#模拟初始化一个cnn提取特征后的图片 32 #(batch_size,height,width,channels)(16, 10, 35, 64) 33 true_labels = [] 34 #随即生成batch中图片对应的序列,无需embedding 35 for i in range(batch_size): 36 seq_label = [] 37 for j in range(dec_seq_len): 38 seq_label.append(random.randint(0,1000)) 39 true_labels.append(seq_label) 40 41 42 #编码 43 def encoder(inp):#inp:shape=(16, 35, 64) 44 #pdb.set_trace() 45 enc_init_shape = [batch_size, enc_lstm_dim]#[16,256] 46 with tf.variable_scope('encoder_rnn'): 47 with tf.variable_scope('forward'): 48 lstm_cell_fw = tf.nn.rnn_cell.LSTMCell(enc_lstm_dim) 49 init_fw = tf.nn.rnn_cell.LSTMStateTuple(\ 50 tf.get_variable("enc_fw_c", enc_init_shape),\ 51 tf.get_variable("enc_fw_h", enc_init_shape) 52 ) 53 with tf.variable_scope('backward'): 54 lstm_cell_bw = tf.nn.rnn_cell.LSTMCell(enc_lstm_dim) 55 init_bw = tf.nn.rnn_cell.LSTMStateTuple(\ 56 tf.get_variable("enc_bw_c", enc_init_shape),\ 57 tf.get_variable("enc_bw_h", enc_init_shape) 58 ) 59 output, _ = tf.nn.bidirectional_dynamic_rnn(lstm_cell_fw, \ 60 lstm_cell_bw, \ 61 inp, \ 62 sequence_length = tf.fill([batch_size],\ 63 tf.shape(inp)[1]), #(35,35,35...,35,35,35) 64 initial_state_fw = init_fw, \ 65 initial_state_bw = init_bw \ 66 )#shape=(16, 35, 256) 67 return tf.concat(2,output)##shape=(16, 35, 512) 68 69 encoder = tf.make_template('fun', encoder) 70 # shape is (batch size, rows, columns, features) 71 # swap axes so rows are first. map splits tensor on first axis, so encoder will be applied to tensors 72 # of shape (batch_size,time_steps,feat_size) 73 rows_first = tf.transpose(cnn,[1,0,2,3])#shape=(10, 16, 35, 64) 74 res = tf.map_fn(encoder, rows_first, dtype=tf.float32)#shape=(10, 16, 35, 512) 75 encoder_output = tf.transpose(res,[1,0,2,3])#shape=(16, 10, 35, 512) 76 77 dec_lstm_cell = tf.nn.rnn_cell.LSTMCell(dec_lstm_dim) 78 dec_init_shape = [batch_size, dec_lstm_dim] 79 dec_init_state = tf.nn.rnn_cell.LSTMStateTuple( tf.truncated_normal(dec_init_shape),\ 80 tf.truncated_normal(dec_init_shape) ) 81 82 init_words = np.zeros([batch_size,1,vocab_size])#(16, 1, 1002) 83 84 85 #pdb.set_trace() 86 (output,state) = decoder.embedding_attention_decoder(dec_init_state,#[16, 512]第一个解码cell的state=[c,h] 87 tf.reshape(encoder_output,[batch_size, -1,2*enc_lstm_dim]), 88 #encoder输出reshape为 attention states作为attention模块的输入 shape=(16,350,512) 89 dec_lstm_cell,#lstm单元,作为解码层 90 vocab_size,#1002 91 dec_seq_len,#8 92 batch_size,#16 93 embedding_size,#100 94 feed_previous=True)#dec_seq_len = num_words = time_steps 95 pdb.set_trace() 96 cross_entropy = tf.reduce_mean(tf.nn.sparse_softmax_cross_entropy_with_logits(output,true_labels)) 97 learning_rate = tf.train.exponential_decay(lr, global_step, 50, 0.9) 98 train_step = tf.train.AdamOptimizer(learning_rate).minimize(cross_entropy,global_step=global_step) 99 correct_prediction = tf.equal(tf.to_int32(tf.argmax( output, 2)), true_labels) 100
decode.py
1 #-*-coding:utf8-*- 2 3 4 """ 5 截取自tensorflow seq2seq.py 文件 6 """ 7 import numpy as np 8 import tensorflow as tf 9 import pdb 10 from tensorflow.python import shape 11 from tensorflow.python.framework import dtypes 12 from tensorflow.python.framework import ops 13 from tensorflow.python.ops import array_ops 14 from tensorflow.python.ops import control_flow_ops 15 from tensorflow.python.ops import embedding_ops 16 from tensorflow.python.ops import math_ops 17 from tensorflow.python.ops import nn_ops 18 from tensorflow.python.ops import rnn 19 from tensorflow.python.ops import rnn_cell 20 from tensorflow.python.ops import variable_scope 21 from tensorflow.python.util import nest 22 23 linear = rnn_cell._linear # pylint: disable=protected-access 24 25 def attention_decoder(initial_state,#(16, 512) 26 attention_states,#shape=(16, 350, 512) 27 cell, 28 vocab_size,#1002 29 time_steps,#num_words,8 30 batch_size,#16 31 output_size=None,#512 32 loop_function=None, 33 dtype=None, 34 scope=None): 35 pdb.set_trace() 36 if attention_states.get_shape()[2].value is None:#tf 张量 get_shape()方法获取size 37 raise ValueError("Shape[2] of attention_states must be known: %s" 38 % attention_states.get_shape()) 39 if output_size is None: 40 output_size = cell.output_size#512 41 42 with variable_scope.variable_scope(scope or "attention_decoder", dtype=dtype) as scope: 43 dtype = scope.dtype 44 45 attn_length = attention_states.get_shape()[1].value #350 46 if attn_length is None: 47 attn_length = shape(attention_states)[1] 48 attn_size = attention_states.get_shape()[2].value#512 49 50 # To calculate W1 * h_t we use a 1-by-1 convolution, need to reshape before. 51 hidden = array_ops.reshape(attention_states, [-1, attn_length, 1, attn_size])#shape=(16, 350, 1, 512) 52 attention_vec_size = attn_size # Size of query vectors for attention. 512 53 k = variable_scope.get_variable("AttnW",[1, 1, attn_size, attention_vec_size])#shape=(1,1,512,512) 54 hidden_features = nn_ops.conv2d(hidden, k, [1, 1, 1, 1], "SAME")#(16 ,350, 1, 512) w_1*h_j 55 v = variable_scope.get_variable("AttnV", [attention_vec_size]) 56 57 58 def attention(query): 59 #LSTMStateTuple(c= shape=(16, 512) dtype=float32>, h=< shape=16, 512) dtype=float32>) 60 """Put attention masks on hidden using hidden_features and query.""" 61 if nest.is_sequence(query): # If the query is a tuple, flatten it. 62 query_list = nest.flatten(query) #[c,h],第一个随即初始化,以后调用之前计算的 63 for q in query_list: # Check that ndims == 2 if specified. 64 ndims = q.get_shape().ndims 65 if ndims: 66 assert ndims == 2 67 query = array_ops.concat(1, query_list)# shape=(16, 1024) 68 with variable_scope.variable_scope("Attention_0"): 69 y = linear(query, attention_vec_size, True)# shape=(16, 512) w_2*s_t 70 y = array_ops.reshape(y, [-1, 1, 1, attention_vec_size]) # shape=(16, 1, 1, 512) 71 s = math_ops.reduce_sum( 72 v * math_ops.tanh(hidden_features + y), [2, 3]) #!!!!!!!!!!!公式(3)shape=(16, 350) 73 a = nn_ops.softmax(s)# 公式(2)shape=(16, 350) 74 # Now calculate the attention-weighted vector d. 75 d = math_ops.reduce_sum( 76 array_ops.reshape(a, [-1, attn_length, 1, 1]) * hidden,#公式(1) 77 [1, 2])#shape=(16, 512) 78 ds = array_ops.reshape(d, [-1, attn_size])#shape=(16, 512) #!!!!!!!!!!!!以上是attention model中三个关键公式的实现 79 return ds 80 #pdb.set_trace() 81 prev = array_ops.zeros([batch_size,output_size])# shape=(16, 512) cell层第一个cell启动计算所需输入, 82 #随机初始化,以后的cell调用之前的计算结果 83 batch_attn_size = array_ops.pack([batch_size, attn_size]) #(2,?) 84 attn = array_ops.zeros(batch_attn_size, dtype=dtype)#shape=(16, 512) 85 attn.set_shape([None, attn_size])#(16,512) 86 87 def cond(time_step, prev_o_t, prev_softmax_input, state_c, state_h, outputs2): 88 return time_step < time_steps 89 90 def body(time_step, prev_o_t, prev_softmax_input, state_c, state_h, outputs2):#prev_o_t=prev:shape=(16,512) 91 #outputs:shape=(16, ?, 1002) prev_softmax_input=init_word:shape=(16, 1002) 92 state = tf.nn.rnn_cell.LSTMStateTuple(state_c,state_h)#第一次随机初始状态,之后调用之前的 93 pdb.set_trace() 94 with variable_scope.variable_scope("loop_function", reuse=True): 95 inp = loop_function(prev_softmax_input, time_step)#shape=(16,100) inp用来做什么 作为每个cell单元从下而 96 #来的输入??而prev_o_t则为从左而来的输入??而且Inp和上一个cell单元的softmax_input(最终进softmax之前的cell输出)有关(prev_softmax_input) 97 98 input_size = inp.get_shape().with_rank(2)[1]#100 99 if input_size.value is None: 100 raise ValueError("Could not infer input size from input: %s" % inp.name) 101 x = tf.concat(1,[inp,prev_o_t])#shape=(16, 612) 这个地方inp ,prev_o_t = loop_function(softmax_output),output 102 # Run the RNN. 103 cell_output, state = cell(x, state)#decoder层512个lstm单元 cell_output:shape=(16, 512) state:shape=(16, 512) 104 # Run the attention mechanism. 105 attn = attention(state)#shape=(16, 512) attenion模块的输出,C_i 106 107 with variable_scope.variable_scope("AttnOutputProjection"): 108 output = math_ops.tanh(linear([cell_output, attn], output_size, False))#shape=(16, 512) y_i = f(C_i,S_i) 109 with variable_scope.variable_scope("FinalSoftmax"): 110 softmax_input = linear(output,vocab_size,False)#shape=(16, 1002) #decoder层后加一层softmax??作为softmax_input 111 112 new_outputs = tf.concat(1, [outputs2,tf.expand_dims(softmax_input,1)])#shape=(16, ?, 1002)[,...y_t-1,y_t,...] 113 return (time_step + tf.constant(1, dtype=tf.int32),\ 114 output, softmax_input, state.c, state.h, new_outputs)#既是输出,又是下一轮的输入 115 116 time_step = tf.constant(0, dtype=tf.int32) 117 shape_invariants = [time_step.get_shape(),\ 118 prev.get_shape(),\ 119 tf.TensorShape([batch_size, vocab_size]),\ 120 tf.TensorShape([batch_size,512]),\ 121 tf.TensorShape([batch_size,512]),\ 122 tf.TensorShape([batch_size, None, vocab_size])] 123 124 # START keyword is 0 125 init_word = np.zeros([batch_size, vocab_size])#shape=(16,1002) 126 127 loop_vars = [time_step,\ 128 prev,\ 129 tf.constant(init_word, dtype=tf.float32),\ 130 initial_state.c,initial_state.h,\ 131 tf.zeros([batch_size,1,vocab_size])] 136 137 outputs = tf.while_loop(cond, body, loop_vars, shape_invariants)##shape=(16, ?, 1002) 138 ''' 139 loop_vars = [...] 140 while cond(*loop_vars): 141 loop_vars = body(*loop_vars) 142 ''' 143 144 return outputs[-1][:,1:], tf.nn.rnn_cell.LSTMStateTuple(outputs[-3],outputs[-2]) 145 146 def embedding_attention_decoder(initial_state,#shape=(16, 512) 147 attention_states,# shape=(16, 350, 512) 148 cell,#定义的lstm单元 149 num_symbols,#1002 150 time_steps, 151 batch_size,#16 152 embedding_size,#100 153 output_size=None,#512 154 output_projection=None, 155 feed_previous=False,#True 156 update_embedding_for_previous=True, 157 dtype=None, 158 scope=None): 159 if output_size is None: 160 output_size = cell.output_size#512 161 if output_projection is not None: 162 proj_biases = ops.convert_to_tensor(output_projection[1], dtype=dtype) 163 proj_biases.get_shape().assert_is_compatible_with([num_symbols]) 164 165 with variable_scope.variable_scope(scope or "embedding_attention_decoder", dtype=dtype) as scope: 166 embedding = variable_scope.get_variable("embedding",[num_symbols, embedding_size]) 167 loop_function = tf.nn.seq2seq._extract_argmax_and_embed(embedding, 168 output_projection,update_embedding_for_previous) if feed_previous else None 169 #(16,1002)==>(16,100)找argmax,然后embedding 170 return attention_decoder( 171 initial_state, 172 attention_states, 173 cell, 174 num_symbols,#1002 175 time_steps,#8 176 batch_size, 177 output_size=output_size,#512 178 loop_function=loop_function)
关于embedding接口:
测试如下:
1 #-*-coding:utf8-*- 2 3 __author = "buyizhiyou" 4 __date = "2017-11-21" 5 6 import tensorflow as tf 7 import numpy as np 8 9 ''' 10 测试embedding接口 11 ''' 12 embedding = tf.Variable(np.identity(5,dtype=np.int32)) 13 inputs = tf.placeholder(dtype=tf.int32,shape=[None]) 14 input_embedding = tf.nn.embedding_lookup(embedding,inputs) 15 16 with tf.Session() as sess: 17 sess.run(tf.global_variables_initializer()) 18 print(sess.run(embedding)) 19 ''' 20 [[1 0 0 0 0] 21 [0 1 0 0 0] 22 [0 0 1 0 0] 23 [0 0 0 1 0] 24 [0 0 0 0 1]] 25 ''' 26 print(sess.run(input_embedding,feed_dict={inputs:[1,2,3,0,3,2,1]})) 27 ''' 28 [[0 1 0 0 0] 29 [0 0 1 0 0] 30 [0 0 0 1 0] 31 [1 0 0 0 0] 32 [0 0 0 1 0] 33 [0 0 1 0 0] 34 [0 1 0 0 0]] 35 '''