使用 tf.while_loop 累积图表的输出

问题描述 投票:0回答:1

我有一个 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
    。我的想法是将单个小批量元素沿时间轴的所有图像视为 CNN 的小批量。本质上,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 张量的元组,这个问题仍然存在。

我也愿意对代码进行彻底的重新设计,只要它能很好地完成工作。

python tensorflow deep-learning recurrent-neural-network
1个回答
2
投票

解决方案是使用

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()
© www.soinside.com 2019 - 2024. All rights reserved.