0%

torch | Variable 与 tensor

了解 Variable 之前,就要分版本来讲解,一个是 0.4 之前,一个是 0.4 之后。


参考资料


在这里不谈论 0.4 之前的版本了,太老了。


0.4 之后


Facebook 推出了 PyTorch 0.4.0 版本,该版本有诸多更新和改变,比如支持 WindowsVariableTensor 合并等等。

torch.Tensortorch.autograd.Variable现在是同一个类。torch.Tensor 能够像之前的Variable一样追踪历史和反向传播。Variable仍能够正常工作,但是返回的是Tensor。所以在0.4的代码中,不需要使用Variable了。

Tensor 中 type () 的变化

这里需要注意到张量的 type()不再反映数据类型,而是改用 isinstance()x.type() 来表示数据类型,代码如下:

1
2
3
4
5
6
7
>>> x = torch.DoubleTensor([1, 1, 1])
>>> print(type(x)) # was torch.DoubleTensor
"<class 'torch.Tensor'>"
>>> print(x.type()) # OK: 'torch.DoubleTensor'
'torch.DoubleTensor'
>>> print(isinstance(x, torch.DoubleTensor)) # OK: True
True

autograd 用于跟踪历史记录

作为 autograd 方法的核心标志,requires_grad 现在是 Tensors 类的一个属性。让我们看看这个变化是如何体现在代码中的。autograd 使用先前用于 Variable 的相同规则。当操作中任意输入 Tensorrequire_grad = True 时,它开始跟踪历史记录。代码如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
>>> x = torch.ones(1)  # create a tensor with requires_grad=False (default)
>>> x.requires_grad
False
>>> y = torch.ones(1) # another tensor with requires_grad=False
>>> z = x + y
>>> # both inputs have requires_grad=False. so does the output
>>> z.requires_grad
False
>>> # then autograd won't track this computation. let's verify!
>>> z.backward()
RuntimeError: element 0 of tensors does not require grad and does not have a grad_fn
>>>
>>> # now create a tensor with requires_grad=True
>>> w = torch.ones(1, requires_grad=True)
>>> w.requires_grad
True
>>> # add to the previous result that has require_grad=False
>>> total = w + z
>>> # the total sum now requires grad!
>>> total.requires_grad
True
>>> # autograd can compute the gradients as well
>>> total.backward()
>>> w.grad
tensor([ 1.])
>>> # and no computation is wasted to compute gradients for x, y and z, which don't require grad
>>> z.grad == x.grad == y.grad == None
True

requires_grad 操作

除了直接设置属性之外,你还可以使用 my_tensor.requires_grad_(requires_grad = True) 在原地更改此标志,或者如上例所示,在创建时将其作为参数传递(默认为False)来实现,代码如下:

1
2
3
4
5
6
>>> existing_tensor.requires_grad_()
>>> existing_tensor.requires_grad
True
>>> my_tensor = torch.zeros(3, 4, requires_grad=True)
>>> my_tensor.requires_grad
True

关于 .data

.data 是从 Variable 中获取底层 Tensor 的主要方式。 合并后,调用 y = x.data 仍然具有相似的语义。因此 y 将是一个与 x 共享相同数据的 Tensor,并且 requires_grad = False,它与 x 的计算历史无关。

然而,在某些情况下 .data 可能不安全。 对 x.data 的任何更改都不会被 autograd 跟踪,如果在反向过程中需要 x,那么计算出的梯度将不正确。另一种更安全的方法是使用 x.detach(),它将返回一个与 requires_grad = False 时共享数据的 Tensor,但如果在反向过程中需要 x,那么 autograd 将会就地更改它。

标量

0.4 之后的PyTorch中引入了适当的标量(0维张量)支持! 可以使用新版本中的 torch.tensor 函数来创建标量(这将在后面更详细地解释,现在只需将它认为是PyTorchnumpy.array的等效项),代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
>>> torch.tensor(3.1416)         # create a scalar directly
tensor(3.1416)
>>> torch.tensor(3.1416).size() # scalar is 0-dimensional
torch.Size([])
>>> torch.tensor([3]).size() # compare to a vector of size 1
torch.Size([1])
>>>
>>> vector = torch.arange(2, 6) # this is a vector
>>> vector
tensor([ 2., 3., 4., 5.])
>>> vector.size()
torch.Size([4])
>>> vector[3] # indexing into a vector gives a scalar
tensor(5.)
>>> vector[3].item() # .item() gives the value as a Python number
5.0
>>> mysum = torch.tensor([2, 3]).sum()
>>> mysum
tensor(5)
>>> mysum.size()
torch.Size([])

累计损失函数

考虑在 PyTorch0.4.0 版本之前广泛使用的 total_loss + = loss.data [0] 模式。Loss 是一个包含张量(1,)Variable,但是在新发布的 0.4.0 版本中,loss 是一个 0维标量。 对于标量的索引是没有意义的(目前的版本会给出一个警告,但在0.5.0中将会报错一个硬错误):使用 loss.item()从标量中获取 Python 数字。

值得注意得是,如果你在累积损失时未能将其转换为 Python 数字,那么程序中的内存使用量可能会增加。这是因为上面表达式的右侧,在先前版本中是一个 Python 浮点型数字,而现在它是一个零维的张量。因此,总损失将会张量及其历史梯度的累加,这可能会需要更多的时间来自动求解梯度值。

请我喝杯咖啡吧~