Dataset.from_tensors和Dataset.from_tensor_slices有什么区别?

95
我有一个由NumPy矩阵表示的数据集,形状为(num_features, num_examples),我希望将其转换为TensorFlow类型tf.Dataset
我正在努力尝试理解这两种方法之间的区别:Dataset.from_tensorsDataset.from_tensor_slices。哪个是正确的?为什么?
TensorFlow文档(链接)表示,这两种方法都接受张量的嵌套结构,但使用from_tensor_slices时,张量在第0维应具有相同的大小。
5个回答

110

from_tensors将输入组合在一起,并返回一个只包含单个元素的数据集:

>>> t = tf.constant([[1, 2], [3, 4]])
>>> ds = tf.data.Dataset.from_tensors(t)
>>> [x for x in ds]
[<tf.Tensor: shape=(2, 2), dtype=int32, numpy=
 array([[1, 2],
        [3, 4]], dtype=int32)>]
< p > from_tensor_slices 会为输入张量的每一行创建一个单独的元素,从而创建一个数据集:

>>> t = tf.constant([[1, 2], [3, 4]])
>>> ds = tf.data.Dataset.from_tensor_slices(t)
>>> [x for x in ds]
[<tf.Tensor: shape=(2,), dtype=int32, numpy=array([1, 2], dtype=int32)>,
 <tf.Tensor: shape=(2,), dtype=int32, numpy=array([3, 4], dtype=int32)>]

43
@MathewScarpino:你能详细说明何时使用when吗? - dhiraj suvarna
9
我认为混淆(至少对我来说是这样)的源头在于名称。由于from_tensor_slices从原始数据创建片段......理想的名称应该是“to_tensor_slices” - 因为您正在将数据创建成张量片段。一旦您按照这些思路进行思考,TF2中的所有文档都变得非常清晰! - HopeKing
4
对我来说,文档中缺少的一个重要信息是这些方法会把多个张量作为元组传递,例如 from_tensors((t1, t2, t3,))。有了这个知识,from_tensors 生成的数据集中,每个输入张量就像数据集的一行;而 from_tensor_slices 生成的数据集中,每个输入张量就像数据集的一列。因此,在后一种情况下,所有张量的长度都必须相同,并且生成的数据集的元素(行)是由每列的一个元素组成的元组。 - user1488777

18

1) 两者之间的主要区别在于from_tensor_slices中的嵌套元素在第0维度上必须具有相同的尺寸:

# exception: ValueError: Dimensions 10 and 9 are not compatible
dataset1 = tf.data.Dataset.from_tensor_slices(
    (tf.random_uniform([10, 4]), tf.random_uniform([9])))
# OK, first dimension is same
dataset2 = tf.data.Dataset.from_tensors(
    (tf.random_uniform([10, 4]), tf.random_uniform([10])))

第二个不同之处详细解释在这里,当tf.Dataset的输入是一个列表时,就会出现这种情况。例如:

dataset1 = tf.data.Dataset.from_tensor_slices(
    [tf.random_uniform([2, 3]), tf.random_uniform([2, 3])])

dataset2 = tf.data.Dataset.from_tensors(
    [tf.random_uniform([2, 3]), tf.random_uniform([2, 3])])

print(dataset1) # shapes: (2, 3)
print(dataset2) # shapes: (2, 2, 3)
在上面的代码中,from_tensors创建一个3D张量,而from_tensor_slices将输入张量合并。如果您有不同来源的不同图像通道,并希望将它们连接成一个RGB图像张量,则这非常方便。
3) 如前一个答案中所述,from_tensors将输入张量转换为一个大张量:
import tensorflow as tf

tf.enable_eager_execution()

dataset1 = tf.data.Dataset.from_tensor_slices(
    (tf.random_uniform([4, 2]), tf.random_uniform([4])))

dataset2 = tf.data.Dataset.from_tensors(
    (tf.random_uniform([4, 2]), tf.random_uniform([4])))

for i, item in enumerate(dataset1):
    print('element: ' + str(i + 1), item[0], item[1])

print(30*'-')

for i, item in enumerate(dataset2):
    print('element: ' + str(i + 1), item[0], item[1])

输出:

element: 1 tf.Tensor(... shapes: ((2,), ()))
element: 2 tf.Tensor(... shapes: ((2,), ()))
element: 3 tf.Tensor(... shapes: ((2,), ()))
element: 4 tf.Tensor(... shapes: ((2,), ()))
-------------------------
element: 1 tf.Tensor(... shapes: ((4, 2), (4,)))

PS:应该是tf.random.uniform而不是tf.random_uniform。 - J W
如何将一种类型转换为另一种类型?我发现某些tf函数根据使用的类型返回错误。 - Areza

14

试试这个:

import tensorflow as tf  # 1.13.1
tf.enable_eager_execution()

t1 = tf.constant([[11, 22], [33, 44], [55, 66]])

print("\n=========     from_tensors     ===========")
ds = tf.data.Dataset.from_tensors(t1)
print(ds.output_types, end=' : ')
print(ds.output_shapes)
for e in ds:
    print (e)

print("\n=========   from_tensor_slices    ===========")
ds = tf.data.Dataset.from_tensor_slices(t1)
print(ds.output_types, end=' : ')
print(ds.output_shapes)
for e in ds:
    print (e)

输出:

=========      from_tensors    ===========
<dtype: 'int32'> : (3, 2)
tf.Tensor(
[[11 22]
 [33 44]
 [55 66]], shape=(3, 2), dtype=int32)

=========   from_tensor_slices      ===========
<dtype: 'int32'> : (2,)
tf.Tensor([11 22], shape=(2,), dtype=int32)
tf.Tensor([33 44], shape=(2,), dtype=int32)
tf.Tensor([55 66], shape=(2,), dtype=int32)

输出本身已经很清楚明了,但是可以看到from_tensor_slices()对于(from_tensors()的输出)在其第一维上进行了切片。你也可以尝试使用:

t1 = tf.constant([[[11, 22], [33, 44], [55, 66]],
                  [[110, 220], [330, 440], [550, 660]]])

使用tf 2,我得到了以下错误:AttributeError: 'TensorDataset'对象没有'output_types'属性。 - Ray Tayek

13

我认为 @MatthewScarpino 已经清楚地解释了这两种方法的区别。

在这里,我尝试描述这两种方法的典型用法:

  • from_tensors 可以用于从几个小数据集构建一个更大的数据集,即数据集的大小(长度)变大;

  • from_tensor_slices 则可用于将不同的元素组合成一个数据集,例如将特征和标签组合成一个数据集(这也是为什么张量的第一维应该相同)。也就是说,数据集变得“更宽”。


5

简单来说:

from_tensors()

返回:一个元素
类型:TensorDataset

from_tensor_slices()

返回:多个与输入长度相同的元素
类型:TensorSliceDataset

解释:

from_tensors()

对于1-D输入

import tensorflow as tf
dataset_ft = tf.data.Dataset.from_tensors([1, 2, 3])
type(dataset_ft)

>>> tensorflow.python.data.ops.dataset_ops.TensorDataset

现在,如果我们循环遍历此数据集,我们只会得到一个对象:

for _ in dataset_ft:
    print(_)  

>>> tf.Tensor([1 2 3], shape=(3,), dtype=int32)

如果我们提供2维或更多维的输入会怎样?

使用2维输入时

import tensorflow as tf
dataset_ft = tf.data.Dataset.from_tensors([[1, 2, 3], [4, 5, 6]])
type(dataset_ft)

>>> tensorflow.python.data.ops.dataset_ops.TensorDataset

现在,如果我们遍历这个数据集,我们仍然只会得到一个对象:

for _ in dataset_ft:
    print(_)

>>> tf.Tensor(
>>> [[1 2 3]
>>> [4 5 6]], shape=(2, 3), dtype=int32)

正如您所看到的,生成的张量形状与输入相同,没有变化。

from_tensor_slices()

它删除第一维并将其用作数据集维度。

在使用1维输入时

import tensorflow as tf
dataset_fts = tf.data.Dataset.from_tensor_slices([1, 2, 3])
type(dataset_fts)

>>> tensorflow.python.data.ops.dataset_ops.TensorSliceDataset

现在,如果我们循环遍历这个数据集,我们将有多个对象:

for _ in dataset_fts:
    print(_)

>>> tf.Tensor(1, shape=(), dtype=int32)
>>> tf.Tensor(2, shape=(), dtype=int32)
>>> tf.Tensor(3, shape=(), dtype=int32)

如果我们提供2维或更高维的输入,会发生什么?

使用2维输入

import tensorflow as tf
dataset_fts = tf.data.Dataset.from_tensor_slices([[1, 2, 3], [4, 5, 6]])
type(dataset_fts)

>>> tensorflow.python.data.ops.dataset_ops.TensorSliceDataset

如果我们循环遍历这个二维数据集,我们将得到两个一维元素。
for _ in dataset_fts:
    print(_)

>>> tf.Tensor([1 2 3], shape=(3,), dtype=int32)
>>> tf.Tensor([4 5 6], shape=(3,), dtype=int32)

这是我能给出的最简单的解释。为了更好地理解,建议您使用不同的输入运行这两个函数,并查看返回元素的形状。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接