如何在 tf.data.Dataset 中输入不同大小的列表列表

Posted

技术标签:

【中文标题】如何在 tf.data.Dataset 中输入不同大小的列表列表【英文标题】:How to input a list of lists with different sizes in tf.data.Dataset 【发布时间】:2018-05-14 19:27:39 【问题描述】:

我有一长串整数列表(代表句子,每个都有不同的大小),我想使用 tf.data 库提供这些列表。每个列表(列表的列表)都有不同的长度,我得到一个错误,我可以在这里重现:

t = [[4,2], [3,4,5]]
dataset = tf.data.Dataset.from_tensor_slices(t)

我得到的错误是:

ValueError: Argument must be a dense tensor: [[4, 2], [3, 4, 5]] - got shape [2], but wanted [2, 2].

有没有办法做到这一点?

编辑 1:为了清楚起见,我不想填充列表的输入列表(它是包含超过一百万个元素的句子列表,长度不一)我想使用 tf.data 库来提供,以适当的方式,具有不同长度的列表列表。

【问题讨论】:

如果您将句子列表(字符串列表)传递给tf.data.Dataset.from_tensor_slices,它应该可以工作,然后您应该能够使用dataset.map(your_function) 将每个句子转换为整数列表。然后您可以使用dataset.padded_batch 自动添加填充。 这个例子很有用:github.com/tensorflow/nmt#data-input-pipeline 嗨@OlivierMoindrot,我看过那个例子。我担心的是:当您在训练中运行图形时(即每次向模型提供新数据时),它们会执行映射函数,还是在训练之前在整个数据集上执行,然后输入结果?在我看来,第一个训练比第二个慢得多,这就是我想要避免的。 这是tf.data 的重点,它在后台使用队列,只根据需要处理数据。您可以“预取”数据以确保您的 GPU 永远不会等待数据并且以 100% 的速度工作。由于数据在一端被消耗(用于训练),因此之前的队列被数据填满。您甚至可以使用num_parallel_calls 拥有多个工作人员。 dataset.prefetch 【参考方案1】:

对于那些使用 TensorFlow 2 并正在寻找答案的人 我发现以下内容可以直接使用参差不齐的张量。 只要整个数据集适合内存,它应该比生成器快得多。

t = [[[4,2]],
     [[3,4,5]]]

rt=tf.ragged.constant(t)
dataset = tf.data.Dataset.from_tensor_slices(rt)

for x in dataset:
  print(x)

生产

<tf.RaggedTensor [[4, 2]]>
<tf.RaggedTensor [[3, 4, 5]]>

出于某种原因,在单个数组上至少有 2 个维度是非常特别的。

【讨论】:

+1 但 tf 2.1 仅供参考,不再需要额外的括号【参考方案2】:

除了@mrry 的回答,如果您想创建(图像、标签)对,也可以使用以下代码:

import itertools
data = tf.data.Dataset.from_generator(lambda: itertools.izip_longest(images, labels),
                                      output_types=(tf.float32, tf.float32),
                                      output_shapes=(tf.TensorShape([None, None, 3]), 
                                                     tf.TensorShape([None])))

iterator = dataset.make_one_shot_iterator()
next_element = iterator.get_next()

with tf.Session() as sess:
    image, label = sess.run(next_element)  # ==> shape: [320, 420, 3], [20]
    image, label = sess.run(next_element)  # ==> shape: [1280, 720, 3], [40]

【讨论】:

【参考方案3】:

您可以使用tf.data.Dataset.from_generator() 将任何可迭代的Python 对象(如列表列表)转换为Dataset

t = [[4, 2], [3, 4, 5]]

dataset = tf.data.Dataset.from_generator(lambda: t, tf.int32, output_shapes=[None])

iterator = dataset.make_one_shot_iterator()
next_element = iterator.get_next()

with tf.Session() as sess:
  print(sess.run(next_element))  # ==> '[4, 2]'
  print(sess.run(next_element))  # ==> '[3, 4, 5]'

【讨论】:

@mrry,我也在做同样的想法,来自生成器的数据集可以批量化,我的意思是小批量化?【参考方案4】:

我认为 tensorflow 不支持在给定维度上具有不同数量元素的张量。

但是,一个简单的解决方案是用尾随零填充嵌套列表(在必要时):

t = [[4,2], [3,4,5]]
max_length = max(len(lst) for lst in t)
t_pad = [lst + [0] * (max_length - len(lst)) for lst in t]
print(t_pad)
dataset = tf.data.Dataset.from_tensor_slices(t_pad)
print(dataset)

输出:

[[4, 2, 0], [3, 4, 5]]
<TensorSliceDataset shapes: (3,), types: tf.int32>

零对于模型来说应该不是什么大问题:从语义上讲,它们只是每个实际句子列表末尾的大小为零的额外句子。

【讨论】:

您好,感谢您的回答,由于列表的大小,我无法填充整个列表。我会为每个批次做填充,但不是为由数百万个句子组成的整个数据集。

以上是关于如何在 tf.data.Dataset 中输入不同大小的列表列表的主要内容,如果未能解决你的问题,请参考以下文章

规范化 tf.data.Dataset

tf.data.Dataset.padded_batch 以不同的方式填充每个功能

如何在 keras 自定义回调中访问 tf.data.Dataset?

tf.keras 模型 多个输入 tf.data.Dataset

如何将 tf.data.Dataset 与 kedro 一起使用?

如何在 tensorboard 中显示 Tensorflow 2.0 中的 tf.data.Dataset.map 子图?