【问题标题】:Indexing list of tensors张量的索引列表
【发布时间】:2020-11-10 19:31:37
【问题描述】:

我有两个相同的张量列表(大小不同),除了第一个的所有张量都分配给 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

【问题讨论】:

    标签: python numpy pytorch


    【解决方案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)
    

    【讨论】:

    • 感谢您的解释。对于第二部分,我认为它不会引发异常,因为形状不同所以它不能堆叠,因为如果它们都具有相同的形状,它也会失败。此外,如果列表只有一个张量,即 np.array([torch.zeros(2)]),它也会失败。我仍然无法得到这种行为。
    • 有趣,我可以确认您的发现。这似乎意味着 numpy 正在尝试在某个时候转换为标量。我对发生这种转换的 numpy C 源代码不是非常熟悉,但我会看一下,看看我是否能准确地找出发生这种情况的原因。也就是说,如果您希望使用 numpy 进行索引,指定 dtype=object 似乎仍然是解决方案。虽然我认为在大多数情况下 numpy 在这里完全没有必要,因为您可以使用普通的 python 列表理解来选择列表的子集,这样更容易解释。
    • 是的,我已经尝试使用 dtype=object 并且效果很好,并且我同意可以以不同的方式进行选择,但我试图了解为什么会发生这种行为。再次感谢。
    猜你喜欢
    • 1970-01-01
    • 2023-03-15
    • 2017-04-26
    • 1970-01-01
    • 1970-01-01
    • 2022-11-24
    • 2017-09-11
    • 2019-09-15
    • 1970-01-01
    相关资源
    最近更新 更多