欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

PyTorch深度学习60分钟闪电战:02 Autograd - 自动微分

程序员文章站 2022-03-06 20:57:34
...

本系列是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