blu*_*nox 52 neural-network contiguous deep-learning lstm pytorch
我在github上看到了这个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())
Run Code Online (Sandbox Code Playgroud)
举个简单的例子,当使用批量大小为1和seq_length
10时inputs
,targets
看起来像这样:
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]
Run Code Online (Sandbox Code Playgroud)
所以一般来说我的问题是,contiguous()
我需要什么以及为什么需要它?
此外,我不明白为什么该方法被调用目标序列而不是输入序列,因为两个变量都包含相同的数据.
怎么可能targets
是inputs
不连续的,仍然是连续的?
编辑:
我试图忽略调用contiguous()
,但这会在计算损失时导致错误消息.
RuntimeError: invalid argument 1: input is not contiguous at .../src/torch/lib/TH/generic/THTensor.c:231
Run Code Online (Sandbox Code Playgroud)
所以显然contiguous()
在这个例子中调用是必要的.
(为了保持这种可读性,我避免在这里发布完整代码,可以使用上面的GitHub链接找到它.)
提前致谢!
Shi*_*hah 119
在PyTorch中Tensor上的操作很少,它们并没有真正改变张量的内容,而只是如何将索引转换为张量到字节的位置.这些操作包括:
narrow()
,view()
,expand()
和transpose()
例如:当你调用时transpose()
,PyTorch不会生成具有新布局的新张量,它只是修改Tensor对象中的元信息,因此偏移和步幅是新形状.转置张量和原始张量确实共享记忆!
x = torch.randn(3,2)
y = torch.transpose(x, 0, 1)
x[0, 0] = 42
print(y[0,0])
# prints 42
Run Code Online (Sandbox Code Playgroud)
这就是连续概念的用武之地.上面x
是连续的,但y
不是因为它的内存布局不同于从头开始制作的相同形状的张量.请注意,单词"contiguous"有点误导,因为它不是张量的内容在断开的内存块周围展开.这里的字节仍然分配在一个内存块中,但元素的顺序是不同的!
当你调用时contiguous()
,它实际上会生成张量的副本,因此元素的顺序与从头开始创建的相同形状的张量相同.
通常你不需要担心这个.如果PyTorch期望连续张量,但如果不是那么你会得到RuntimeError: input is not contiguous
,然后你只需要添加一个调用contiguous()
.
Keh*_*eho 65
如果一维数组的[0, 1, 2, 3, 4]
项在内存中彼此相邻排列,则该一维数组是连续的,如下所示:
如果存储它的内存区域如下所示,则它不是连续的:
对于二维或更多维数组,项目也必须彼此相邻,但顺序遵循不同的约定。让我们考虑下面的二维数组:
>>> t = torch.tensor([[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11]])
Run Code Online (Sandbox Code Playgroud)
如果行像这样彼此相邻存储,则内存分配是C 连续的:
这就是 PyTorch 认为是连续的。
>>> t.is_contiguous()
True
Run Code Online (Sandbox Code Playgroud)
PyTorch 的 Tensor 类方法stride()
给出了要跳过以获取每个维度中的下一个元素的字节数
>>> t.stride()
(4, 1)
Run Code Online (Sandbox Code Playgroud)
我们需要跳过 4 个字节才能转到下一行,但只需要跳过一个字节即可转到同一行中的下一个元素。
正如其他答案中所述,某些 Pytorch 操作不会更改内存分配,只会更改元数据。
例如转置方法。让我们转置张量:
内存分配没有改变:
但大步却做到了:
>>> t.T.stride()
(1, 4)
Run Code Online (Sandbox Code Playgroud)
我们需要跳过 1 个字节才能转到下一行,需要跳过 4 个字节才能转到同一行中的下一个元素。张量不再是 C 连续的(实际上是Fortran 连续的:每列彼此相邻存储)
>>> t.T.is_contiguous()
False
Run Code Online (Sandbox Code Playgroud)
contiguous()
将重新安排内存分配,使张量是 C 连续的:
>>> t.T.contiguous().stride()
(3, 1)
Run Code Online (Sandbox Code Playgroud)
一些操作,例如reshape()
和view()
,会对底层数据的连续性产生不同的影响。
pat*_*_ai 25
连续的()→张量
Run Code Online (Sandbox Code Playgroud)Returns a contiguous tensor containing the same data as self
张量.如果自张量是连续的,则此函数返回自张量.
凡contiguous
在这里是指在内存中是连续的.因此该contiguous
函数根本不会影响目标张量,它只是确保它存储在连续的内存块中.
小智 6
tensor.contiguous()将创建张量的副本,并且副本中的元素将以连续方式存储在内存中。当我们首先对张量进行transpose()并对其进行整形(查看)时,通常需要contiguous()函数。首先,让我们创建一个连续的张量:
aaa = torch.Tensor( [[1,2,3],[4,5,6]] )
print(aaa.stride())
print(aaa.is_contiguous())
#(3,1)
#True
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
好的,我们可以发现transpose(),narrow()和张量切片以及expand()将使生成的张量不连续。有趣的是,repeat()和view()不会使其不连续。所以现在的问题是:如果我使用不连续张量会怎样?
答案是view()函数不能应用于不连续的张量。这可能是因为view()要求将张量连续存储,以便它可以在内存中进行快速整形。例如:
bbb.view(-1,3)
Run Code Online (Sandbox Code Playgroud)
我们将得到错误:
---------------------------------------------------------------------------
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
Run Code Online (Sandbox Code Playgroud)
为了解决这个问题,只需将contiguous()添加到不连续的张量中,以创建连续的副本,然后应用view()
bbb.contiguous().view(-1,3)
#tensor([[1., 4., 2.],
[5., 3., 6.]])
Run Code Online (Sandbox Code Playgroud)
接受的答案太好了,我试图欺骗transpose()
功能效果。我创建了两个可以检查samestorage()
和 的函数contiguous
。
def samestorage(x,y):
if x.storage().data_ptr()==y.storage().data_ptr():
print("same storage")
else:
print("different storage")
def contiguous(y):
if True==y.is_contiguous():
print("contiguous")
else:
print("non contiguous")
Run Code Online (Sandbox Code Playgroud)
我检查并得到了这个结果作为表格:
您可以查看下面的检查器代码,但让我们举一个当张量不连续时的示例。我们不能简单地调用view()
那个张量,我们需要reshape()
它或者我们也可以调用.contiguous().view()
.
x = torch.randn(3,2)
y = x.transpose(0, 1)
y.view(6) # RuntimeError: view size is not compatible with input tensor's size and stride (at least one dimension spans across two contiguous subspaces). Use .reshape(...) instead.
x = torch.randn(3,2)
y = x.transpose(0, 1)
y.reshape(6)
x = torch.randn(3,2)
y = x.transpose(0, 1)
y.contiguous().view(6)
Run Code Online (Sandbox Code Playgroud)
还要注意的是,有些方法最终会创建连续和不连续的张量。有些方法可以对同一个存储进行操作,有些方法flip()
会在返回之前创建一个新的存储(读取:克隆张量)。
检查器代码:
import torch
x = torch.randn(3,2)
y = x.transpose(0, 1) # flips two axes
print("\ntranspose")
print(x)
print(y)
contiguous(y)
samestorage(x,y)
print("\nnarrow")
x = torch.randn(3,2)
y = x.narrow(0, 1, 2) #dim, start, len
print(x)
print(y)
contiguous(y)
samestorage(x,y)
print("\npermute")
x = torch.randn(3,2)
y = x.permute(1, 0) # sets the axis order
print(x)
print(y)
contiguous(y)
samestorage(x,y)
print("\nview")
x = torch.randn(3,2)
y=x.view(2,3)
print(x)
print(y)
contiguous(y)
samestorage(x,y)
print("\nreshape")
x = torch.randn(3,2)
y = x.reshape(6,1)
print(x)
print(y)
contiguous(y)
samestorage(x,y)
print("\nflip")
x = torch.randn(3,2)
y = x.flip(0)
print(x)
print(y)
contiguous(y)
samestorage(x,y)
print("\nexpand")
x = torch.randn(3,2)
y = x.expand(2,-1,-1)
print(x)
print(y)
contiguous(y)
samestorage(x,y)
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
30738 次 |
最近记录: |