TensorFlow实现Seq2seq+Attention

实现Seq2seq+Attention有以下几个步骤:

  1. 定义RNNCell(封装了attention机制)
  2. 定义traininghelper
  3. 定义basicdecoder,传入1、2中定义好的cell和helper
  4. 将3中定义好的decoder传入dynamic_decode运行

1. attention机制

tf.contrib.seq2seq.LuongAttention

参数 含义
num_units encoder阶段输出向量的维度hidden_size
memory 一个batch里,encoder阶段产生的所有的特征向量,维数为[batch_size, max_time, num_units]
memory_sequence_length 记录memory中 的特征向量的长度,维数是[batch_size,],令memory中超过memory_sequence_length的值为0
probability_fn 将打分函数直接转成概率,默认的是softmax
score_mask_value 在将分数传到probability_fn函数之前的掩码值,在有Probability_fn函数的情况下才用

tf.contrib.seq2seq.AttentionWrapper

# 分为三步
# 第一步是定义attention机制
# 第二步是定义要是用的基础的RNNCell
# 第三步是使用AttentionWrapper进行封装 

#定义要使用的attention机制。
attention_mechanism=tf.contrib.seq2seq.LuongAttention(
num_units=self.rnn_size, 
memory=encoder_outputs,
memory_sequence_length=encoder_inputs_length)
# 定义decoder阶段要是用的LSTMCell,然后为其封装attention wrapper
decoder_cell = self._create_rnn_cell()
decoder_cell = tf.contrib.seq2seq.AttentionWrapper(
cell=decoder_cell, 
attention_mechanism=attention_mechanism, attention_layer_size=self.rnn_size, 
name='Attention_Wrapper')

2. helper

用来确定decoder部分的输入,训练过程应直接使用上一时刻的真实值作为下一时刻输入,预测过程中可以使用贪婪的方法选择概率最大的那个值作为下一时刻的输入

tf.contrib.seq2seq.TrainingHelper

helper = tf.contrib.seq2seq.TrainingHelper(
input=input_vectors,
sequence_length=input_lengths)
input大小为[batch_size, step, 每个时间步输入的长度],
sequence_length长度为batch_size,表示每个输入的真实长度,因为有些是padding的

GreedyEmbeddingHelper(embedding, start_tokens, end_token)

https://blog.csdn.net/tudaodiaozhale/article/details/99335220
TrainingHelper并不需要这个,是因为在训练阶段,我们给TrainingHelper的就是[batch_size, seq_len, embed_size]的输入,已经是词向量了。而在推理阶段,我们只给了一个开始符,给了我们需要的句子长度,所以我们在输出一个词的时候还需要进行embedding_lookup成词向量作为下一个时刻的输入。

3. basic_decoder

tf.contrib.seq2seq.BasicDecoder(cell, helper, initial_state, output_layer)

basic_decoder文件定义了一个基本的Decoder类实例BasicDecoder
cell:RNNCell,也就是decode阶段的神经元,一般是封装了AttentionWrapper的RNNCell
如:decoder_cell = tf.nn.rnn_cell.LSTMCell(config.hidden_size)
helper:Helper类,用于确定训练和推理阶段decoder输入的内容
initial_state:初始状态,一般是用Encoder的最后一个隐层状态,也就是标准Seq2seq的做法
output_layer:输出层,是一个tf.layers.Layer的对象

4. tf.contrib.seq2seq.dynamic_decode

final_outputs, final_state, final_sequence_lengths = tf.contrib.seq2seq.dynamic_decode(
   decoder,
   output_time_major=False,
   impute_finished=False,
   maximum_iterations=None,
   parallel_iterations=32,
   swap_memory=False,
   scope=None
   )

decoder: 一般为定义好的BasicDecoder、BeamSearchDecoder或者自己定义的decoder类对象
impute_finished: Boolean,为真时会拷贝最后一个时刻的状态并将输出置零,程序运行更稳定,使最终状态和输出具有正确的值,在反向传播时忽略最后一个完成步。但是会降低程序运行速度。
maximum_iterations: 最大解码步数,一般训练设置为decoder_inputs_length,预测时设置一个想要的最大序列长度即可。程序会在产生<eos>或者到达最大步数处停止。

final_outputs是一个namedtuple,tf.contrib.seq2seq.BasicDecoderOutput类型,里面包含两项(rnn_outputs, sample_id)
rnn_output: [batch_size, decoder_targets_length, vocab_size],保存decode每个时刻每个单词的概率,可以用来计算loss
sample_id: [batch_size], tf.int32,保存最终的编码结果。可以表示最后的答案
maximum_iterations=self.max_target_sequence_length)

mask矩阵 tf.sequence_mask

对tensor进行mask,返回True和False组成的tensor

在NLP中,一个常见的问题是输入序列长度不等,也就是说我们文本中的每句话都是长短不一的,而mask可以帮助我们处理这个问题。虽然RNN等模型可以处理不定长的input,但是在实践中,需要对input做batchsize,转换成固定大小的tensor,方便矩阵操作.
s1: I like cats.
s2: He does not like cats.
假设默认的seq_len是5,一般会对s1做pad处理,也就是把它填充到长度为5,变成:
I like cats <PAD> <PAD>
在上述例子数字编码后,开始做embedding,而pad也会有embedding向量,但pad本身没有实际意义,参与训练可能还是有害的。因此,有必要通过一个mask tensor来记录哪些是真实的value,上述例子的两个mask如下:
1 1 1 0 0
1 1 1 1 1

tf.contrib.seq2seq.sequence_losss(training_logits,targets,masks)

training_logits:输出层的结果
targets:目标值
masks:使用tf.sequence_mask计算的结果,在这里作为权重,也就是说我们在计算交叉熵时不会把<PAD>计算进去。

tf.argmax

能给出某个tensor对象在某一维上的其数据最大值所在的索引值,常用于metric(如acc)的计算
当axis=0时,返回每一列最大值的索引

decoder_logits=[
    [
        [1,2,3,4],
        [2,3,4,5]
    ],[
        [6,7,8,9],
        [7,8,9,0]
    ]
]
out = tf.argmax(decoder_logits, 2)
with tf.Session() as sess:
    print(sess.run(out))

>>>
[[3 3]
 [3 2]]

tf.nn.embedding_lookup(params, ids)

根据input_ids中的id,寻找params中的第id行
https://www.cnblogs.com/gaofighting/p/9625868.html

tf.nn.bidirectional_dynamic_rnn 双向RNN

((encoder_fw_outputs, encoder_bw_outputs),
(encoder_fw_final_state, encoder_bw_final_state)) = tf.nn.bidirectional_dynamic_rnn 

outputs为(output_fw, output_bw),是一个包含前向cell输出tensor和后向cell输出tensor组成的二元组。假设 time_major=false, 而且tensor的shape为[batch_size, max_time, depth]。实验中使用tf.concat(outputs, 2)将其拼接。
output_states为(output_state_fw, output_state_bw),包含了前向和后向最后的隐藏状态的组成的二元组。
output_state_fw和output_state_bw的类型为LSTMStateTuple。
LSTMStateTuple由(c,h)组成,分别代表memory cell和hidden state。

报错:list indices must be integers or slices, not tuple

需要使用np.array转换成矩阵

tf.concat() 张量拼接

https://blog.csdn.net/leviopku/article/details/82380118

numpy数组切片

这是numpy的切片操作,一般结构如num[a:b,c:d],分析时以逗号为分隔符
逗号之前为要取的num行的下标范围(a到b-1),逗号之后为要取的num列的下标范围(c到d-1)
前面是行索引,后面是列索引
https://blog.csdn.net/qq_41375609/article/details/95027651

seq_targets = [
  [3,7,2],
  [1,2,0],
  [9,2,0]
]
seq_targets = np.array(seq_targets)
seq_targets = seq_targets[:,:-1]
print(seq_targets)
decoder_inputs = tf.concat([tf.reshape(tokens_go,[-1,1]), seq_targets], 1)

>>>
[[3 7]
[1 2]
[9 2]]

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

推荐阅读更多精彩内容