我正在处理一个时间序列问题,其中每个时间序列都相当长(10 ^ 3-10 ^ 4个时间步长,并且每个时间序列的长度都不同)。
对于每个序列,我可以定义一个Python生成器,一次生成一个值。我正在使用tf.data.Dataset.from_generator()
构造函数将这些生成器包装到tf.data API中。该文档建议使用from_generator()
和tf.contrib.data.parallel_interleave()
转换来并行化从我的Python生成器提取的内容。
我在这些数据的下游使用的是有状态的RNN(例如LSTM或GRU)。我想将时间序列分块到较小的窗口中(〜10 ^ 2),并使用每个块作为训练示例(即,截断的BPTT)。由于我的数据正在流式传输,我认为这意味着在将每个生成器通过管道传递之前,先保存window_size
个时间步,再与其他生成器的数据进行批处理。我还想在这些块之间保存RNN状态,以便我仍然可以学习长期依赖关系。
我的问题是想要创建这些生成器的批处理输出的填充批处理。理想情况下,我想向神经网络展示生成器输出的窗口,当生成器的某些子集先于其他子集耗尽时,需要填充。我知道,如果我消耗了每个生成器的整个生成器输出,则可以使用Dataset.padded_batch()
(然后可以根据需要在时间维度上将填充的批处理切成窗口大块)。但是,我想将每个窗口传递给它可用的神经网络。如果其中一个生成器先于其他生成器用尽自己的电量,那么我想用填充值填充它,直到所有其他生成器都具有填充值,以便我可以重置RNN状态并以空的初始RNN状态开始下一批生成器。我被困在这里是因为tf.contrib.data.parallel_interleave()
转换产生的数据集在耗尽每个生成器时都会丢弃,并且时间序列在从其生成的样本之间不会保持一致的顺序。
这是一个小例子:
import tensorflow as tf
def stepwise_generator(length):
for i in range(length):
yield i
lengths = list(range(1,10,2)) # [1, 3, 5, 7, 9]
window_length = 4
batch_size = 3
dataset = tf.data.Dataset.from_tensor_slices(lengths)
gen = lambda length: tf.data.Dataset.from_generator(
stepwise_generator, tf.float32, output_shapes=[], args=(length,)
).batch(window_length) # this batching saves window_length timesteps per generator
dataset = dataset.apply(
tf.contrib.data.parallel_interleave(gen, cycle_length=batch_size)
)
dataset = dataset.padded_batch(batch_size, (-1,), np.inf)
# batching 3 generators at once, and padding exhausted ones with inf.
# using a batch_size value no more than cycle_length above means we
# shouldn't start a new generator mid-batch (i think)
iterator = dataset.make_one_shot_iterator()
tensor = iterator.get_next()
outs = []
with tf.Session() as sess:
while True:
try:
out = sess.run(tensor)
outs.append(out)
except tf.errors.OutOfRangeError:
break
print(np.asarray(outs))
输出:
[[[ 0. inf inf inf] # batch 1
[ 0. 1. 2. inf]
[ 0. 1. 2. 3.]]
[[ 4. inf inf inf] # batch 2 - the generator in index -1 in the
[ 0. 1. 2. 3.] # previous batch gets cycled to index 0 and two
[ 0. 1. 2. 3.]] # new generators are initiated
[[ 4. 5. 6. inf] # batch 3 - more generator cycling, and the one in
[ 4. 5. 6. 7.] # index 1 also gets cycled to index 2 in the same
[ 8. inf inf inf]]] # batch (because we have run out of generators in
# parallel_interleave)
我想要的输出将是
[[[ 0. inf inf inf] # batch 1
[ 0. 1. 2. inf]
[ 0. 1. 2. 3.]]
[[inf] # batch 2 - the leftover timestep from a padded
[inf] # batch of the first 3 generators
[4. ]]
[[ 0. 1. 2. 3.] # batch 3 - only two generators are left so this is
[ 0. 1. 2. 3.]] # an end-of-epoch smaller batch
[[ 4. 5. 6. inf] # batch 4
[ 4. 5. 6. 7.]]
[[inf] # batch 5
[ 8.]]]
在这里,将在第2批和第5批之后重置RNN的内部状态。
同样,如果我消耗了每个生成器的全部输出,然后填充,批处理和切片,则可以轻松创建所需的输出,但是我想生成生成器的批处理,它们可能每个都在实际接收数据。从例如开始的时间单独的模拟,使其可用。
答案 0 :(得分:0)
TensorFlow 中的有状态 RNN 需要固定批大小,因此您想要的输出不起作用:批大小从 3 变为 2。
所以你需要有这样的东西:
[[[ 0. inf inf inf] # batch 1
[ 0. 1. 2. inf]
[ 0. 1. 2. 3.]]
[[inf] # batch 2 - the leftover timestep from a padded
[inf] # batch of the first 3 generators
[4. ]]
[[ 0. 1. 2. 3.] # batch 3 - only two generators are left
[ 0. 1. 2. 3.] # but we still need the same batch size
[ inf inf inf inf]] # so this row of `inf` is needed
[[ 4. 5. 6. inf] # batch 4
[ 4. 5. 6. 7.]
[ inf inf inf inf]]
[[inf] # batch 5
[ 8.]
[inf]]]
我认为使用您的 interleave
+ padded_batch
方法无法做到这一点。
然而,一种有效的方法是将所有序列填充到相同的长度。这是一个使用 TensorFlow 2.4.1 的工作示例(它应该适用于其他 TF 2 版本):
import tensorflow as tf
import numpy as np
lengths = list(range(1,12,2)) # [1, 3, 5, 7, 9, 11]
max_length = max(lengths)
def stepwise_generator(length):
for i in range(max_length):
if i < length:
yield float(i)
else:
yield np.inf
window_length = 4
batch_size = 3
dataset = tf.data.Dataset.from_tensor_slices(lengths)
gen = lambda length: tf.data.Dataset.from_generator(
stepwise_generator, tf.float32, args=(length,)
).batch(window_length) # this batching saves window_length timesteps per generator
dataset = dataset.interleave(gen, cycle_length=batch_size)
dataset = dataset.batch(batch_size)
for batch in dataset:
print(batch)
这给出了以下输出:
tf.Tensor(
[[ 0. inf inf inf]
[ 0. 1. 2. inf]
[ 0. 1. 2. 3.]], shape=(3, 4), dtype=float32)
tf.Tensor(
[[inf inf inf inf]
[inf inf inf inf]
[ 4. inf inf inf]], shape=(3, 4), dtype=float32)
tf.Tensor(
[[inf inf inf]
[inf inf inf]
[inf inf inf]], shape=(3, 3), dtype=float32)
tf.Tensor(
[[0. 1. 2. 3.]
[0. 1. 2. 3.]
[0. 1. 2. 3.]], shape=(3, 4), dtype=float32)
tf.Tensor(
[[ 4. 5. 6. inf]
[ 4. 5. 6. 7.]
[ 4. 5. 6. 7.]], shape=(3, 4), dtype=float32)
tf.Tensor(
[[inf inf inf]
[ 8. inf inf]
[ 8. 9. 10.]], shape=(3, 3), dtype=float32)
注意事项:
batch()
而不是 padded_batch()
tf.contrib
包已在 TF 2 中删除,但 tf.contrib.data.parallel_interleave()
已提升为核心 API:您现在可以使用 dataset.interleave()
dataset.make_one_shot_iterator()
、iterator.get_next()
或 tf.Session()
等。它要简单得多。