哪些功能或模块需要连续输入?
What functions or modules require contiguous input?
据我了解,只要某个函数或模块需要连续张量,您就需要显式调用 tensor.contiguous()
。否则你会得到像这样的异常:
RuntimeError: invalid argument 1: input is not contiguous at .../src/torch/lib/TH/generic/THTensor.c:231
(例如 。)
哪些功能或模块需要连续输入?是否有记录?
或者换句话说,什么情况下需要调用contiguous
?
例如Conv1d
,是否需要连续输入?文档没有提到这一点。当文档没有提到这一点时,这总是暗示它不需要连续输入?
(我记得在 Theano 中,任何获得一些非连续输入的操作都会自动转换它,这要求它是连续的。)
来自 pytorch 文档:contiguous() → Tensor。 Returns 包含与自身张量相同数据的连续张量。如果自张量是连续的,则此函数 returns 自张量。
我认为没有完整的列表。这取决于您如何实现张量处理函数。
如果您查看有关编写 C++ and CUDA extensions 的教程,您会发现典型的 pytorch CUDA 操作如下所示:
- 具有
torch::Tensor
个参数的 C++ 接口。 class 为 access/manipulate 张量数据提供 API。
- 具有
float*
个参数的 CUDA 内核。这些指针直接指向存储张量数据的内存
显然,用指针处理张量中的数据比处理张量的 API 效率更高 class。但最好处理具有连续内存布局(或至少是常规布局)的指针。
我相信原则上即使没有连续数据也可以用指针操作数据,如果提供足够的内存布局信息的话。但是你必须考虑各种布局,代码可能会更加乏味。
Facebook 可能有一些技巧可以让一些内置操作处理非连续数据(我对此不太了解),但大多数自定义扩展模块要求输入是连续的。
通过 source_code 进一步深入挖掘后,似乎 view
是唯一在传递非连续输入时明确导致异常的函数.
人们会期望 any operation using Tensor Views
有可能因非连续输入而失败。实际上,这些功能中的大部分或全部似乎是这样的:
(a.) 实现时支持非连续块(参见下面的示例),即张量迭代器可以处理指向内存中不同数据块的多个指针,这可能会牺牲性能,否则
(b.) 对 .contiguous()
的调用包装了操作(一个这样的例子显示 here for torch.tensor.diagflat()
)。 reshape
本质上是 contiguous()
-wrapped 形式的 view
.
通过扩展,view
相对于 reshape
的主要好处似乎是当张量意外不连续时的显式异常与代码以性能为代价静默处理这种差异。
此结论基于:
- 使用非连续输入测试所有 Tensor View 操作。
- 其他感兴趣的非张量视图函数的源代码分析(例如 Conv1D,其中包括在所有非平凡输入情况下必要时对
contiguous
的调用)。
- 从 pytorch 的设计理念推断为一种简单、有时缓慢、易于使用的语言。
- 在 Pytorch Discuss 上交叉发布。
- 广泛审查网络报告的涉及非连续错误的错误,所有这些错误都围绕着对
view
. 的有问题的调用
我没有全面测试所有pytorch函数,因为有数千个。
(a.) 的示例:
import torch
import numpy
import time
# allocation
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()))
# view of a contiguous tensor
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()))
# diagonal() on a contiguous tensor
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()))
# Diagonal and a few tensor view ops on a non-contiguous tensor
test = test[::2,::2,::2] # indexing is a Tensor View op
resulting in a non-contiguous output
print(test.is_contiguous()) # False
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()))
# reshape, which requires a tensor copy operation to new memory
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
)运行得相对更快。因此,这些操作似乎是以一种无需数据副本即可处理非连续输入的方式实现的。
据我了解,只要某个函数或模块需要连续张量,您就需要显式调用 tensor.contiguous()
。否则你会得到像这样的异常:
RuntimeError: invalid argument 1: input is not contiguous at .../src/torch/lib/TH/generic/THTensor.c:231
(例如
哪些功能或模块需要连续输入?是否有记录?
或者换句话说,什么情况下需要调用contiguous
?
例如Conv1d
,是否需要连续输入?文档没有提到这一点。当文档没有提到这一点时,这总是暗示它不需要连续输入?
(我记得在 Theano 中,任何获得一些非连续输入的操作都会自动转换它,这要求它是连续的。)
来自 pytorch 文档:contiguous() → Tensor。 Returns 包含与自身张量相同数据的连续张量。如果自张量是连续的,则此函数 returns 自张量。
我认为没有完整的列表。这取决于您如何实现张量处理函数。
如果您查看有关编写 C++ and CUDA extensions 的教程,您会发现典型的 pytorch CUDA 操作如下所示:
- 具有
torch::Tensor
个参数的 C++ 接口。 class 为 access/manipulate 张量数据提供 API。 - 具有
float*
个参数的 CUDA 内核。这些指针直接指向存储张量数据的内存
显然,用指针处理张量中的数据比处理张量的 API 效率更高 class。但最好处理具有连续内存布局(或至少是常规布局)的指针。
我相信原则上即使没有连续数据也可以用指针操作数据,如果提供足够的内存布局信息的话。但是你必须考虑各种布局,代码可能会更加乏味。
Facebook 可能有一些技巧可以让一些内置操作处理非连续数据(我对此不太了解),但大多数自定义扩展模块要求输入是连续的。
通过 source_code 进一步深入挖掘后,似乎 view
是唯一在传递非连续输入时明确导致异常的函数.
人们会期望 any operation using Tensor Views 有可能因非连续输入而失败。实际上,这些功能中的大部分或全部似乎是这样的:
(a.) 实现时支持非连续块(参见下面的示例),即张量迭代器可以处理指向内存中不同数据块的多个指针,这可能会牺牲性能,否则
(b.) 对 .contiguous()
的调用包装了操作(一个这样的例子显示 here for torch.tensor.diagflat()
)。 reshape
本质上是 contiguous()
-wrapped 形式的 view
.
通过扩展,view
相对于 reshape
的主要好处似乎是当张量意外不连续时的显式异常与代码以性能为代价静默处理这种差异。
此结论基于:
- 使用非连续输入测试所有 Tensor View 操作。
- 其他感兴趣的非张量视图函数的源代码分析(例如 Conv1D,其中包括在所有非平凡输入情况下必要时对
contiguous
的调用)。 - 从 pytorch 的设计理念推断为一种简单、有时缓慢、易于使用的语言。
- 在 Pytorch Discuss 上交叉发布。
- 广泛审查网络报告的涉及非连续错误的错误,所有这些错误都围绕着对
view
. 的有问题的调用
我没有全面测试所有pytorch函数,因为有数千个。
(a.) 的示例:
import torch
import numpy
import time
# allocation
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()))
# view of a contiguous tensor
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()))
# diagonal() on a contiguous tensor
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()))
# Diagonal and a few tensor view ops on a non-contiguous tensor
test = test[::2,::2,::2] # indexing is a Tensor View op
resulting in a non-contiguous output
print(test.is_contiguous()) # False
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()))
# reshape, which requires a tensor copy operation to new memory
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
)运行得相对更快。因此,这些操作似乎是以一种无需数据副本即可处理非连续输入的方式实现的。