概述
完整源码地址: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:完整部分所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复