Pytorch 重塑张量维度
Pytorch reshape tensor dimension
例如,我有维度为 (5) 的一维向量。我想将其重塑为二维矩阵 (1,5)。
这是我用 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]])
>>>
但是我如何使用 Pytorch 张量(和变量)做到这一点。我不想切换回 numpy 并再次切换到 Torch 变量,因为它会丢失反向传播信息。
这是我在 Pytorch 中的内容
>>> import torch
>>> from torch.autograd import Variable
>>> a = torch.Tensor([1,2,3,4,5])
>>> a
1
2
3
4
5
[torch.FloatTensor of size 5]
>>> a.size()
(5L,)
>>> a_var = variable(a)
>>> a_var = Variable(a)
>>> a_var.size()
(5L,)
.....do some calculation in forward function
>>> a_var.size()
(5L,)
现在我希望它的大小为 (1, 5)。
如何在不丢失梯度信息的情况下调整或重塑 Variable 中 pytorch 张量的维度。 (因为我会在倒退之前喂入另一个模型)
你可能会用到
a.view(1,5)
Out:
1 2 3 4 5
[torch.FloatTensor of size 1x5]
使用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]
或者你可以使用这个,'-1' 意味着你不必指定元素的数量。
In [3]: a.view(1,-1)
Out[3]:
1 2 3 4 5
[torch.FloatTensor of size 1x5]
这个问题已经得到彻底回答,但我想为经验不足的 python 开发人员补充一点,您可能会发现 *
运算符与 view()
结合使用很有用。
例如,如果您有一个特定的张量大小,您希望不同的数据张量符合,您可以尝试:
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)
这也适用于 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)
为了就地修改张量的形状,你应该使用
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])
在 PyTorch 中,如果操作末尾有下划线(如 tensor.resize_()
),则该操作会对原始张量进行 in-place
修改。
此外,您可以简单地在 torch Tensor 中使用 np.newaxis
来增加维度。这是一个例子:
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])
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'
假设以下代码:
import torch
import numpy as np
a = torch.tensor([1, 2, 3, 4, 5])
以下三个调用具有完全相同的效果:
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
请注意,对于任何生成的张量,如果您修改其中的数据,您也会修改 a 中的数据,因为它们没有数据的副本,而是引用 a 中的原始数据.
res_1[0,0] = 2
a[0] == res_1[0,0] == 2 # Returns true
另一种方法是使用 resize_
就地操作:
a.shape == res_1.shape # Returns false
a.reshape_((1, 5))
a.shape == res_1.shape # Returns true
小心使用 resize_
或其他与 autograd
的就地操作。请参阅以下讨论:https://pytorch.org/docs/stable/notes/autograd.html#in-place-operations-with-autograd
重塑 PyTorch 张量的方法有多种。您可以将这些方法应用于任何维度的张量。
让我们从二维 2 x 3
张量开始:
x = torch.Tensor(2, 3)
print(x.shape)
# torch.Size([2, 3])
为了给这个问题增加一些稳健性,让我们通过在前面添加一个新维度和在中间添加另一个维度来重塑 2 x 3
张量,生成一个 1 x 2 x 1 x 3
张量.
方法 1:使用 None
添加维度
使用 NumPy 风格 insertion of None
(aka np.newaxis
) to add dimensions anywhere you want. 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])
方法二:解压
使用 torch.Tensor.unsqueeze(i)
(a.k.a. torch.unsqueeze(tensor, i)
或就地版本 unsqueeze_()
) 在第 i 个维度添加一个新维度。 returned 张量与原始张量共享相同的数据。在此示例中,我们可以使用 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])
在使用 PyTorch 的实践中,adding an extra dimension for the batch 可能很重要,因此您可能经常看到 unsqueeze(0)
。
方法三:查看
使用torch.Tensor.view(*shape)
指定所有维度。 returned 张量与原始张量共享相同的数据。
print(x.shape)
# torch.Size([2, 3])
y = x.view(1, 2, 1, 3)
print(y.shape)
# torch.Size([1, 2, 1, 3])
方法 4:重塑
使用 torch.Tensor.reshape(*shape)
(aka torch.reshape(tensor, shapetuple)
) to specify all the dimensions. 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()
函数,因为它可以让您定义所有维度,并且可以 return 视图或副本。
print(x.shape)
# torch.Size([2, 3])
y = x.reshape(1, 2, 1, 3)
print(y.shape)
# torch.Size([1, 2, 1, 3])
此外,来自 O'Reilly 2019 年的书 Programming PyTorch for Deep Learning,作者写道:
现在您可能想知道 view()
和 reshape()
之间有什么区别。答案是 view()
作为原始张量的视图运行,因此如果底层数据发生变化,视图也会发生变化(反之亦然)。但是,如果所需视图不连续,view()
可能会抛出错误;也就是说,如果从头开始创建所需形状的新张量,它不会共享它所占用的内存块。如果发生这种情况,您必须先调用 tensor.contiguous()
,然后才能使用 view()
。但是,reshape()
会在幕后完成所有这些工作,所以一般来说,我建议使用 reshape()
而不是 view()
。
方法五:resize_
使用就地函数torch.Tensor.resize_(*sizes)
修改原始张量。文档指出:
警告。这是一个低级方法。存储被重新解释为 C-contiguous,忽略当前步幅(除非目标大小等于当前大小,在这种情况下张量保持不变)。对于大多数用途,您会希望使用 view()
来检查连续性,或者 reshape()
来在需要时复制数据。要使用自定义步幅就地更改大小,请参阅 set_()
。
print(x.shape)
# torch.Size([2, 3])
x.resize_(1, 2, 1, 3)
print(x.shape)
# torch.Size([1, 2, 1, 3])
我的观察
如果您只想添加一个维度(例如,为批次添加第 0 个维度),则使用 unsqueeze(0)
。如果你想完全改变维度,使用 reshape()
.
另请参阅:
What is the difference between view() and unsqueeze()?
In PyTorch 0.4, is it recommended to use reshape
than view
when it is possible?
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])
torch.reshape() is made to dupe the numpy reshape 方法。
它在 view() and 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)
输出:
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])
但是你知道吗,它也可以替代 squeeze() and 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)
输出:
torch.Size([4])
torch.Size([1, 4])
torch.Size([1, 1, 4])
torch.Size([1, 1, 4])
torch.Size([4])
torch.Size([4])
据我所知,重塑张量的最好方法是使用einops
。它通过提供简单而优雅的功能解决了各种重塑问题。在你的情况下,代码可以写成
from einops import rearrange
ans = rearrange(tensor,'h -> 1 h')
我强烈推荐你试试。
顺便说一句,您可以将它与 pytorch/tensorflow/numpy 和许多其他库一起使用。
例如,我有维度为 (5) 的一维向量。我想将其重塑为二维矩阵 (1,5)。
这是我用 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]])
>>>
但是我如何使用 Pytorch 张量(和变量)做到这一点。我不想切换回 numpy 并再次切换到 Torch 变量,因为它会丢失反向传播信息。
这是我在 Pytorch 中的内容
>>> import torch
>>> from torch.autograd import Variable
>>> a = torch.Tensor([1,2,3,4,5])
>>> a
1
2
3
4
5
[torch.FloatTensor of size 5]
>>> a.size()
(5L,)
>>> a_var = variable(a)
>>> a_var = Variable(a)
>>> a_var.size()
(5L,)
.....do some calculation in forward function
>>> a_var.size()
(5L,)
现在我希望它的大小为 (1, 5)。 如何在不丢失梯度信息的情况下调整或重塑 Variable 中 pytorch 张量的维度。 (因为我会在倒退之前喂入另一个模型)
你可能会用到
a.view(1,5)
Out:
1 2 3 4 5
[torch.FloatTensor of size 1x5]
使用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]
或者你可以使用这个,'-1' 意味着你不必指定元素的数量。
In [3]: a.view(1,-1)
Out[3]:
1 2 3 4 5
[torch.FloatTensor of size 1x5]
这个问题已经得到彻底回答,但我想为经验不足的 python 开发人员补充一点,您可能会发现 *
运算符与 view()
结合使用很有用。
例如,如果您有一个特定的张量大小,您希望不同的数据张量符合,您可以尝试:
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)
这也适用于 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)
为了就地修改张量的形状,你应该使用
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])
在 PyTorch 中,如果操作末尾有下划线(如 tensor.resize_()
),则该操作会对原始张量进行 in-place
修改。
此外,您可以简单地在 torch Tensor 中使用 np.newaxis
来增加维度。这是一个例子:
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])
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'
假设以下代码:
import torch
import numpy as np
a = torch.tensor([1, 2, 3, 4, 5])
以下三个调用具有完全相同的效果:
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
请注意,对于任何生成的张量,如果您修改其中的数据,您也会修改 a 中的数据,因为它们没有数据的副本,而是引用 a 中的原始数据.
res_1[0,0] = 2
a[0] == res_1[0,0] == 2 # Returns true
另一种方法是使用 resize_
就地操作:
a.shape == res_1.shape # Returns false
a.reshape_((1, 5))
a.shape == res_1.shape # Returns true
小心使用 resize_
或其他与 autograd
的就地操作。请参阅以下讨论:https://pytorch.org/docs/stable/notes/autograd.html#in-place-operations-with-autograd
重塑 PyTorch 张量的方法有多种。您可以将这些方法应用于任何维度的张量。
让我们从二维 2 x 3
张量开始:
x = torch.Tensor(2, 3)
print(x.shape)
# torch.Size([2, 3])
为了给这个问题增加一些稳健性,让我们通过在前面添加一个新维度和在中间添加另一个维度来重塑 2 x 3
张量,生成一个 1 x 2 x 1 x 3
张量.
方法 1:使用 None
添加维度
使用 NumPy 风格 insertion of None
(aka np.newaxis
) to add dimensions anywhere you want. 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])
方法二:解压
使用 torch.Tensor.unsqueeze(i)
(a.k.a. torch.unsqueeze(tensor, i)
或就地版本 unsqueeze_()
) 在第 i 个维度添加一个新维度。 returned 张量与原始张量共享相同的数据。在此示例中,我们可以使用 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])
在使用 PyTorch 的实践中,adding an extra dimension for the batch 可能很重要,因此您可能经常看到 unsqueeze(0)
。
方法三:查看
使用torch.Tensor.view(*shape)
指定所有维度。 returned 张量与原始张量共享相同的数据。
print(x.shape)
# torch.Size([2, 3])
y = x.view(1, 2, 1, 3)
print(y.shape)
# torch.Size([1, 2, 1, 3])
方法 4:重塑
使用 torch.Tensor.reshape(*shape)
(aka torch.reshape(tensor, shapetuple)
) to specify all the dimensions. 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()
函数,因为它可以让您定义所有维度,并且可以 return 视图或副本。
print(x.shape)
# torch.Size([2, 3])
y = x.reshape(1, 2, 1, 3)
print(y.shape)
# torch.Size([1, 2, 1, 3])
此外,来自 O'Reilly 2019 年的书 Programming PyTorch for Deep Learning,作者写道:
现在您可能想知道 view()
和 reshape()
之间有什么区别。答案是 view()
作为原始张量的视图运行,因此如果底层数据发生变化,视图也会发生变化(反之亦然)。但是,如果所需视图不连续,view()
可能会抛出错误;也就是说,如果从头开始创建所需形状的新张量,它不会共享它所占用的内存块。如果发生这种情况,您必须先调用 tensor.contiguous()
,然后才能使用 view()
。但是,reshape()
会在幕后完成所有这些工作,所以一般来说,我建议使用 reshape()
而不是 view()
。
方法五:resize_
使用就地函数torch.Tensor.resize_(*sizes)
修改原始张量。文档指出:
警告。这是一个低级方法。存储被重新解释为 C-contiguous,忽略当前步幅(除非目标大小等于当前大小,在这种情况下张量保持不变)。对于大多数用途,您会希望使用 view()
来检查连续性,或者 reshape()
来在需要时复制数据。要使用自定义步幅就地更改大小,请参阅 set_()
。
print(x.shape)
# torch.Size([2, 3])
x.resize_(1, 2, 1, 3)
print(x.shape)
# torch.Size([1, 2, 1, 3])
我的观察
如果您只想添加一个维度(例如,为批次添加第 0 个维度),则使用 unsqueeze(0)
。如果你想完全改变维度,使用 reshape()
.
另请参阅:
What is the difference between view() and unsqueeze()?
In PyTorch 0.4, is it recommended to use reshape
than view
when it is possible?
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])
torch.reshape() is made to dupe the numpy reshape 方法。
它在 view() and 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)
输出:
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])
但是你知道吗,它也可以替代 squeeze() and 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)
输出:
torch.Size([4])
torch.Size([1, 4])
torch.Size([1, 1, 4])
torch.Size([1, 1, 4])
torch.Size([4])
torch.Size([4])
据我所知,重塑张量的最好方法是使用einops
。它通过提供简单而优雅的功能解决了各种重塑问题。在你的情况下,代码可以写成
from einops import rearrange
ans = rearrange(tensor,'h -> 1 h')
我强烈推荐你试试。
顺便说一句,您可以将它与 pytorch/tensorflow/numpy 和许多其他库一起使用。