简单地说,torch.Tensor.view()
它的灵感来自numpy.ndarray.reshape()
or numpy.reshape()
,创建了张量的新视图,只要新的形状与原始张量的形状兼容。
让我们通过一个具体的例子来详细理解这一点。
In [43]: t = torch.arange(18)
In [44]: t
Out[44]:
tensor([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17])
使用 shape 的张量t
,只能为以下形状创建(18,)
新视图:
(1, 18)
或等价 (1, -1)
或 等价 或 等价 或等价或等价 或等价 或 等价 或等价 或(-1, 18)
(2, 9)
(2, -1)
(-1, 9)
(3, 6)
(3, -1)
(-1, 6)
(6, 3)
(6, -1)
(-1, 3)
(9, 2)
(9, -1)
(-1, 2)
(18, 1)
(18, -1)
(-1, 1)
正如我们已经从上面的形状元组中观察到的,形状元组的元素(例如 等)的乘积2*9
必须3*6
始终等于原始张量中元素的总数(18
在我们的示例中)。
要观察的另一件事是,我们-1
在每个形状元组的一个地方使用了 a。通过使用 a -1
,我们在自己进行计算时变得懒惰,而是将任务委托给 PyTorch 在创建新视图时为形状计算该值。需要注意的重要一点是,我们只能在形状元组中使用单个-1
。其余值应由我们明确提供。否则 PyTorch 会通过抛出一个来抱怨RuntimeError
:
RuntimeError:只能推断一维
因此,对于上述所有形状,PyTorch 将始终返回原始张量的新视图t
。这基本上意味着它只是为请求的每个新视图更改张量的步幅信息。
下面是一些示例,说明了张量的步幅如何随着每个新视图的变化而变化。
# stride of our original tensor `t`
In [53]: t.stride()
Out[53]: (1,)
现在,我们将看到新视图的进步:
# shape (1, 18)
In [54]: t1 = t.view(1, -1)
# stride tensor `t1` with shape (1, 18)
In [55]: t1.stride()
Out[55]: (18, 1)
# shape (2, 9)
In [56]: t2 = t.view(2, -1)
# stride of tensor `t2` with shape (2, 9)
In [57]: t2.stride()
Out[57]: (9, 1)
# shape (3, 6)
In [59]: t3 = t.view(3, -1)
# stride of tensor `t3` with shape (3, 6)
In [60]: t3.stride()
Out[60]: (6, 1)
# shape (6, 3)
In [62]: t4 = t.view(6,-1)
# stride of tensor `t4` with shape (6, 3)
In [63]: t4.stride()
Out[63]: (3, 1)
# shape (9, 2)
In [65]: t5 = t.view(9, -1)
# stride of tensor `t5` with shape (9, 2)
In [66]: t5.stride()
Out[66]: (2, 1)
# shape (18, 1)
In [68]: t6 = t.view(18, -1)
# stride of tensor `t6` with shape (18, 1)
In [69]: t6.stride()
Out[69]: (1, 1)
这就是view()
函数的魔力。只要新视图的形状与原始形状兼容,它只会更改每个新视图的(原始)张量的步幅。
从strides元组中可能观察到的另一件有趣的事情是,第 0个位置的元素的值等于形状元组的第 1 个位置的元素的值。
In [74]: t3.shape
Out[74]: torch.Size([3, 6])
|
In [75]: t3.stride() |
Out[75]: (6, 1) |
|_____________|
这是因为:
In [76]: t3
Out[76]:
tensor([[ 0, 1, 2, 3, 4, 5],
[ 6, 7, 8, 9, 10, 11],
[12, 13, 14, 15, 16, 17]])
步幅(6, 1)
表示要沿着第 0 维从一个元素到下一个元素,我们必须跳跃或走 6 步。(即从0
到,必须采取 6 个步骤。)但是要从第一个维度6
中的一个元素到下一个元素,我们只需要一个步骤(例如从到)。2
3
因此,步长信息是如何从内存访问元素以执行计算的核心。
只要新形状与原始张量的形状兼容,此函数将返回一个视图并且与使用完全相同。torch.Tensor.view()
否则,它将返回一个副本。
但是,注释torch.reshape()
警告说:
连续输入和具有兼容步幅的输入可以在不复制的情况下进行重塑,但不应依赖于复制与查看行为。