我有一个多维张量,让我们以这个简单的张量为例:
out = torch.Tensor(3, 4, 5)
我必须获取该张量out[:,0,:]
的一部分/子部分,然后应用方法view(-1)
,但这是不可能的:
out[:,0,:].view(-1)
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 ../aten/src/TH/generic/THTensor.cpp:203
一种解决方案是克隆子部分:
out[:,0,:].clone().view(-1)
有比克隆更好/更快的解决方案吗?
答案 0 :(得分:2)
您所做的一切都会很好。也就是说,一种更可移植的方法是使用reshape
,它将在可能时返回视图,但是在必要时将创建连续的副本。这样,它将做最快的事情。根据您的情况,必须复制数据,但是始终使用reshape
有时将无法生成副本。
因此您可以使用
out[:,0,:].reshape(-1)
Gotcha
这里有一个重要的陷阱。如果对reshape
的输出执行就地操作,则可能会或不会影响原始张量,具体取决于是否返回视图或副本。
例如,假设out
已经连续,那么在这种情况下
>>> x = out[:,0,:].reshape(-1) # returns a copy
>>> x[0] = 10
>>> print(out[0,0,0].item() == 10)
False
x
是副本,因此对其所做的更改不会影响out
。但是在这种情况下
>>> x = out[:,:,0].reshape(-1) # returns a view
>>> x[0] = 10
>>> print(out[0,0,0].item() == 10)
True
x
是一个视图,因此对x
的就地更改也会更改out
。
替代
几个替代方法是
out[:,0,:].flatten() # .flatten is just a special case of .reshape
和
out[:,0,:].contiguous().view(-1)
尽管如果您想要最快的方法,我建议您使用contiguous().view
后一种方法,因为通常来说,它比reshape
或flatten
更有可能返回副本。这是因为contiguous
将创建一个副本,即使基础数据在后续条目之间具有相同数量的字节。因此,两者之间是有区别的
out[:,:,0].contiguous().view(-1) # creates a copy
和
out[:,:,0].flatten() # creates a non-contiguous view (b/c underlying data has uniform spacing of out.shape[2] values between entries)
由于contiguous().view
不连续,因此out[:,:,0]
方法强制执行复制,但是flatten
/ reshape
将创建视图,因为基础数据是均匀间隔的。
有时候contiguous()
不会创建副本,例如比较
out[0,:,:].contiguous().view(-1) # creates a view b/c out[0,:,:] already is contiguous
和
out[0,:,:].flatten() # creates a view
由于out[0,:,:]
已经是连续的,因此它们都可以生成原始数据的视图而无需复制。
如果要确保out
与扁平化的对象完全分离,那么使用.clone()
的原始方法是可行的方法。