通过源代码的进一步挖掘,似乎只有
view
函数在传递非连续输入时明确引发异常。
人们会期望使用张量视图的任何操作都有可能因为非连续输入而失败。实际上,似乎大多数或所有这些函数:
(a.) 支持非连续块实现(见下面的示例),即张量迭代器可以处理指向内存中各个数据块的多个指针,但可能会牺牲性能,或者
(b.) 调用.contiguous()
包装操作(一个这样的例子显示在这里,是对torch.tensor.diagflat()
的调用)。reshape
本质上是view
的contiguous()
包装形式。
通过扩展,似乎
view
相对于
reshape
的主要优点就是当张量意外地不连续时,可以显式地触发异常,而代码会默默地处理这种差异,以性能为代价。
这个结论基于以下几点:
- 测试了所有具有非连续输入的 Tensor View 操作。
- 源代码分析了其他感兴趣的非 Tensor View 函数(例如 Conv1D),其中包括在所有非平凡输入情况下必要的调用
contiguous
。
- 从 PyTorch 的设计哲学中推断出来,它是一种简单、有时慢速、易于使用的语言。
- 在 Pytorch Discuss 上进行了交叉发布。
- 广泛审查了涉及非连续错误的 Web 报告错误,所有这些错误都围绕着对
view
的问题调用。
我没有全面测试所有 PyTorch 函数,因为有成千上万个函数。
(a.)的例子:
import torch
import numpy
import time
start = time.time()
test = torch.rand([10000,1000,100])
torch.cuda.synchronize()
end = time.time()
print("Allocation took {} sec. Data is at address {}. Contiguous:
{}".format(end -
start,test.storage().data_ptr(),test.is_contiguous()))
start = time.time()
test.view(-1)
torch.cuda.synchronize()
end = time.time()
print("view() took {} sec. Data is at address {}. Contiguous:
{}".format(end -
start,test.storage().data_ptr(),test.is_contiguous()))
start = time.time()
test.diagonal()
torch.cuda.synchronize()
end = time.time()
print("diagonal() took {} sec. Data is at address {}. Contiguous:
{}".format(end -
start,test.storage().data_ptr(),test.is_contiguous()))
test = test[::2,::2,::2]
resulting in a non-contiguous output
print(test.is_contiguous())
start = time.time()
test = test.unsqueeze(-1).expand([test.shape[0],test.shape[1],test.shape[2],100]).diagonal()
torch.cuda.synchronize()
end = time.time()
print("non-contiguous tensor ops() took {} sec. Data is at
address {}. Contiguous: {}".format(end -
start,test.storage().data_ptr(),test.is_contiguous()))
start = time.time()
test = test.reshape(-1) + 1.0
torch.cuda.synchronize()
end = time.time()
print("reshape() took {} sec. Data is at address {}. Contiguous: {}".format(end - start,test.storage().data_ptr(),test.is_contiguous()))
以下是输出内容:
Allocation took 4.269254922866821 sec. Data is at address 139863636672576. Contiguous: True
view() took 0.0002810955047607422 sec. Data is at address 139863636672576. Contiguous: True
diagonal() took 6.532669067382812e-05 sec. Data is at address 139863636672576. Contiguous: True
False
non-contiguous tensor ops() took 0.00011277198791503906 sec. Data is at address 139863636672576. Contiguous: False
reshape() took 0.13828253746032715 sec. Data is at address 94781254337664. Contiguous: True
在第4个块中,有一些张量视图操作是在一个非连续的输入张量上执行的。这些操作能够正常运行,并且保持数据在相同的内存地址中,比需要将数据复制到新的内存地址(例如第5个块中的
reshape
)的操作运行得更快。因此,看起来这些操作是以一种处理非连续输入而不需要数据复制的方式实现的。
view
和reshape
作为例子。还有什么?这就是我的问题。我不是在问不支持多维输入的操作或其他类似的情况。这些都有很好的文档说明。 - Albertreshape
明确不需要连续的输入,它是一种惰性复制操作,如果可能的话执行与view
相同的功能,否则将数据复制到新的张量中。 - DerekGview
需要连续输入,那么这不是正确的吗?现在您改变了答案,但它并没有真正回答我的问题。有哪些操作、函数或模块需要连续输入?您能给出一些例子吗? - Albert