我是靠谱客的博主 欣慰鞋垫,最近开发中收集的这篇文章主要介绍tensorflow实现seq2seq:完整部分,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

完整源码地址:https://github.com/colin0000007/seq2seq

源码中我将实现封装为了2个类,一个BasicSeq2SeqModel,一个AttentionSeq2SeqModel。前者包含了bi-rnn,beam search等特效,后者只是多了attention。下面详细讲讲BasicSeq2SeqModel。

1.训练数据

seq2seq数据分为源序列,目标序列。

这里的测试数据源序列是字母序列,目标序列是字母序列的倒序。

例如:

source:
ozi
dzrkwam
cuemir
krmwaes
target:
izo
mawkrzd
rimeuc
seawmrk

训练好模型后,输入一个字母序列,期望得到这个序列的倒序。

2.输入和数据

source:就是源序列

target:就是目标序列

比如seq2seq的机器翻译中,source就是被翻译的句子。

Target就是翻译到的句子。

第一步首先定义输入的tensor。包括:

source的tensor,shape=[batchsize,src_max_len]

target的tensor,shape=[batchsize,max_len]

这里有一个问题是,最终的标签(y)是什么?实际上就是target

序列生成的过程。

比如:

Source: abcd

Target:dcba

如果统计后知道src_max_len = 5,tgt_max_len=6

需要对tgt_max_len+1,因为target_x和target_y中分别包含了<s>和</s>

对于不足长度的样本补充<pad>

其次还需在target中添加<s></s>表示序列的开始和结尾。

那么最后的数据应该是这样:

source:

a b c d <pad>

target_x:

<s> d c b a <pad>

target_y:

d c b a </s> <pad>

也就是用<s>加上source上下文向量预测a

用a加上source 上下文向量预测b

生成时遇到</s>或者达到最大生成序列长度就结束。

一般都需要对source和target做embeding。至于是否需要单独为target_x和target_y声明2个tensor完全取决你自己。因为decoder最终使用的embedding后的tensor,此时对原来的target tensor处理一下就可以作为target_y。另外在实现时使用的tf.contrib.seq2seq.sequence_loss,并不要求把target_y变成one-hot向量。

对应代码部分:

#定义encoder的输入tensor,加入了词嵌入
def input_tensor():
#这里设置位None,意思是不指定batch_size用到多少就是多少
source_batch = tf.placeholder(tf.int32,[None,None],name="source_batch")
#对source做词嵌入,词向量矩阵的shape为[source的词库大小,嵌入维度]
#嵌入矩阵中每一行就是一个词向量
source_embedding = tf.get_variable(shape=[source_vocab_size,source_embedding_size],name='source_embedding')
#使用embedding_lookup从embedding矩阵中查询词向量从而将X的每一个单词的index转换一个词向量
embedded_source_batch = tf.nn.embedding_lookup(source_embedding,source_batch)
#最后返回的embedded_X.shape = [batch_size,time_step,embedding_size]
#target类似
target_batch_x = tf.placeholder(tf.int32,[None,None])
target_batch_y = tf.placeholder(tf.int32,[None,None])
target_embedding = tf.get_variable(shape=[target_vocab_size,source_embedding_size],name='target_embedding')
embedded_target_batch_x = tf.nn.embedding_lookup(target_embedding,target_batch_x)
source_batch_seq_len = tf.placeholder(tf.int32,[None],name="source_batch_seq_len")
target_batch_seq_len = tf.placeholder(tf.int32,[None],name="target_batch_seq_len")
#保存当前batch的最长序列值,mask的时候需要用到
tgt_batch_max_len = tf.placeholder(tf.int32,[],name="target_batch_max_len")
return embedded_source_batch,embedded_target_batch_x,source_batch,target_batch_x,target_batch_y,source_batch_seq_len,target_batch_seq_len,target_embedding,tgt_batch_max_len

3.encoder

encoder比较简单,就是堆叠几层rnn cell就行了,关于encoder我以前一直有个错误的想法,以为需要单独训练encoder,实际上并不是,encoder是和decoder连接到一起的,做梯度下降时参数一并更新。

#参照tensorflow/nmt的官方教程
def build_encoder(embedded_source_batch,source_batch_seq_len):
#定义rnn cell的获取
def get_rnn_cell():
return tf.nn.rnn_cell.LSTMCell(num_units=rnn_num_units)
#定义encoder的rnn_layer
rnn_layer = tf.nn.rnn_cell.MultiRNNCell([get_rnn_cell() for _ in range(rnn_cell_size)])
#将rnn沿时间序列展开
#
encoder_outputs: 返回了每一个序列节点的output,shape为[batch_size,max_time, num_units]
#
encoder_state: 返回了每个cell最后一个序列节点的state输出,shape为cell个数*[batch_size, num_units]
#
sequence_length:传入一个list保存每个样本的序列的真实长度,教程中说这样做有助于提高效率
encoder_outputs, encoder_state = tf.nn.dynamic_rnn(
rnn_layer, embedded_source_batch,
sequence_length=source_batch_seq_len, time_major=False,dtype=tf.float32)
return encoder_outputs, encoder_state

4.decoder

decoder要麻烦一些,首选定义rnn cell,还需要定义projection layer,映射层。它的作用是将decoder的rnn输出映射为大小target词库大小,这样可以做分类。训练用到trainingHelper,测试阶段可以使用GreedyEmbeddingHelper或者beamSearch来解码。inference定义的decoder需要用到训练定义的decoder,这样可以共享rnn参数,projection layer的参数。具体看代码:

训练encoder:

def build_decoder(encoder_outputs, encoder_state,embedded_target_batch_x,target_batch_seq_len,target_embedding):
def get_rnn_cell():
return tf.nn.rnn_cell.LSTMCell(num_units=rnn_num_units)
rnn_layer = tf.nn.rnn_cell.MultiRNNCell([get_rnn_cell() for _ in range(rnn_cell_size)])
#decoder:需要helper,rnn cell,helper被分离开可以使用不同的解码策略,比如预测时beam search,贪婪算法
#这里projection_layer就是一个全连接层,encoder_state连接到encoder_embede后的向量维度不能和target词汇数量一致所以需要映射层
#这里使用tensorflow.python.keras.layers.core.Dense一直报错
#换成tf.layers.Dense后解决 但是我在另一份能够运行的seq2seq代码中发现使用上面的Dense并不会报错
projection_layer = tf.layers.Dense(units=target_vocab_size,kernel_initializer = tf.truncated_normal_initializer(mean = 0.0, stddev=0.1))
training_helper = tf.contrib.seq2seq.TrainingHelper(
embedded_target_batch_x, target_batch_seq_len, time_major=False)
decoder = tf.contrib.seq2seq.BasicDecoder(
rnn_layer, training_helper, encoder_state,
output_layer=projection_layer)
#将序列展开
decoder_output,_,_ = tf.contrib.seq2seq.dynamic_decode(decoder,output_time_major=False,maximum_iterations=target_max_len)
#decoder_output的shape:[batch_size,序列长度,target_vocab_size]
return decoder_output,rnn_layer,projection_layer

inference阶段的decoder:

#推理阶段,也就是预测阶段
def inference(target_embedding,encoder_state,decoder_rnn_layer,decoder_projection_layer):
inuput_batch = tf.placeholder(dtype=tf.int32, shape=[1],name="input_batch")
start_tokens = tf.tile(tf.constant(value=start_token_id, dtype=tf.int32,shape=[1]), multiples = inuput_batch, name="start_tokens_2")
predicting_helper = tf.contrib.seq2seq.GreedyEmbeddingHelper(target_embedding,start_tokens,end_token_id)
predicting_decoder = tf.contrib.seq2seq.BasicDecoder(
decoder_rnn_layer, predicting_helper, encoder_state,
output_layer=decoder_projection_layer)
predicting_decoder_output, _ ,_= tf.contrib.seq2seq.dynamic_decode(predicting_decoder,output_time_major=False,maximum_iterations=target_max_len)
tf.identity(predicting_decoder_output.sample_id, name='predictions2')

4.loss构建,训练计算图构建等

直接贴代码,更多细节问题参考前几篇说细节的文章。

def seq2seq_model(embedded_source_batch,source_batch_seq_len,embedded_target_batch_x,target_batch_seq_len,target_embedding):
encoder_outputs, encoder_state = build_encoder(embedded_source_batch, source_batch_seq_len)
decoder_output,rnn_layer,projection_layer = build_decoder(encoder_outputs, encoder_state, embedded_target_batch_x, target_batch_seq_len, target_embedding)
inference(target_embedding, encoder_state, rnn_layer, projection_layer)
print("encoder_outputs:",encoder_outputs)
print("encoder_state:",encoder_state)
print("decoder_output:",decoder_output)
return decoder_output
def build_graph():
#构造计算图
train_graph = tf.Graph()
with train_graph.as_default():
#1.tensor声明
embedded_source_batch,embedded_target_batch_x,source_batch,target_batch_x,target_batch_y,source_batch_seq_len,target_batch_seq_len,target_embedding,tgt_batch_max_len = input_tensor()
#2.构造seq2seq产生的output tensor
decoder_output = seq2seq_model(embedded_source_batch, source_batch_seq_len, embedded_target_batch_x, target_batch_seq_len,target_embedding)
#1和2这个步骤必须在同一个graph下声明
#对这2个decoder的输出获取不同的tensor并且取名字
training_logits = tf.identity(decoder_output.rnn_output, 'logits')
print("training_logits.shape:",training_logits.shape)
#尝试是否能成功
#mask的作用是:计算loss时忽略pad的部分,这部分的loss不需要算,提升性能,
masks = tf.sequence_mask(target_batch_seq_len, tgt_batch_max_len, dtype=tf.float32, name='masks')
with tf.name_scope("optimization"):
#loss
#这里尝试使用下tensorflow-nmt官方教程中的tf.nn.sparse_softmax_cross_entropy_with_logits
#不测试了,不能使用mask
loss = tf.contrib.seq2seq.sequence_loss(
training_logits,
target_batch_y,
masks)
optimizer = tf.train.AdamOptimizer(lr)
# Gradient Clipping
gradients = optimizer.compute_gradients(loss)
capped_gradients = [(tf.clip_by_value(grad, -5., 5.), var) for grad, var in gradients if grad is not None]
train_op = optimizer.apply_gradients(capped_gradients)
return train_graph,loss,train_op,source_batch,target_batch_x,target_batch_y,source_batch_seq_len,target_batch_seq_len,tgt_batch_max_len

这篇本来打算连着前面几个一起写完的,一期末就忙忘了,很多东西也有点不熟悉了,写得很水。

最后

以上就是欣慰鞋垫为你收集整理的tensorflow实现seq2seq:完整部分的全部内容,希望文章能够帮你解决tensorflow实现seq2seq:完整部分所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(48)

评论列表共有 0 条评论

立即
投稿
返回
顶部