我尝试实现的总体思路是一个seq2seq模型(来自模型中的translate.py示例,基于seq2seq类)。这个模型训练得不错。
此外,我使用了编码完成后RNN的隐藏状态,就在解码开始之前(我称之为“编码结束时的隐藏状态”)。我将这个编码结束时的隐藏状态输入到另一个子图中,我称之为“prices”(见下文)。这个子图的训练梯度不仅会通过这个额外的子图反向传播,还会回到RNN的编码部分(这是我想要和需要的)。
计划是向编码结束时的隐藏状态添加更多这样的子图,因为我想以多种方式分析输入短语。
现在,在训练过程中,当我同时评估和训练两个子图(编码器+价格和编码器+解码器)时,网络并不会收敛。然而,如果我按照以下方式(伪代码)执行训练:
if global_step % 10 == 0: execute-the-price-training_codeelse: execute-the-decoder-training_code
所以我不是同时训练两个子图。现在它会收敛,但编码器+解码器部分的收敛速度比我只训练这一部分且从不训练价格子图时慢得多。
我的问题是:我应该能够同时训练这两个子图。但可能我需要重新调整流回到编码结束时隐藏状态的梯度。在这里,我们从价格子图和解码器子图中都得到了梯度。应该如何进行这种重新调整呢?我没有找到任何描述这种尝试的论文,但也许我用的关键词不对。
这是代码的训练部分:
这是(几乎原始的)训练操作准备:
if not forward_only: self.gradient_norms = [] self.updates = [] opt = tf.train.AdadeltaOptimizer(self.learning_rate) for bucket_id in xrange(len(buckets)): tf.scalar_summary("seq2seq loss", self.losses[bucket_id]) gradients = tf.gradients(self.losses[bucket_id], var_list_seq2seq) 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, var_list_seq2seq), global_step=self.global_step))
现在,另外,我还运行了一个第二子图,它以编码结束时的隐藏状态作为输入:
with tf.name_scope('prices') as scope: #第一层 W_price_first_layer = tf.Variable(tf.random_normal([num_layers*size, self.prices_hidden_layer_size], stddev=0.35), name="W_price_first_layer") B_price_first_layer = tf.Variable(tf.zeros([self.prices_hidden_layer_size]), name="B_price_first_layer") self.output_price_first_layer = tf.add(tf.matmul(self.hidden_state, W_price_first_layer), B_price_first_layer) self.activation_price_first_layer = tf.nn.sigmoid(self.output_price_first_layer) #self.activation_price_first_layer = tf.nn.Relu(self.output_price_first_layer) #第二层到softmax(价格范围) W_price = tf.Variable(tf.random_normal([self.prices_hidden_layer_size, self.prices_bit_size], stddev=0.35), name="W_price") W_price_t = tf.transpose(W_price) B_price = tf.Variable(tf.zeros([self.prices_bit_size]), name="B_price") self.output_price_second_layer = tf.add(tf.matmul(self.activation_price_first_layer, W_price),B_price) self.price_prediction = tf.nn.softmax(self.output_price_second_layer) self.label_price = tf.placeholder(tf.int32, shape=[self.batch_size], name="price_label") #记住价格可训练变量 var_list_prices = tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES, "prices") var_list_all = tf.trainable_variables() #反向传播 self.loss_price = tf.nn.sparse_softmax_cross_entropy_with_logits(self.output_price_second_layer, self.label_price) self.loss_price_scalar = tf.reduce_mean(self.loss_price) self.optimizer_price = tf.train.AdadeltaOptimizer(self.learning_rate_prices) self.training_op_price = self.optimizer_price.minimize(self.loss_price, var_list=var_list_all)
非常感谢
回答:
我预计同时运行两个优化器会导致对公共变量的梯度更新不一致,这可能是导致您的训练不收敛的原因。
相反,如果您将每个子网络的标量损失添加到“损失集合”中(例如,通过tf.contrib.losses.add_loss()
或tf.add_to_collection(tf.GraphKeys.LOSSES, ...)
,您可以使用tf.contrib.losses.get_total_loss()
获取一个单一的损失值,该值可以传递给单个标准TensorFlow的tf.train.Optimizer
子类。TensorFlow将为您的分裂网络推导出适当的反向传播计算。
get_total_loss()
方法只是简单地计算添加到损失集合中的值的未加权和。我不熟悉关于如何或是否应该缩放这些值的文献,但您可以使用任何任意的(可微分的)TensorFlow表达式来组合这些损失,并将结果传递给单个优化器。