在PyTorch中扩展一个非单一维度,但不复制内存中的数据?

nlejzf6q  于 2022-11-29  发布在  其他
关注(0)|答案(1)|浏览(190)

假设我们有一个大小为[a,b,c]的Tensors,它不一定是连续的,并且b>>1
我想把它在第二维展开(但不是复制)n次,得到一个大小为[a,nb,c]的Tensor。
问题是,如果不显式地复制内存中的数据,我就找不到一种方法来执行此操作。

s.repeat_interleave(n,dim=1)
s.unsqueeze(-2).expand(-1,-1,n,-1).contiguous().view([a,-1,c])
s.unsqueeze(-2).expand(-1,-1,n,-1).reshape([a,-1,c])

将执行复制步骤,并显著降低速度。
有人知道解决办法吗?
提前感谢!

vof42yt1

vof42yt11#

我认为这是不可能的,这里有一个最小的例子来说明我的观点。
考虑一个 Torch 。Tensor[1, 2, 3],大小为(3,)。如果我们想在不执行复制的情况下扩展它,我们将创建一个新的Tensorview。例如,假设我们想创建一个包含原始Tensor值两倍的视图。即[1, 2, 3, 1, 2, 3],其大小为(2*3,)。但是,不可能定义仅使用步幅的视图,原因如下:为了从1步进到2,或者从2步进到3,我们需要步幅值为1。但是为了从3步进到1,我们需要步幅值为-2,但是所实现的步幅系统对于给定轴不能具有不同的值。
我不能100%肯定这是不可能的。也许存在一个非常聪明的技巧,通过使用torch.as_strided()函数的storage_offset参数,或其他东西。而且,也许这个功能将在未来的版本中实现,例如,如果你试图设置一个负的步幅值,你会有错误

>>> torch.as_strided(torch.tensor([1, 2, 3]), size=(1,), stride=(-1,))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
RuntimeError: as_strided: Negative strides are not supported at the moment, got strides: [-1]

这表明这个功能将来可能会改变(这里我使用了pytorch1.13.0版)。
有人可能会说,你可以先用torch.Tensor.expand()展开一个新的维度,然后用flatten()得到结果,但这是行不通的,让我来解释为什么。在expand()的文档中,解释了这个函数返回Tensor的新的view(所以这并不做任何复制),在flatten()的文档中,解释了这个函数将尝试返回展平Tensor的视图,如果不可能,它将返回一个副本。那么让我们试试这个方法,并使用tensor.storage().nbytes()检查每一步Tensor的存储器大小:

>>> a = torch.tensor([1, 2, 3])
>>> print(a)
tensor([1, 2, 3])
>>> print(a.storage().nbytes())
24
>>> b = a.expand([2, 3])
>>> print(b)
tensor([[1, 2, 3],
        [1, 2, 3]])
>>> print(b.storage().nbytes())
24
>>> c = b.flatten()
>>> print(c)
tensor([1, 2, 3, 1, 2, 3])
>>> print(c.storage().nbytes())
48

正如我们所看到的,flatten()似乎不能返回展平Tensor的视图,因为c占用的内存是a的两倍。如果flatten()的pyTorch实现不能做到这一点,这可能意味着它确实不可能做到。

相关问题