简体   繁体   English

PyTorch 重塑张量维度

[英]PyTorch reshape tensor dimension

I want to reshape a vector of shape (5,) into a matrix of shape (1, 5) .我想将形状(5,)的向量重塑为形状(1, 5)的矩阵。

With numpy, I can do:使用 numpy,我可以做到:

>>> import numpy as np
>>> a = np.array([1, 2, 3, 4, 5])
>>> a.shape
(5,)
>>> a = np.reshape(a, (1, 5))
>>> a.shape
(1, 5)
>>> a
array([[1, 2, 3, 4, 5]])

But how do I do this with PyTorch?但是我如何使用 PyTorch 做到这一点?

Use torch.unsqueeze(input, dim, out=None) :使用torch.unsqueeze(input, dim, out=None)

>>> import torch
>>> a = torch.Tensor([1, 2, 3, 4, 5])
>>> a

 1
 2
 3
 4
 5
[torch.FloatTensor of size 5]

>>> a = a.unsqueeze(0)
>>> a

 1  2  3  4  5
[torch.FloatTensor of size 1x5]

you might use你可能会使用

a.view(1,5)
Out: 

 1  2  3  4  5
[torch.FloatTensor of size 1x5]

There are multiple ways of reshaping a PyTorch tensor.有多种方法可以重塑 PyTorch 张量。 You can apply these methods on a tensor of any dimensionality.您可以将这些方法应用于任何维度的张量。

Let's start with a 2-dimensional 2 x 3 tensor:让我们从一个二维2 x 3张量开始:

x = torch.Tensor(2, 3)
print(x.shape)
# torch.Size([2, 3])

To add some robustness to this problem, let's reshape the 2 x 3 tensor by adding a new dimension at the front and another dimension in the middle, producing a 1 x 2 x 1 x 3 tensor.为了给这个问题增加一些鲁棒性,让我们通过在前面添加一个新维度并在中间添加另一个维度来重塑2 x 3张量,从而产生一个1 x 2 x 1 x 3张量。

Approach 1: add dimension with None方法 1:使用None添加维度

Use NumPy-style insertion of None (aka np.newaxis ) to add dimensions anywhere you want.使用 NumPy 样式None插入(又名np.newaxis )在任何你想要的地方添加维度 See here .这里

print(x.shape)
# torch.Size([2, 3])

y = x[None, :, None, :] # Add new dimensions at positions 0 and 2.
print(y.shape)
# torch.Size([1, 2, 1, 3])

Approach 2: unsqueeze方法2:解压

Use torch.Tensor.unsqueeze(i) (aka torch.unsqueeze(tensor, i) or the in-place version unsqueeze_() ) to add a new dimension at the i'th dimension.使用torch.Tensor.unsqueeze(i) (又名torch.unsqueeze(tensor, i)或就地版本unsqueeze_() )在第 i 个维度添加一个新维度。 The returned tensor shares the same data as the original tensor.返回的张量与原始张量共享相同的数据。 In this example, we can use unqueeze() twice to add the two new dimensions.在这个例子中,我们可以使用unqueeze()两次来添加两个新维度。

print(x.shape)
# torch.Size([2, 3])

# Use unsqueeze twice.
y = x.unsqueeze(0) # Add new dimension at position 0
print(y.shape)
# torch.Size([1, 2, 3])

y = y.unsqueeze(2) # Add new dimension at position 2
print(y.shape)
# torch.Size([1, 2, 1, 3])

In practice with PyTorch, adding an extra dimension for the batch may be important, so you may often see unsqueeze(0) .在 PyTorch 的实践中, 为批处理添加额外的维度可能很重要,因此您可能经常会看到unsqueeze(0)

Approach 3: view方法三:查看

Use torch.Tensor.view(*shape) to specify all the dimensions.使用torch.Tensor.view(*shape)指定所有尺寸。 The returned tensor shares the same data as the original tensor.返回的张量与原始张量共享相同的数据。

print(x.shape)
# torch.Size([2, 3])

y = x.view(1, 2, 1, 3)
print(y.shape)
# torch.Size([1, 2, 1, 3])

Approach 4: reshape方法四:重塑

Use torch.Tensor.reshape(*shape) (aka torch.reshape(tensor, shapetuple) ) to specify all the dimensions.使用torch.Tensor.reshape(*shape) (又名torch.reshape(tensor, shapetuple) )指定所有维度。 If the original data is contiguous and has the same stride, the returned tensor will be a view of input (sharing the same data), otherwise it will be a copy.如果原始数据是连续的并且具有相同的步幅,则返回的张量将是输入的视图(共享相同的数据),否则将是副本。 This function is similar to the NumPy reshape() function in that it lets you define all the dimensions and can return either a view or a copy.此函数类似于 NumPy reshape()函数,因为它允许您定义所有维度并可以返回视图或副本。

print(x.shape)
# torch.Size([2, 3])

y = x.reshape(1, 2, 1, 3)
print(y.shape)
# torch.Size([1, 2, 1, 3])

Furthermore, from the O'Reilly 2019 book Programming PyTorch for Deep Learning , the author writes:此外,作者在 O'Reilly 2019 年出版的Programming PyTorch for Deep Learning中写道:

Now you might wonder what the difference is between view() and reshape() .现在您可能想知道view()reshape()之间有什么区别。 The answer is that view() operates as a view on the original tensor, so if the underlying data is changed, the view will change too (and vice versa).答案是view()作为原始张量上的视图运行,因此如果基础数据发生更改,视图也会更改(反之亦然)。 However, view() can throw errors if the required view is not contiguous;但是,如果所需的视图不连续, view()可能会抛出错误; that is, it doesn't share the same block of memory it would occupy if a new tensor of the required shape was created from scratch.也就是说,如果从头开始创建所需形状的新张量,它不会共享相同的内存块。 If this happens, you have to call tensor.contiguous() before you can use view() .如果发生这种情况,您必须先调用tensor.contiguous()才能使用view() However, reshape() does all that behind the scenes, so in general, I recommend using reshape() rather than view() .但是, reshape()会在幕后完成所有这些工作,所以总的来说,我建议使用reshape()而不是view()

Approach 5: resize_方法5:resize_

Use the in-place function torch.Tensor.resize_(*sizes) to modify the original tensor.使用就地函数torch.Tensor.resize_(*sizes)修改原始张量。 The documentation states:该文档指出:

WARNING.警告。 This is a low-level method.这是一种低级方法。 The storage is reinterpreted as C-contiguous, ignoring the current strides (unless the target size equals the current size, in which case the tensor is left unchanged).存储被重新解释为 C 连续,忽略当前步幅(除非目标大小等于当前大小,在这种情况下张量保持不变)。 For most purposes, you will instead want to use view() , which checks for contiguity, or reshape() , which copies data if needed.在大多数情况下,您将改为使用view()来检查连续性,或者reshape()来在需要时复制数据。 To change the size in-place with custom strides, see set_() .要使用自定义步幅就地更改大小,请参阅set_()

print(x.shape)
# torch.Size([2, 3])

x.resize_(1, 2, 1, 3)
print(x.shape)
# torch.Size([1, 2, 1, 3])

My observations我的观察

If you want to add just one dimension (eg to add a 0th dimension for the batch), then use unsqueeze(0) .如果您只想添加一个维度(例如为批次添加第 0 个维度),请使用unsqueeze(0) If you want to totally change the dimensionality, use reshape() .如果您想完全改变维度,请使用reshape()

See also:也可以看看:

What's the difference between reshape and view in pytorch? pytorch中的reshape和view有什么区别?

What is the difference between view() and unsqueeze()? view() 和 unsqueeze() 有什么区别?

In PyTorch 0.4, is it recommended to use reshape than view when it is possible? 在 PyTorch 0.4 中,是否建议在可能的情况下使用reshape而不是view

For in-place modification of the shape of the tensor, you should use tensor.resize_() :对于张量形状的就地修改,您应该使用tensor.resize_()

In [23]: a = torch.Tensor([1, 2, 3, 4, 5])

In [24]: a.shape
Out[24]: torch.Size([5])


# tensor.resize_((`new_shape`))    
In [25]: a.resize_((1,5))
Out[25]: 

 1  2  3  4  5
[torch.FloatTensor of size 1x5]

In [26]: a.shape
Out[26]: torch.Size([1, 5])

In PyTorch, if there's an underscore at the end of an operation (like tensor.resize_() ) then that operation does in-place modification to the original tensor.在 PyTorch 中,如果操作末尾有下划​​线(如tensor.resize_() ),则该操作会对原始张量in-place修改。


Also, you can simply use np.newaxis in a torch Tensor to increase the dimension.此外,您可以简单地在火炬张量中使用np.newaxis来增加维度。 Here is an example:这是一个例子:

In [34]: list_ = range(5)
In [35]: a = torch.Tensor(list_)
In [36]: a.shape
Out[36]: torch.Size([5])

In [37]: new_a = a[np.newaxis, :]
In [38]: new_a.shape
Out[38]: torch.Size([1, 5])

or you can use this, the '-1' means you don't have to specify the number of the elements.或者你可以使用它,'-1' 意味着你不必指定元素的数量。

In [3]: a.view(1,-1)
Out[3]:

 1  2  3  4  5
[torch.FloatTensor of size 1x5]

This question has been thoroughly answered already, but I want to add for the less experienced python developers that you might find the * operator helpful in conjunction with view() .这个问题已经得到了彻底的回答,但是我想为经验不足的 python 开发人员补充一点,您可能会发现*运算符与view()结合使用很有帮助。

For example if you have a particular tensor size that you want a different tensor of data to conform to, you might try:例如,如果您有一个特定的张量大小,您希望不同的数据张量符合,您可以尝试:

img = Variable(tensor.randn(20,30,3)) # tensor with goal shape
flat_size = 20*30*3
X = Variable(tensor.randn(50, flat_size)) # data tensor

X = X.view(-1, *img.size()) # sweet maneuver
print(X.size()) # size is (50, 20, 30, 3)

This works with numpy shape too:这也适用于 numpy shape

img = np.random.randn(20,30,3)
flat_size = 20*30*3
X = Variable(tensor.randn(50, flat_size))
X = X.view(-1, *img.shape)
print(X.size()) # size is (50, 20, 30, 3)

torch.reshape() is made to dupe the numpy reshape method.torch.reshape()用于欺骗numpy reshape方法。

It came after the view() and torch.resize_() and it is inside the dir(torch) package.它出现在view()torch.resize_()之后,它位于dir(torch)包中。

import torch
x=torch.arange(24)
print(x, x.shape)
x_view = x.view(1,2,3,4) # works on is_contiguous() tensor
print(x_view.shape)
x_reshaped = x.reshape(1,2,3,4) # works on any tensor
print(x_reshaped.shape)
x_reshaped2 = torch.reshape(x_reshaped, (-1,)) # part of torch package, while view() and resize_() are not
print(x_reshaped2.shape)

Out:出去:

tensor([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17,
        18, 19, 20, 21, 22, 23]) torch.Size([24])
torch.Size([1, 2, 3, 4])
torch.Size([1, 2, 3, 4])
torch.Size([24])

But did you know it can also work as a replacement for squeeze() and unsqueeze()但是你知道它也可以作为squeeze()unsqueeze()的替代品吗?

x = torch.tensor([1, 2, 3, 4])
print(x.shape)
x1 = torch.unsqueeze(x, 0)
print(x1.shape)
x2 = torch.unsqueeze(x1, 1)
print(x2.shape)
x3=x.reshape(1,1,4)
print(x3.shape)
x4=x.reshape(4)
print(x4.shape)
x5=x3.squeeze()
print(x5.shape)

Out:出去:

torch.Size([4])
torch.Size([1, 4])
torch.Size([1, 1, 4])
torch.Size([1, 1, 4])
torch.Size([4])
torch.Size([4])
import torch
>>>a = torch.Tensor([1,2,3,4,5])
>>>a.size()
torch.Size([5])
#use view to reshape

>>>b = a.view(1,a.shape[0])
>>>b
tensor([[1., 2., 3., 4., 5.]])
>>>b.size()
torch.Size([1, 5])
>>>b.type()
'torch.FloatTensor'

As far as I know, the best way to reshape tensors is to use einops .据我所知,重塑张量的最佳方法是使用einops It solves various reshape problems by providing a simple and elegant function.它通过提供简单而优雅的功能解决了各种重塑问题。 In your situation, the code could be written as在您的情况下,代码可以写成

from einops import rearrange
ans = rearrange(tensor,'h -> 1 h')

I highly recommend you try it.我强烈建议您尝试一下。

BTW, you can use it with pytorch/tensorflow/numpy and many other libraries.顺便说一句,您可以将它与 pytorch/tensorflow/numpy 和许多其他库一起使用。

Assume the following code:假设以下代码:

import torch
import numpy as np
a = torch.tensor([1, 2, 3, 4, 5])

The following three calls have the exact same effect:以下三个调用具有完全相同的效果:

res_1 = a.unsqueeze(0)
res_2 = a.view(1, 5)
res_3 = a[np.newaxis,:]
res_1.shape == res_2.shape == res_3.shape == (1,5)  # Returns true

Notice that for any of the resulting tensors, if you modify the data in them, you are also modifying the data in a, because they don't have a copy of the data, but reference the original data in a.请注意,对于任何生成的张量,如果您修改其中的数据,您也在修改 a 中的数据,因为它们没有数据的副本,而是引用 a 中的原始数据。

res_1[0,0] = 2
a[0] == res_1[0,0] == 2  # Returns true

The other way of doing it would be using the resize_ in place operation:另一种方法是使用resize_ in place 操作:

a.shape == res_1.shape  # Returns false
a.reshape_((1, 5))
a.shape == res_1.shape # Returns true

Be careful of using resize_ or other in-place operation with autograd .小心使用resize_或其他就地操作autograd See the following discussion: https://pytorch.org/docs/stable/notes/autograd.html#in-place-operations-with-autograd请参阅以下讨论: https ://pytorch.org/docs/stable/notes/autograd.html#in-place-operations-with-autograd

import torch
t = torch.ones((2, 3, 4))
t.size()
>>torch.Size([2, 3, 4])
a = t.view(-1,t.size()[1]*t.size()[2])
a.size()
>>torch.Size([2, 12])

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM