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

PyTorch自动微分(Autograd)

程序员文章站 2022-07-12 22:56:29
...

简介

PyTorch 中所有神经网络的核心是 autograd 自动求导包. 我们先来简单介绍一下, 然后我们会去训练我们的第一个神经网络.

autograd 自动求导包针对张量上的所有操作都提供了自动微分操作. 这是一个逐个运行的框架, 这意味着您的反向传播是由您的代码如何运行来定义的, 每个单一的迭代都可以不一样.

让我们用一些更简单的术语与例子来了解这些套路.

Variable(变量)

autograd.Variable 是包的核心类. 它包装了张量, 并且支持几乎所有的操作. 一旦你完成了你的计算, 你就可以调用 .backward() 方法, 然后所有的梯度计算会自动进行.

你还可以通过 .data 属性来访问原始的张量, 而关于该 variable(变量)的梯度会被累计到 .grad 上去(grad有梯度的意思).

PyTorch自动微分(Autograd)

还有一个针对自动求导实现来说非常重要的类 - Functionfunction有函数的意思).

VariableFunction 是相互联系的, 并且它们构建了一个非循环的图, 编码了一个完整的计算历史信息. 每一个 variable(变量)都有一个 .grad_fn 属性, 它引用了一个已经创建了 VariableFunction (除了用户创建的 Variable 之外 - 它们的 grad_fnNone ).

如果你想计算导数, 你可以在 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=41izi , 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