PyTorch深度学习60分钟闪电战:02 Autograd - 自动微分
本系列是PyTorch官网Tutorial Deep Learning with PyTorch: A 60 Minute Blitz 的翻译和总结。
下载本文的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
。
Tensor
和Function
相互连接并建立一个无环图,该图对完整的计算历史进行编码。每个张量都有一个.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