PyTorch-contiguous()
deep-learning
lstm
neural-network
pytorch
22
0

我正在通过github (link)上的LSTM语言模型的这个例子。对我来说,它的一般功能非常清楚。但是我仍然在努力理解调用contiguous()作用,这在代码中多次发生。

例如,在代码的第74/75行中,创建了LSTM的输入和目标序列。数据(存储在ids )是二维的,其中第一维是批量大小。

for i in range(0, ids.size(1) - seq_length, seq_length):
    # Get batch inputs and targets
    inputs = Variable(ids[:, i:i+seq_length])
    targets = Variable(ids[:, (i+1):(i+1)+seq_length].contiguous())

举一个简单的例子,当使用批处理大小1和seq_length 10个inputstargets看起来像这样:

inputs Variable containing:
0     1     2     3     4     5     6     7     8     9
[torch.LongTensor of size 1x10]

targets Variable containing:
1     2     3     4     5     6     7     8     9    10
[torch.LongTensor of size 1x10]

因此,总的来说,我的问题是, contiguous()是什么?为什么需要它?

此外,我不明白为什么要针对目标序列而不是针对输入序列调用该方法,因为这两个变量都包含相同的数据。

targets怎么可能是不连续的而inputs仍然是连续的?

编辑: 我试图省去调用contiguous() ,但这在计算损失时会导致错误消息。

RuntimeError: invalid argument 1: input is not contiguous at .../src/torch/lib/TH/generic/THTensor.c:231

因此,显然在此示例中调用contiguous()是必要的。

(为了保持可读性,我避免在此处发布完整的代码,可以使用上面的GitHub链接找到它。)

提前致谢!

参考资料:
Stack Overflow
收藏
评论
共 4 个回答
高赞 时间 活跃

就像在前面的答案中contigous()分配连续的内存块一样 ,当我们将张量传递给c或c ++后端代码(其中张量作为指针传递)时,这将很有帮助

收藏
评论

pytorch文档中

contiguous()→张量

Returns a contiguous tensor containing the same data as self 

张量。如果自张量是连续的,则此函数返回自张量。

此处contiguous是指内存中连续。因此, contiguous函数根本不会影响目标张量,它只是确保将其存储在连续的内存块中。

收藏
评论

在PyTorch中,对Tensor进行的操作很少真正改变张量的内容,而仅改变如何将张量的索引转换为字节位置。这些操作包括:

narrow()view()expand()transpose()

例如:当您调用transpose() ,PyTorch不会使用新的布局生成新的张量,它只是修改Tensor对象中的元信息,因此offset和stride用于新形状。转置张量和原始张量确实共享内存!

x = torch.randn(3,2)
y = torch.transpose(x, 0, 1)
x[0, 0] = 42
print(y[0,0])
# prints 42

这是连续的概念来自英寸以上x是连续的但y不是因为它的内存布局是比从头制成相同形状的张量不同。请注意, “连续”一词有点误导,因为它不是张量的内容散布在未连接的内存块周围。这里字节仍然分配在一个内存块中,但是元素的顺序不同!

当您调用contiguous() ,它实际上会复制张量,因此元素的顺序将与从头开始创建的具有相同形状的张量相同。

通常,您无需为此担心。如果PyTorch期望连续张量,但如果不是,那么您将得到RuntimeError: input is not contiguous ,然后只需添加对contiguous()的调用即可。

收藏
评论

tensor.contiguous()将创建张量的副本,并且副本中的元素将以连续方式存储在内存中。当我们首先对张量进行transpose()并对其进行整形(查看)时,通常需要contiguous()函数。首先,让我们创建一个连续的张量:

aaa = torch.Tensor( [[1,2,3],[4,5,6]] )
print(aaa.stride())
print(aaa.is_contiguous())
#(3,1)
#True

stride()返回(3,1)的意思是:当沿着第一维移动每一步(逐行)时,我们需要在内存中移动3步。沿第二维(逐列)移动时,我们需要在内存中移动1步。这表明张量中的元素是连续存储的。

现在我们尝试将come函数应用于张量:

bbb = aaa.transpose(0,1)
print(bbb.stride())
print(bbb.is_contiguous())

ccc = aaa.narrow(1,1,2)   ## equivalent to matrix slicing aaa[:,1:3]
print(ccc.stride())
print(ccc.is_contiguous())


ddd = aaa.repeat(2,1 )   # The first dimension repeat once, the second dimension repeat twice
print(ddd.stride())
print(ddd.is_contiguous())

## expand is different from repeat  if a tensor has a shape [d1,d2,1], it can only be expanded using "expand(d1,d2,d3)", which
## means the singleton dimension is repeated d3 times
eee = aaa.unsqueeze(2).expand(2,3,3)
print(eee.stride())
print(eee.is_contiguous())

fff = aaa.unsqueeze(2).repeat(1,1,8).view(2,-1,2)
print(fff.stride())
print(fff.is_contiguous())

#(1, 3)
#False
#(3, 1)
#False
#(3, 1)
#True
#(3, 1, 0)
#False
#(24, 2, 1)
#True

好的,我们可以发现transpose(),narrow()和张量切片以及expand()将使生成的张量不连续。有趣的是,repeat()和view()不会使其不连续。所以现在的问题是: 如果我使用不连续张量会怎样?

答案是view()函数不能应用于不连续的张量。这可能是因为view()要求将张量连续存储,以便可以在内存中进行快速整形。例如:

bbb.view(-1,3)

我们将得到错误:

---------------------------------------------------------------------------
RuntimeError                              Traceback (most recent call last)
<ipython-input-63-eec5319b0ac5> in <module>()
----> 1 bbb.view(-1,3)

RuntimeError: 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(). at /pytorch/aten/src/TH/generic/THTensor.cpp:203

为了解决这个问题,只需将contiguous()添加到不连续的张量中,以创建连续的副本,然后应用view()

bbb.contiguous().view(-1,3)
#tensor([[1., 4., 2.],
        [5., 3., 6.]])
收藏
评论
新手导航
  • 社区规范
  • 提出问题
  • 进行投票
  • 个人资料
  • 优化问题
  • 回答问题

关于我们

常见问题

内容许可

联系我们

@2020 AskGo
京ICP备20001863号