训练数据和预处理
数据集是电影剧本中的对话,我们首先需要做一些预处理以获得正确的数据格式。
切字分词
使用结巴分词。
移除低频词
代码中,用vocabulary_size 限制词表的大小。用UNK代替不包括在词表中的单词。例如,单词“非线性”不在词表中,则句子“非线性在神经网络中是重要的”变成“UNK在神经网络中是重要的”。
准备开始和结束标签以及填充标签
在decoder端,GO表示解码开始,用EOS表示解码结束,同时用PAD表示填充。模型使用bucketing处理不同长度的句子。如果输入是3个tocken的英语句子,相应的输出是6个tocken的法语句子,则它们将被放入到[5,10]的bucket中。编码器将输入的长度将填充到5,解码器输入的长度将填充到10 ,填充标签是PAD。
创建模型
model = seq2seq_model.Seq2SeqModel(
source_vocab_size=FLAGS.vocab_size,
target_vocab_size=FLAGS.vocab_size,
buckets=BUCKETS,
size=FLAGS.size,
num_layers=FLAGS.num_layers,
max_gradient_norm=FLAGS.max_gradient_norm,
batch_size=FLAGS.batch_size,
learning_rate=FLAGS.learning_rate,
learning_rate_decay_factor=FLAGS.learning_rate_decay_factor,
use_lstm=False,
forward_only=forward_only)
source_vocab_size是源输入词表的大小,
target_vocab_size是目标输出词表的大小。
Bucketing是一种有效处理不同长度的句子的方法。例如将英语翻译成法语时,输入具有不同长度的英语句子L1,输出是具有不同长度的法语句子L2,原则上应该为每一对(L1,L2 + 1)创建一个seq2seq模型。这会导致图很大,包括许多非常相似的子图。另一方面,我们可以用一个特殊的_PAD符号填充每个句子。然后,只需要一个seq2seq模型。但是对于较短的句子,要编码和解码许多无用的PAD符号,这样的模型也是低效的。作为折中,使用多个buckets 并且将每个句子填充为对应的bucket的长度。在config.py中,使用以下bucket。
BUCKETS = [(8, 10), (10, 15), (20, 25), (40, 50)]
参数size代表网络中每一层的单元数目。
num_layers 代表网络的层数。
max_gradient_norm 表示梯度将被最大限度地削减到这个规范。
batch_size 表示训练时的批处理大小。模型的构建与batch_size大小无关,所以在初始化之后它仍可以改变,e.g., 在decoding是时候可以改变batch_size。
learning_rate 是初始的学习率。
learning_rate_decay_factor 学习率衰减因子,到了一定的阶段,学习率按照衰减因子进行衰减。
use_lstm 是一个布尔变量,表示是否使用lstm作为基本单元。true表示使用lstm,false表示使用gru。
num_samples 代表采样sampled softmax的数量。因为我们是使用softmax来处理输出的,如果输出词表很大时,计算效率会受到影响。因此当输出输出词表大于num_samples 时使用采样softmax;当输出输出词表小于num_samples 时使用softmax。
forward_only 是一个布尔变量。false意味着在解码器端,使用decoder_inputs作为输入。例如,decoder_inputs 是‘GO, W, X, Y, Z ’,正确的输出应该是’W, X, Y, Z, EOS’。假设第一个时刻的输出不是’W’,在第二个时刻也要使用’W’作为输入。当设为true时,只使用decoder_inputs的第一个时刻的输入,即’GO’,以及解码器的在每一时刻的真实输出作为下一时刻的输入。
Get a batch and make a step.
encoder_inputs, decoder_inputs, target_weights = model.get_batch(
train_set, bucket_id)
bucket_id是随机选定的。例如:BUCKETS = [(8, 10), (10, 15), (20, 25), (40, 50)], 如果bucket_id 是1,则表示(10,15)
train_set 的长度与BUCKETS 一样,每个输入输出对按照BUCKETS 里面每个turple的长度分到相应的组里面。例如,一对输入输出的长度分别是10,13,则它们被分到第二组(10,15)中。
对于给定的bucket_id,返回train_set 中对应的数据。例如 bucket_id 是1,则encoder_inputs, decoder_inputs 的长度分别是10,15的list, 每个元素是长度为128(batch_size)的array。需要注意的是,假设开始选取的encoder_inputs是128 × 10,返回的encoder_inputs是10 × 128。例如:
encoder_inputs=[[0,11,25,34,45,55,61,72,85,93]
...
[2,3,4,5,6,7,8,9,10]]
batch_encoder_inputs=[[0,.....2]
[11,....3]
...
[93,.....10]]
return batch_encoder_inputs
同理decoder_inputs 也一样。target_weights 是decoder端每个词的权重,target是decoder_inputs 向后移动一个位置得到的,如果target是PAD,则权重是0,否则初始为1,最后一个target是EOS,权重是0。 这三者将作为model.step的输入进行训练。
_, step_loss, _ = model.step(sess, encoder_inputs, decoder_inputs, target_weights, bucket_id, forward_only=False)
将encoder_inputs, decoder_inputs, target_weights 转换为输入张量input_feed,以便于用tf进行计算。
output_feed = [self.updates[bucket_id], # Update Op that does SGD.
self.gradient_norms[bucket_id], # Gradient norm.
self.losses[bucket_id]] # Loss for this batch.
outputs = session.run(output_feed, input_feed)
if not forward_only:
return outputs[1], outputs[2], None # Gradient norm, loss, no outputs.
else:
return None, outputs[0], outputs[1:] # No gradient norm, loss, outputs.
其中self.losses 由 tf.nn.seq2seq.model_with_buckets 获得。
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)
self.losses 是一个张量list,代表每个bucket的损失。
self.outputs 是每个bucket的输出,其中每个元素是形状为[batch_size x output_size] 的张量。
seq2seq_f 是使用的seq2seq模型,例如seq2seq_q = lambda x, y: basic_rnn_seq2seq(x, y, rnn_cell.GRUCell(24)) 是一个基本的seq2seq模型,网络中的单元是GRU。seq2seq模型有很多种,例如basic_rnn_seq2seq ,embedding_rnn_seq2seq,实验中使用的是embedding_attention_seq2seq。
outputs, states=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=None,
feed_previous=False)
encoder_inputs与decoder_inputs 都是输入张量
cell是网络中的单元
num_encoder_symbols和num_decoder_symbols 是输入输出词表大小
embedding_size 是词向量大小。
output_projection 是输出投影。如果没有指定,模型的输出将是[batch-size × num_decoder_symbols]的张量,表示每个生成符号的对数。当训练一个输出词表很大的模型时,即当num_decoder_symbols很大时,存储这些张量是不符合实际的,最好是返回较小的输出张量,然后使用output_projection将其投影到大的输出张量上。这样就可以使用采样softmax损失。
self.updates表示用后向传播更新参数。self.gradient_norms表示梯度的范数。
# Gradients and SGD update operation for training the model.
params = tf.trainable_variables()
if not forward_only:
self.gradient_norms = []
self.updates = []
opt = tf.train.GradientDescentOptimizer(self.learning_rate)
for b in xrange(len(buckets)):
gradients = tf.gradients(self.losses[b], params)
clipped_gradients, norm = tf.clip_by_global_norm(gradients,max_gradient_norm)
self.gradient_norms.append(norm)
self.updates.append(opt.apply_gradients(
zip(clipped_gradients, params), global_step=self.global_step))
tf.gradients计算梯度,tf.clip_by_global_norm()是将梯度按照max_gradient_norm比率截取,opt.apply_gradients() 函数进行梯度更新。
save checkpoint, print statistics, and run evals.
# Save checkpoint and zero timer and loss.
checkpoint_path = os.path.join(FLAGS.model_dir, "model.ckpt")
model.saver.save(sess, checkpoint_path, global_step=model.global_step)
step_time, loss = 0.0, 0.0
# Run evals on development set and print their perplexity.
for bucket_id in xrange(len(BUCKETS)):
encoder_inputs, decoder_inputs, target_weights = model.get_batch(dev_set, bucket_id)
_, eval_loss, _ = model.step(sess, encoder_inputs, decoder_inputs, target_weights, bucket_id, True)
eval_ppx = math.exp(eval_loss) if eval_loss < 300 else float('inf')
print(" eval: bucket %d perplexity %.2f" % (bucket_id, eval_ppx))
http://blog.csdn.net/u013713117/article/details/54986436