前言
原文地址:https://discuss.pytorch.org/t/contigious-vs-non-contigious-tensor/30107
正文
Contiguous是一个标志,指示张量在内存中是否连续存储。
让我们举个例子来看看,我们如何得到一个非连续的张量。
# 创建一个形状是[4, 3]的张量
x = torch.arange(12).view(4, 3)
print(x, x.stride())
> tensor([[ 0, 1, 2],
[ 3, 4, 5],
[ 6, 7, 8],
[ 9, 10, 11]])
# 步幅,stride
> (3, 1)
如你所见,张量具有所要的形状,也就是[4, 3]。步幅(stride)也很有趣,它告诉我们,我们在内存中需要跳过多少个位置(step),才可以沿着某个轴(axis)移动到下一个位置。
如果我们看一下步幅(stride),我们会看到,我们必须跳过3个值才能转到新行,而只需跳过1个值就能转到下一列。
到目前为止都很好理解。
这些值按顺序存储在内存中,即内存单元应将数据[0,1,2,3,…,11]保存到连续地址空间。
现在让我们转置张量,并再次看一下步幅:
y = x.t() # 转置
print(y, y.stride())
print(y.is_contiguous())
> tensor([[ 0, 3, 6, 9],
[ 1, 4, 7, 10],
[ 2, 5, 8, 11]])
> (1, 3) # 步幅
> False # 是否连续
print 语句正确地输出了张量的x的转置视图。
但是,步幅现在已经互换了。
为了转到下一行,我们只需要跳过1个值,而转到下一列需要跳过3个值。
这也说得通,如果我们回想一下张量在内存中的布局:
注意:转置并没有让内存中的布局发生改变。
[0, 1, 2, 3, 4, ..., 11]
为了移动到下一列(例如,从0到3,我们必须跳过3个值。
因此,张量不再是不连续的!
这对我们来说并不是一个真正的问题,除了某些操作不起作用。
例如,如果我们尝试获得 y 的扁平化视图,我们将遇到运行时错误:
try:
y = y.view(-1) # 希望的到y的一维向量试图
except RuntimeError as e:
print(e)
> invalid argument 2: view size is not compatible with input tensor's size and stride (at least one dimension spans across two contiguous subspaces). Call .contiguous() before .view().
So let’s call .contiguous() before the view call:
y = y.contiguous() # 显示调用contiguous方法
>>> y
tensor([[ 0, 3, 6, 9],
[ 1, 4, 7, 10],
[ 2, 5, 8, 11]])
print(y.stride())
> (4, 1)
>>> y.view(-1)
tensor([ 0, 3, 6, 9, 1, 4, 7, 10, 2, 5, 8, 11])
现在,内存布局再次是连续的(看看步幅),view工作正常。
我不完全确定,但我假设调用contiguous会调用复制操作让内存再次连续。
也就是说,连续数组对于某些矢量化指令工作是必要的前提条件。
调用contiguous会导致内存复制操作吗?
如果在非连续张量上调用 contiguous,则将执行复制,否则,这将是一个没有影响的操作。