使用tf.while_loop(TensorFlow)累积图的输出

时间:2018-07-13 17:14:12

标签: python tensorflow deep-learning recurrent-neural-network

长话短说,我有一个RNN堆叠在CNN的顶部。 CNN是分别创建和培训的。为了说明问题,让我们假设CNN以[BATCH SIZE,H,W,C]占位符的形式输入(H =高度,W =宽度,C =通道数)。

现在,当堆叠在RNN顶部时,组合网络的整体输入将具有以下形状:[BATCH SIZE,TIME SEQUENCE,H,W,C],即小批量中的每个样本都包含TIME_SEQUENCE许多图像。此外,时间序列的长度是可变的。有一个名为sequence_lengths的单独占位符,形状为[BATCH SIZE],其中包含与小批量中每个样本的长度相对应的标量值。 TIME SEQUENCE的值对应于最大可能的时间序列长度,对于长度较小的样本,其余值用零填充。

我想做什么

我想将CNN的输出以张量[BATCH SIZE,TIME SEQUENCE,1]的形式累加(最后一个维度仅包含CNN为每个批次元素的每个时间样本输出的最终分数),以便我可以将全部信息转发到堆叠在CNN顶部的RNN。棘手的事情是,我还希望能够将错误从RNN反向传播到CNN(CNN已经进行了预训练,但是我想对权重进行微调),所以我必须留在图中,即我无法调用session.run()

  • 选项A: 最简单的方法是将整个网络输入张量调整为[BATCH SIZE * TIME SEQUENCE,H,W,C]。这样做的问题是,BATCH SIZE * TIME SEQUENCE可能大到2000,所以当试图将大批量的批处理放入CNN时,我肯定会用光内存。而且批次大小太大,无论如何都无法进行培训。而且,很多序列只是填充零,这会浪费计算量。

  • 选项B: 使用tf.while_loop。我的想法是将单个minibatch元素沿时间轴的所有图像都视为CNN的minibatch。本质上,CNn将在每次迭代中处理大小为[TIME SEQUENCE,H,W,C]的批处理(并非每次都精确地对TIME SEQUENCE进行多次图像处理;确切的数量取决于序列长度)。我现在拥有的代码如下:

    # The output tensor that I want populated
    image_output_sequence = tf.Variable(tf.zeros([batch_size, max_sequence_length, 1], tf.float32))
    
    # Counter for the loop. I'll process one batch element per iteration.
    # One batch element contains a variable number of images for each time step. All these images will form a minibatch for the CNN.
    loop_counter = tf.get_variable('loop_counter', dtype=tf.int32, initializer=0)
    
    # Loop variables that will be passed to the body and cond methods
    loop_vars = [input_image_sequence, sequence_lengths, image_output_sequence, loop_counter]
    # input_image_sequence: [BATCH SIZE, TIME SEQUENCE, H, W, C]
    # sequence_lengths: [BATCH SIZE]
    # image_output_sequence: [BATCH SIZE, TIME SEQUENCE, 1]
    
    # abbreviations for vars in loop_vars:
    # iis --> input_image_sequence
    # sl --> sequence_lengths
    # ios --> image_output_sequence
    # lc --> loop_counter
    def cond(iis, sl, ios, lc):  
        return tf.less(lc, batch_size)
    
    def body(iis, sl, ios, lc):
        seq_len = sl[lc]  # the sequence length of the current batch element
        cnn_input_batch = iis[lc, :seq_len]  # extract the relevant portion (the rest are just padded zeros)
    
        # propagate this 'batch' through the CNN
        my_cnn_model.process_input(cnn_input_batch)
    
        # Pad the remaining indices
        padding = [[0, 0], [0, batch_size - seq_len]]
        padded_cnn_output = tf.pad(cnn_input_batch_features, paddings=padding, mode='CONSTANT', constant_values=0)
    
        # The problematic part: assign these processed values to the output tensor
        ios[lc].assign(padded_cnn_features)
        return [iis, sl, ios, lc + 1]
    
    _, _, result, _ = tf.while_loop(cond, body, loop_vars, swap_memory=True)
    

my_cnn_model.process_input内,我只是通过一个普通的CNN传递输入。在其中创建的所有变量都与tf.AUTO_REUSE一起使用,因此应确保while循环在所有循环迭代中重用相同的权重。

确切的问题

image_output_sequence是一个变量,但是当tf.while_loop调用body方法时,它变成了一个Tensor类型对象,无法对其进行赋值。我收到错误消息:Sliced assignment is only supported for variables

即使我使用另一种格式(例如使用大小分别为[TIME SEQUENCE,H,W,C]的BATCH SIZE张量元组),该问题仍然存在。

我也愿意完全重新设计代码,只要它能很好地完成工作即可。

1 个答案:

答案 0 :(得分:1)

解决方案是使用类型为TensorArray的对象,该对象专门用于解决此类问题。以下行:

image_output_sequence = tf.Variable(tf.zeros([batch_size, max_sequence_length, 1], tf.float32))

替换为:

image_output_sequence = tf.TensorArray(size=batch_size, dtype=tf.float32, element_shape=[max_sequence_length, 1], infer_shape=True)

TensorArray实际上并不是每个元素都需要固定的形状,但就我而言,它是固定的,因此最好将其实施。

然后在body函数中,替换为:

ios[lc].assign(padded_cnn_features)

具有:

ios = ios.write(lc, padded_cnn_output)

然后在tf.while_loop语句之后,可以将TensorArray堆叠起来以形成常规Tensor进行进一步处理:

stacked_tensor = result.stack()