张量索引列表

时间:2020-11-10 19:31:37

标签: python numpy pytorch

我有两个相同的张量列表(大小不同),但对于第一个张量,所有张量都分配给了cuda设备。例如:

list1=[torch.tensor([0,1,2]).cuda(),torch.tensor([3,4,5,6]).cuda(),torch.tensor([7,8]).cuda()]
>>> list1
[tensor([0, 1, 2], device='cuda:0'), tensor([3, 4, 5, 6], device='cuda:0'), tensor([7, 8], device='cuda:0')]
list2=[torch.tensor([0,1,2]),torch.tensor([3,4,5,6]),torch.tensor([7,8])]
>>> list2
[tensor([0, 1, 2]), tensor([3, 4, 5, 6]), tensor([7, 8])]

我想根据索引数组从列表中提取一些张量:

ind=torch.tensor([0,2])
>>> ind
tensor([0, 2])

所以我的解决方案是做这样的事情:

np.array(list1)[ind]
np.array(list2)[ind]

我的问题是为什么它与在cuda设备上定义的张量的第一个列表一起使用,而对第二个列表给出错误,如下所示:

>>> np.array(list1)[ind]
array([tensor([0, 1, 2], device='cuda:0'),
       tensor([7, 8], device='cuda:0')], dtype=object)
>>> np.array(list2)[ind]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: only one element tensors can be converted to Python scalars

编辑: 只是为了澄清,因为张量具有不同的形状,所以不会引发错误。以下示例说明了这一点:

list3=[torch.tensor([1,2,3]).cuda()]
list4=[torch.tensor([1,2,3]).cuda(),torch.tensor([4,5,6]).cuda()]
list5=[torch.tensor([1,2,3])]
list6=[torch.tensor([1,2,3]),torch.tensor([4,5,6])]

结果是:

>>> np.array(list3)
array([tensor([1, 2, 3], device='cuda:0')], dtype=object)
>>> np.array(list4)
array([tensor([1, 2, 3], device='cuda:0'),
       tensor([4, 5, 6], device='cuda:0')], dtype=object)
>>> np.array(list5)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: only one element tensors can be converted to Python scalars
>>> np.array(list6)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: only one element tensors can be converted to Python scalars

1 个答案:

答案 0 :(得分:1)

np.array尝试将列表的每个元素转换为numpy数组。仅CPU张量支持此功能。简短的答案是,您可以显式指示numpy使用dtype=object创建一个数组,以使CPU情况正常工作。要了解到底发生了什么,让我们仔细看看这两种情况。

情况1(CUDA张量)

首先请注意,如果您尝试在CUDA张量上使用np.array,则会出现以下错误

np.array(torch.zeros(2).cuda())
TypeError: can't convert CUDA tensor to numpy. Use Tensor.cpu() to copy the tensor to host memory first.

在您的示例中,numpy尝试将list1的每个元素转换为numpy数组,但是引发了一个异常,因此它只解决了使用dtype=object创建数组的情况。

你最终会

np.array([torch.tensor([0,1,2]).cuda(), torch.tensor([3,4,5,6]).cuda(), torch.tensor([7,8]).cuda()])

只是一个指向不同对象的容器

array([tensor([0, 1, 2], device='cuda:0'),
       tensor([3, 4, 5, 6], device='cuda:0'),
       tensor([7, 8], device='cuda:0')], dtype=object)

情况2(CPU张量)

对于CPU张量,PyTorch知道如何转换为numpy数组。因此,当您运行

np.array(torch.zeros(2))

您得到一个dtype为float32的numpy数组

array([0., 0.], dtype=float32)

当numpy成功将list2中的每个元素转换为numpy数组,然后尝试将它们堆叠为单个多维数组时,问题出在您的代码中。 Numpy期望每个列表项代表一个多维数组的一行,但是在您的情况下,它发现并非所有行都具有相同的形状,因此不知道如何进行并引发异常。

解决此问题的一种方法是显式指定dtype应该保留object。这基本上告诉numpy“不要先尝试将条目转换为numpy数组”。

np.array([torch.tensor([0,1,2]), torch.tensor([3,4,5,6]), torch.tensor([7,8])], dtype=object)

现在给出与情况1类似的结果

array([tensor([0, 1, 2]),
       tensor([3, 4, 5, 6]),
       tensor([7, 8])], dtype=object)