本系列是PyTorch官网Tutorial Deep Learning with PyTorch: A 60 Minute Blitz 的翻译和总结。
1. PyTorch概览
2. Autograd – 自动微分
3. 神经网络
4. 训练一个分类器
下载本文的Jupyter NoteBook文件:60min_02_Autograd – Automatic Differentiation.ipynb

在PyTorch中,一切神经网络的核心是autograd模块。

autograd模块为张量的所有操作提供自动微分。这是一个由运行定义的框架,这意味着你的反向传播是由代码的运行的方式定义的,并且每一次迭代都可能不同。

让我们通过一些示例以更简单的方式看待这一点。

Tensor

torch.Tensor是这个包的核心类。如果将.requires_grad属性设置为True,这将开始追踪它上的所有操作。当计算结束,你可以调用.backward()方法,自动计算所有梯度。该张量的梯度将累加到.grad属性中。

为了停止一个张量上的历史记录跟踪,你可以调用.detach()方法将它从计算历史中分离,并阻止将来计算中的跟踪。

为了防止跟踪历史记录(和使用内存),你还可以将代码块包装在with torch.no_grad():中。这在评估模型时特别有用,因为模型可能存在带requires_grad=True的可训练参数,但事实上我们不需要梯度。

还有一个类对于autograd实现非常重要——Function

TensorFunction相互连接并建立一个无环图,该图对完整的计算历史进行编码。每个张量都有一个.grad_fn属性,该属性引用一个创建这个张量的Function(用户创建的张量除外,他们的.grad_fn属性为None)。

如果你想计算导数,你可以在张量上调用.backward()。如果Tensor为标量(即,它包含一个元素数据),则无需为.backward()指定参数,但是如果它有更多元素,则需要指定gradient 参数为匹配形状的张量。

import torch

创建一个张量,并设置requires_grad=True来跟踪计算。

x = torch.ones(2, 2, requires_grad=True)
print(x)
tensor([[1., 1.],
        [1., 1.]], requires_grad=True)

执行一项操作:

y = x + 2
print(y)
tensor([[3., 3.],
        [3., 3.]], grad_fn=<AddBackward0>)

y是由相加操作得来的张量,所以它有grad_fn属性。

print(y.grad_fn)
<AddBackward0 object at 0x0000022497B4C630>

我们在y上做更多的操作:

z = y * y * 3
out = z.mean()

print(z, out)
tensor([[27., 27.],
        [27., 27.]], grad_fn=<MulBackward0>) tensor(27., grad_fn=<MeanBackward0>)

.requires_grad_( ... )方法可以变换一个张量的requires_grad属性。

a = torch.randn(2, 2)
a = ((a * 3) / (a - 1))
print(a.requires_grad)
b = (a * a).sum()
print(b.grad_fn)

a.requires_grad_(True)
print(a.requires_grad)
b = (a * a).sum()
print(b.grad_fn)
False
None
True
<SumBackward0 object at 0x0000022497F12EF0>

Gradients

现在让我们开始反向传播。

out.backward()

打印梯度d(out)/dx

print(x.grad)
tensor([[4.5000, 4.5000],
        [4.5000, 4.5000]])

接下来是一个vector-Jacobian product的例子

x = torch.randn(3, requires_grad=True)

y = x * 2
print(y)
while y.data.norm() < 1000:
    y = y * 2
    print(y)
tensor([-0.1796, -0.6436, -1.6582], grad_fn=<MulBackward0>)
tensor([-0.3592, -1.2871, -3.3164], grad_fn=<MulBackward0>)
tensor([-0.7184, -2.5743, -6.6329], grad_fn=<MulBackward0>)
tensor([ -1.4368,  -5.1485, -13.2657], grad_fn=<MulBackward0>)
tensor([ -2.8737, -10.2970, -26.5314], grad_fn=<MulBackward0>)
tensor([ -5.7474, -20.5940, -53.0628], grad_fn=<MulBackward0>)
tensor([ -11.4948,  -41.1880, -106.1257], grad_fn=<MulBackward0>)
tensor([ -22.9896,  -82.3761, -212.2513], grad_fn=<MulBackward0>)
tensor([ -45.9791, -164.7521, -424.5026], grad_fn=<MulBackward0>)
tensor([ -91.9583, -329.5043, -849.0052], grad_fn=<MulBackward0>)
tensor([ -183.9165,  -659.0085, -1698.0105], grad_fn=<MulBackward0>)

现在,y不再是标量。torch.autograd无法直接计算完整雅可比行列,但是如果只想要 vector-Jacobian product,只需通过 backward 将向量作为参数传入:

v = torch.tensor([0.1, 1.0, 0.0001], dtype=torch.float)
y.backward(v)

print(x.grad)
tensor([2.0480e+02, 2.0480e+03, 2.0480e-01])

你也可以使用with torch.no_grad():来包装代码块来使.requires_grad=True的张量通过追踪历史进行自动求导。

print(x.requires_grad)
print((x ** 2).requires_grad)

with torch.no_grad():
    print((x ** 2).requires_grad)
True
True
False