Tensorflow中GNMT的实现采取多层双向LSTM构建,构建基本过程如下:
训练过程:
encoder:
- 双向(仅第一层双向):
bi_output, bi_state = tf.nn.bidirectional_dynamic_rnn(
tf.nn.bidirectional_dynamic_rnn(fw_cell,bw_cell,inputs)
其中fw_cell、bw_cell和下方组装的MultiRNNCell相同,inputs=encoder_emb_inp(词嵌入),而bi_output作为dynamic_rnn的参数,bi_state和encoder_state组成最终输入的encoder_state
- 单向(其它层)
单层:
(1) tf.contrib.rnn.BasicLSTMCell(num_units,forget_bias=forget_bias)
(2) tf.contrib.rnn.GRUCell(num_units)
(3) tf.contrib.rnn.LayerNormBasicLSTMCell
使用上述三类之一建立单层,并且使用tf.contrib.rnn.DropoutWrapper、
tf.contrib.rnn.ResidualWrapper(对应代码i >= num_layers - num_residual_layers)时)、
tf.contrib.rnn.DeviceWrapper指定dropout、是否残差层和运算设备
多层:
通过for i in range(num_layers)组建cell_list,并在layer数大于1时返回tf.contrib.rnn.MultiRNNCell(cell_list)
建立好上述多层双向结构后,推入RNN运算 encoder_outputs, encoder_state = tf.nn.dynamic_rnn(cell_list,bi_output..)。返回结果encoder_outputs和encoder_state作为decoder的输入
decoder:
输入:encoder_outputs, encoder_state,decoder_emb_inp(翻译目标嵌入向量)
模型:
类似encoder单层组装得到cell_list,取cell_list第一层pop[0]通过tf.contrib.seq2seq.AttentionWrapper加入注意力机 制, 注意力机制可选tf.contrib.seq2seq.LuongAttention等,通过GNMTAttentionMultiCell组装第一层和其它层得到 新cell_ist
tf.contrib.seq2seq.TrainingHelper(decoder_emb_inp,length,...) //获取原文对应的翻译结果
my_decoder = tf.contrib.seq2seq.BasicDecoder(cell_list, helper, decoder_initial_state)
outputs, final_context_state, _ = tf.contrib.seq2seq.dynamic_decode(my_decoder,...)
从outputs中获取sample_id和rnn_output,其中sample_id就是翻译结果对应词典中的ID,rnn_output就是最后一个隐层h,通过tf.dense(rnn_output)获取logits
loss:
crossent = tf.nn.sparse_softmax_cross_entropy_with_logits(
labels=target_output, logits=logits)
// 最终的loss还需要去掉毛重(填充部分mask掉),和batch_size平均一下
target_weights = tf.sequence_mask(
self.iterator.target_sequence_length, max_time, dtype=logits.dtype)
loss = tf.reduce_sum(crossent * target_weights) / tf.to_float(self.batch_size)
训练方向:
有了loss以后,定义梯度下降的目标是使loss变低,从而反向更新loss计算过程中用到的各层网络权值,定义过程:
opt = tf.train.GradientDescentOptimizer(learning_rate) // 也有其它选项如adam等
gradients = tf.gradients(self.train_loss,...)
// 其中max_gradient_norm梯度裁剪用,默认是5.0,防止梯度爆炸
clipped_gradients, gradient_norm = tf.clip_by_global_norm(gradients, max_gradient_norm)
self.update = opt.apply_gradients(
zip(clipped_gradients, params), global_step=self.global_step)
推断过程:
和训练过程的decoder比较相似,模型来源于训练记录的结果,只有两点不同
1、数据使用推断数据
2、加入集束搜索(beam_width)
tf.contrib.seq2seq.TrainingHelper + tf.contrib.seq2seq.BasicDecoder 转变为BeamSearchDecoder
---------> my_decoder = tf.contrib.seq2seq.BeamSearchDecoder