1 Autograd简介
PyTorch 中,所有神经网络的核心是 autograd 包。autograd包为张量上的所有操作提供了自动求导机制。它是一个在运行时定义 ( define-by-run )的框架,这意味着反向传播是根据代码如何运行来决定的,并且每次迭代可以是不同的。
本节主要内容:
- autograd的求导机制
- 梯度的反向传播
torch.Tensor 是这个包的核心类。如果设置它的属性 .requires_grad 为 True,那么它将会追踪对于该张量的所有操作。当完成计算后可以通过调用 .backward(),来自动计算所有的梯度。这个张量的所有梯度将会自动累加到.grad属性。
注意:在 y.backward() 时,如果 y 是标量,则不需要为 backward() 传入任何参数;否则,需要传入一个与 y 同形的Tensor。
要阻止一个张量被跟踪历史,可以调用.detach()方法将其与计算历史分离,并阻止它未来的计算记录被跟踪。为了防止跟踪历史记录(和使用内存),可以将代码块包装在 with torch.no_grad(): 中。在评估模型时特别有用,因为模型可能具有 requires_grad = True 的可训练的参数,但是我们不需要在此过程中对他们进行梯度计算。
还有一个类对于autograd的实现非常重要:Function。Tensor 和 Function 互相连接生成了一个无环图 (acyclic graph),它编码了完整的计算历史。每个张量都有一个.grad_fn属性,该属性引用了创建 Tensor 自身的Function(除非这个张量是用户手动创建的,即这个张量的grad_fn是 None )。下面给出的例子中,张量由用户手动创建,因此grad_fn返回结果是None。
from __future__ import print_function
import torch
x = torch.randn(3,3,requires_grad=True)
print(x.grad_fn)
>>None
如果需要计算导数,可以在 Tensor 上调用 .backward()。如果 Tensor 是一个标量(即它包含一个元素的数据),则不需要为 backward() 指定任何参数,但是如果它有更多的元素,则需要指定一个gradient参数,该参数是形状匹配的张量。
创建一个张量并设置requires_grad=True用来追踪其计算历史
x = torch.ones(2, 2, requires_grad=True)
print(x)
>>tensor([[1., 1.],
[1., 1.]], requires_grad=True)
做一次运算,由于y是计算的结果,所以它有grad_fn属性。:
y = x**2 #平方
print(y)
print(y.grad_fn)
>>tensor([[1., 1.],
[1., 1.]], grad_fn=<PowBackward0>)
<PowBackward0 object at 0x7fb9f80bc5e0>
对 y 进行更多操作:
z = y * y * 3
out = z.mean()
print(z)
print(out)
>>tensor([[3., 3.],
[3., 3.]], grad_fn=<MulBackward0>)
tensor(3., grad_fn=<MeanBackward0>)
.requires_grad_(...) 原地改变了现有张量的requires_grad标志。如果没有指定的话,默认输入的这个标志是 False。
a = torch.randn(2, 2) # 缺失情况下默认 requires_grad = False
a = ((a * 3) / (a - 1))
print(a.requires_grad)
a.requires_grad_(True)
print(a.requires_grad)
b = (a * a).sum()
print(b.grad_fn)
>>False
True
<SumBackward0 object at 0x7fba28382fa0>
2 梯度
现在开始进行反向传播,因为 out 是一个标量,因此out.backward()和 out.backward(torch.tensor(1.)) 等价。
out.backward()
输出d(out)/dxd(out)/dxd(out)/dx:
print(x.grad)
>>tensor([[3., 3.],
[3., 3.]])
注意:grad在反向传播过程中是累加的(accumulated),这意味着每一次运行反向传播,梯度都会累加之前的梯度,所以一般在反向传播之前需把梯度清零。
# 再来反向传播⼀一次,注意grad是累加的
out2 = x.sum()
out2.backward()
print(x.grad)
out3 = x.sum()
x.grad.data.zero_()
out3.backward()
print(x.grad)
>>tensor([[4., 4.],
[4., 4.]])
tensor([[1., 1.],
[1., 1.]])
看一个雅可比向量积的例子:
x = torch.randn(3, requires_grad=True)
print(x)
y = x * 2
i = 0
while y.data.norm() < 1000:
y = y * 2
i = i + 1
print(y)
print(i)
>>tensor([ 0.2176, -0.3949, 0.3115], requires_grad=True)
tensor([ 445.6994, -808.8474, 637.9488], grad_fn=<MulBackward0>)
10
在这种情况下,y 不再是标量。torch.autograd 不能直接计算完整的雅可比矩阵,但是如果我们只想要雅可比向量积,只需将这个向量作为参数传给 backward:
v = torch.tensor([0.1, 1.0, 0.0001], dtype=torch.float)
y.backward(v)
print(x.grad)
>>tensor([1.0240e+02, 1.0240e+03, 1.0240e-01])
也可以通过将代码块包装在 with torch.no_grad(): 中,来阻止 autograd 跟踪设置了.requires_grad=True的张量的历史记录。
print(x.requires_grad)
print((x ** 2).requires_grad)
with torch.no_grad():
print((x ** 2).requires_grad)
>>True
True
False
如果我们想要修改 tensor 的数值,但是又不希望被 autograd 记录(即不会影响反向传播), 那么我们可以对 tensor.data 进行操作。
x = torch.ones(1,requires_grad=True)
print(x.data) # 还是一个tensor
print(x.data.requires_grad) # 但是已经是独立于计算图之外
y = 2 * x
x.data *= 100 # 只改变了值,不会记录在计算图,所以不会影响梯度传播
y.backward()
print(x) # 更改data的值也会影响tensor的值
print(x.grad)
>>tensor([1.])
False
tensor([100.], requires_grad=True)
tensor([2.])

本文详细介绍了PyTorch中的autograd机制,它是神经网络的核心部分,负责自动求导。通过设置张量的requires_grad属性,可以追踪其计算历史。在运行时定义的框架使得每次迭代的反向传播都可能不同。张量的grad_fn属性记录了计算过程,而.backward()用于执行反向传播计算梯度。此外,介绍了如何阻止张量历史记录的追踪,以及如何清零梯度。文章还展示了雅可比向量积的计算和在no_grad()上下文管理器中的使用,确保不记录历史。最后,讨论了如何在不影响梯度传播的情况下修改张量的值。
914

被折叠的 条评论
为什么被折叠?



