python - 使用 tf.while_loop (TensorFlow) 从图中累积输出
问题描述
长话短说,我有一个堆叠在 CNN 之上的 RNN。CNN 是单独创建和训练的。为了澄清事情,让我们假设 CNN 以 [BATCH SIZE, H, W, C] 占位符的形式接受输入(H = 高度,W = 宽度,C = 通道数)。
现在,当堆叠在 RNN 之上时,组合网络的整体输入将具有以下形状:[BATCH SIZE, TIME SEQUENCE, H, W, C],即 minibatch 中的每个样本由 TIME_SEQUENCE 多个图像组成。此外,时间序列的长度是可变的。有一个名为sequence_lengths
[BATCH SIZE] 形状的单独占位符,其中包含与小批量中每个样本的长度相对应的标量值。TIME SEQUENCE 的值对应于最大可能的时间序列长度,对于长度较小的样本,剩余的值用零填充。
我想做的事
我想以 [BATCH SIZE, TIME SEQUENCE, 1] 形状的张量累积 CNN 的输出(最后一个维度只包含 CNN 为每个批次元素的每个时间样本输出的最终分数),以便我可以转发将整个信息块传递给 RNN,并堆叠在 CNN 之上。棘手的是,我还希望能够将错误从 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
方法时,它会变成一个无法对其进行赋值的张量类型对象。我收到错误消息:Sliced assignment is only supported for variables
即使我使用另一种格式(例如使用 BATCH SIZE 张量的元组,每个元组的维度为 [TIME SEQUENCE、H、W、C]),这个问题仍然存在。
我也愿意对代码进行彻底的重新设计,只要它能很好地完成工作。
解决方案
解决方案是使用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()
推荐阅读
- python - Dask Dataframes (Python):如何通过子文件运行计算?即如何利用底层文件结构?
- javascript - getuikit 模态根本不显示
- javascript - UnhandledPromiseRejectionWarning: DiscordAPIError: Invalid Form Body
- java - Gluon JavaFX maven nativerun 在加载 fxml 文件时出现错误
- apache-spark - 从文件列表而不是 Spark 中的 PATH 读取是否有效?
- owl - 删除具有 symmetricProperty 的语句时,GraphDB 的行为不一致
- javascript - 如何迭代地构建一个字符串
- excel - 如何检查任何工作表的 A 列中是否存在值,并在其为真的行中返回 B 列中的文本?
- javascript - 如何将 GET 请求与第三方小部件一起使用
- javascript - 如何更改表单中的多个输入