PyTorch自动微分(Autograd)
简介
PyTorch 中所有神经网络的核心是 autograd
自动求导包. 我们先来简单介绍一下, 然后我们会去训练我们的第一个神经网络.
autograd
自动求导包针对张量上的所有操作都提供了自动微分操作. 这是一个逐个运行的框架, 这意味着您的反向传播是由您的代码如何运行来定义的, 每个单一的迭代都可以不一样.
让我们用一些更简单的术语与例子来了解这些套路.
Variable(变量)
autograd.Variable
是包的核心类. 它包装了张量, 并且支持几乎所有的操作. 一旦你完成了你的计算, 你就可以调用 .backward()
方法, 然后所有的梯度计算会自动进行.
你还可以通过 .data
属性来访问原始的张量, 而关于该 variable(变量)的梯度会被累计到 .grad
上去(grad有梯度的意思).
还有一个针对自动求导实现来说非常重要的类 - Function
(function有函数的意思).
Variable
和 Function
是相互联系的, 并且它们构建了一个非循环的图, 编码了一个完整的计算历史信息. 每一个 variable(变量)都有一个 .grad_fn
属性, 它引用了一个已经创建了 Variable
的 Function
(除了用户创建的 Variable
之外 - 它们的 grad_fn
为 None
).
如果你想计算导数, 你可以在 Variable 上调用 .backward()
(backward有反向的意思)方法. 如果 Variable
是标量的形式(例如, 它包含一个元素数据), 你不必指定任何参数给 backward()
, 但是, 如果它有更多的元素. 你需要去指定一个 grad_output
参数, 该参数是一个匹配 shape(形状)的张量.
import torch
from torch.autograd import Variable
创建 variable(变量):
x = torch.ones(2, 2, requires_grad=True)
print(x)
tensor([[1., 1.],
[1., 1.]], requires_grad=True)
variable(变量)的操作:
y = x + 2
print(y)
tensor([[3., 3.],
[3., 3.]], grad_fn=<AddBackward0>)
y 由操作创建,所以它有 grad_fn 属性.
print(y.grad_fn)
<AddBackward0 object at 0x00000250615600A0>
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_(...)
in-place改变一个已有Tensor的requires_grad
标志,默认为False
:
a = torch.randn(2, 2)
a = ((a * 3) / (a - 1))
b = (a * a).sum()
print(a.requires_grad)
print(b.grad_fn)
print(b.requires_grad)
False
None
False
a.requires_grad_(True)
print(a.requires_grad)
b = (a * a).sum()
print(b.grad_fn)
True
<SumBackward0 object at 0x00000250615609D0>
梯度
我们现在开始了解反向传播, out.backward()
与 out.backward(torch.Tensor([1.0]))
这样的方式一样
out.backward()
但因 d ( o u t ) d x \frac{d(out)}{dx} dxd(out) 的梯度:
print(x.grad)
tensor([[4.5000, 4.5000],
[4.5000, 4.5000]])
应该是会得到一个元素全为 4.5
的矩阵。让我们推导出out
Variable“
o
o
o”。我们有
o
=
1
4
∑
i
z
i
o = \frac{1}{4}\sum_iz_i
o=41∑izi ,
z
i
=
3
(
x
i
+
2
)
2
z_i=3(x_i+2)^2
zi=3(xi+2)2 和 $z_i\mid_{x_i=1}=27
。
因
此
,
。因此,
。因此,\frac{∂o}{∂x_i}\mid_{x_i=1}= \frac{∂o}{∂z_i}\frac{∂z_i}{∂x_i} = \frac{1}{4}(6x_i+12)= \frac{3}{2}(x_i+2) =\frac{9}{2}=4.5 $
你可以使用自动求导来做很多有趣的事情:
x = torch.randn(3, requires_grad=True)
print(x)
y = x * 2
while y.data.norm() < 1000: #也可以直接写y.norm(), norm()计算y的p-范数,默认p=2
y = y * 2
print(y)
tensor([ 1.1708, -0.1020, -0.4631], requires_grad=True)
tensor([1198.9468, -104.4859, -474.1878], grad_fn=<MulBackward0>)
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])
再来一个例子:
x = torch.tensor([[1,2,4],[3,1,2]], dtype=torch.float, requires_grad=True)
print(x)
y = x + 2
z = y * y
print(z)
z.backward(torch.ones_like(x))
print(x.grad) #计算得到的就是z相对于x的导数
tensor([[1., 2., 4.],
[3., 1., 2.]], requires_grad=True)
tensor([[ 9., 16., 36.],
[25., 9., 16.]], grad_fn=<MulBackward0>)
tensor([[ 6., 8., 12.],
[10., 6., 8.]])
也可以通过将代码块封装进with torch.no_grad(),来暂停.requires_grad=True的Tensors的历史跟踪。
print(x.requires_grad)
print((x ** 2).requires_grad)
with torch.no_grad():
print((x ** 2).requires_grad)
print((x ** 2).requires_grad)
True
True
False
True
参考:
1.https://blog.csdn.net/anhec/article/details/96422609
2.https://pytorch.apachecn.org/docs/0.3/blitz_autograd_tutorial.html