“视图”方法在PyTorch中如何工作?

问题描述 投票:120回答:5

我对以下代码片段中的方法view()感到困惑。

class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.conv1 = nn.Conv2d(3, 6, 5)
        self.pool  = nn.MaxPool2d(2,2)
        self.conv2 = nn.Conv2d(6, 16, 5)
        self.fc1   = nn.Linear(16*5*5, 120)
        self.fc2   = nn.Linear(120, 84)
        self.fc3   = nn.Linear(84, 10)

    def forward(self, x):
        x = self.pool(F.relu(self.conv1(x)))
        x = self.pool(F.relu(self.conv2(x)))
        x = x.view(-1, 16*5*5)
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        x = self.fc3(x)
        return x

net = Net()

我的困惑在于以下几行。

x = x.view(-1, 16*5*5)

tensor.view()功能有什么作用?我已经在许多地方看到了它的用法,但我无法理解它如何解释它的参数。

如果我将负值作为view()函数的参数会发生什么?例如,如果我打电话,tensor_variable.view(1, 1, -1)会发生什么?

谁能用一些例子解释view()函数的主要原理?

python memory pytorch torch tensor
5个回答
175
投票

视图函数旨在重塑张量。

说你有一个张量

import torch
a = torch.range(1, 16)

a是一个张量,有16个元素,从1到16(包括在内)。如果你想重塑这个张量使其成为4 x 4张量,那么你可以使用

a = a.view(4, 4)

现在a将是一个4 x 4张量。请注意,在重新整形后,元素的总数需要保持不变。将张量a重塑为3 x 5张量是不合适的。

What is the meaning of parameter -1?

如果有任何情况你不知道你想要多少行,但确定列数,那么你可以用-1指定它。 (请注意,您可以将其扩展到具有更多尺寸的张量。只有一个轴值可以为-1)。这是告诉库的一种方式:“给我一个具有这么多列的张量,并计算实现这一点所需的适当行数”。

这可以在您上面给出的神经网络代码中看到。在前向函数中的x = self.pool(F.relu(self.conv2(x)))行之后,您将拥有一个16深度的特征映射。您必须将其展平以将其提供给完全连接的图层。因此,您告诉pytorch重新构造您获得的具有特定列数的张量,并告诉它自己决定行数。

在numpy和pytorch之间绘制相似性,view类似于numpy的reshape函数。


22
投票

让我们举一些例子,从简单到更难。

  1. view方法返回一个张量与self张量相同的数据(这意味着返回的张量具有相同数量的元素),但具有不同的形状。例如: a = torch.arange(1, 17) # a's shape is (16,) a.view(4, 4) # output below 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 [torch.FloatTensor of size 4x4] a.view(2, 2, 4) # output below (0 ,.,.) = 1 2 3 4 5 6 7 8 (1 ,.,.) = 9 10 11 12 13 14 15 16 [torch.FloatTensor of size 2x2x4]
  2. 假设-1不是参数之一,当将它们相乘时,结果必须等于张量中的元素数。如果你这样做:a.view(3, 3),它将引发一个RuntimeError因为shape(3 x 3)对于16个元素的输入无效。换句话说:3 x 3不等于16但是9。
  3. 您可以使用-1作为传递给函数的参数之一,但只能使用一次。所有发生的事情是该方法将为您填写关于如何填充该维​​度的数学。例如,a.view(2, -1, 4)相当于a.view(2, 2, 4)。 [16 /(2 x 4)= 2]
  4. 请注意,返回的张量共享相同的数据。如果您在“视图”中进行更改,则会更改原始张量的数据: b = a.view(4, 4) b[0, 2] = 2 a[2] == 3.0 False
  5. 现在,对于更复杂的用例。文档说每个新的视图维度必须是原始维度的子空间,或者只是跨越d,d + 1,...,d + k,它们满足以下所有i = 0的类似条件的条件。 ..,k - 1,stride [i] = stride [i + 1] x size [i + 1]。否则,需要调用contiguous()才能查看张量。例如: a = torch.rand(5, 4, 3, 2) # size (5, 4, 3, 2) a_t = a.permute(0, 2, 3, 1) # size (5, 3, 2, 4) # The commented line below will raise a RuntimeError, because one dimension # spans across two contiguous subspaces # a_t.view(-1, 4) # instead do: a_t.contiguous().view(-1, 4) # To see why the first one does not work and the second does, # compare a.stride() and a_t.stride() a.stride() # (24, 6, 2, 1) a_t.stride() # (24, 2, 1, 6) 请注意,对于a_t,stride [0]!= stride [1] x size [1],因为24!= 2 x 3

0
投票

我发现x.view(-1, 16 * 5 * 5)相当于x.flatten(1),其中参数1表示展平过程从第一维开始(不展平'样本'维度)正如您所看到的,后一种用法在语义上更清晰,更易于使用,所以我更喜欢flatten()


0
投票

参数-1是什么意思?

您可以将-1读作动态参数或“任何”。因此,在-1中只能有一个参数view()

如果你问x.view(-1,1)这将根据[anything, 1]中的元素数量输出张量形状x。例如:

import torch
x = torch.tensor([1, 2, 3, 4])
print(x,x.shape)
print("...")
print(x.view(-1,1), x.view(-1,1).shape)
print(x.view(1,-1), x.view(1,-1).shape)

将输出:

tensor([1, 2, 3, 4]) torch.Size([4])
...
tensor([[1],
        [2],
        [3],
        [4]]) torch.Size([4, 1])
tensor([[1, 2, 3, 4]]) torch.Size([1, 4])

0
投票

weights.reshape(a, b)将返回一个新的张量,其数据与带有大小(a,b)的权重相同,因为它将数据复制到内存的另一部分。

weights.resize_(a, b)返回具有不同形状的相同张量。但是,如果新形状导致的元素少于原始张量,则一些元素将从张量中移除(但不会从内存中移除)。如果新形状导致的元素多于原始张量,则新元素将在内存中未初始化。

weights.view(a, b)将返回一个新的张量,其数据与大小相同的数据(a,b)

© www.soinside.com 2019 - 2024. All rights reserved.