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

Autograd 自动微分

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

  深度学习的算法本质上是通过反向传播求导数,Pytorch的Autograd模块实现了此功能。在tensor上的所有操作,Autograd 都能自动求微分,避免了手动计算微分的过程。
  其中,autograd.Variable.是Autograd中核心的类,它简单的封装了Tensor,支持所有tensor的操作。
  tensor在被封装为Variable后,可以调用它的**.backward**实现反向传播,自动计算所有的梯度。Variable的数据结构如下图:
Autograd 自动微分
Variable主要包含三个属性:
1  data:保存Varible所包含的tensor
2 grad:保存data对应的梯度,注意,它这里也是一个Variable,而不是个tensor,与data形状一样。
3 gran_fn:指向一个Function对象,用来反向传播计算输入的梯度。
例子如下:
输入一个全为1的3*3的矩阵,为Variable类

import torch
from torch.autograd import Variable
a=torch.ones(3,3)
x =  Variable(a,requires_grad = True)
print(x)

注意这里requires_grad=True表示该变量在自动计算梯度时会保留梯度值,为False则相反。默认为False
输出结果:

tensor([[1., 1., 1.],
        [1., 1., 1.],
        [1., 1., 1.]], requires_grad=True)
        

Varible几乎支持tensor的所有操作,如下:

y=x.sum()
print(y)

输出结果

tensor(9., grad_fn=<SumBackward0>)

查看x和y的grad_fn

print(x.grad_fn)
print(y.grad_fn)

输出结果:

None
<SumBackward0 object at 0x000002289C55CBA8>

可以看到,y是作为x的运算结果产生的,所以y有grad_fn。而x是直接创建的,所以没有

2.关于backward,反向计算导数

backward只能对一个标量求导或者 传入跟变量相关的梯度。

如果Variable是一个标量,无需对backward()指定任何参数,如下:
先看这个:
Autograd 自动微分

import torch
from torch.autograd import Variable
x = Variable(torch.ones(2, 2), requires_grad=True)
y = x + 2
z = y * y * 3
p = z.mean()
p.backward()
print(x.grad)

输出结果

tensor([[4.5000, 4.5000],
        [4.5000, 4.5000]])

但如果Varieble是一个向量,就需要指定一个和Variable中tensor的形状匹配的grad_output参数
如下:
这里,y.backward(weight1),weight1是对求导变量的一个权重,当y为标量的时候,该参数默认为1,就可以跟我们理解的一样,求y对x的导数。至于为什么有这个,参考知乎上的一文:PyTorch 的 backward 为什么有一个 grad_variables 参数?

import torch
from torch.autograd import Variable

x = Variable(torch.Tensor([1, 2, 3, 4, 5]), requires_grad=True)  # 需要求导数
y = x * x

weights1 = torch.Tensor([5, 5, 5, 5, 5])
y.backward(weights1, retain_graph=True)
print(x.grad)

输出结果:

tensor([10., 20., 30., 40., 50.])

backward函数中还有retain_graph参数,使用这个参数时,再次求导的时候,会对之前的导数进行累加,所以每次反向传播前需要把梯度清零,如下,这在神经网络代码中会用到。
如下图:

import torch
from torch.autograd import Variable

x = Variable(torch.Tensor([1, 2, 3, 4, 5]), requires_grad=True)  # 需要求导数
y = x * x

weights1 = torch.Tensor([5, 5, 5, 5, 5])
y.backward(weights1, retain_graph=True)
print(x.grad)

weights1 = torch.Tensor([5, 5, 5, 5, 5])
y.backward(weights1, retain_graph=True)
print(x.grad)

x.grad.data.zero_()#     清零

weights1 = torch.Tensor([5, 5, 5, 5, 5])
y.backward(weights1, retain_graph=True)
print(x.grad)

#输出结果
# tensor([10., 20., 30., 40., 50.])
# tensor([ 20.,  40.,  60.,  80., 100.])
# tensor([10., 20., 30., 40., 50.])