如何在同时训练两个子图时处理渐变

时间:2016-08-05 07:38:53

标签: machine-learning neural-network tensorflow

我想要实现的一般想法是seq2seq模型(取自模型中的translate.py-example,基于seq2seq-class)。这很好。

此外,在完成所有编码后,我正在使用隐藏的rnn状态,就在解码开始之前(我将其称为“编码结束时的隐藏状态”)。我在编码结束时使用这个隐藏状态来将其提供给另一个子图,我称之为“价格”(见下文)。这个子图的训练梯度不仅通过这个附加的子图,而且还回到rnn的编码器部分(这是我想要和需要的)。

计划是在编码结束时向隐藏状态添加更多这样的子图,因为我想以各种方式分析输入短语。

现在在培训期间,当我同时评估和训练两个子图(编码器+价格和编码器+解码器)时,网络不会收敛。但是,如果我通过以下方式(伪代码)执行训练来训练:

if global_step % 10 == 0:
    execute-the-price-training_code
else:
    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:
    #First layer
    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)

    #Second layer to softmax (price ranges)
    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")

    #Remember the prices trainables
    var_list_prices       = tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES, "prices")
    var_list_all          = tf.trainable_variables()

    #Backprop
    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)

一堆

1 个答案:

答案 0 :(得分:1)

我希望同时运行两个优化器会导致公共变量的梯度更新不一致,这可能会导致您的训练无法收敛。

相反,如果您将每个子网络的标量损失添加到“损失集合”(例如,通过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表达式来组合损失并将结果传递给单个优化器。