AUTOGRAD MECHANICS
这部内容会给大家整体地展示自动求导机制是如何工作的以及如何记录下各种操作的
Excluding subgraphs from backward
每一个Tensor都有一个实现pytorch核心功能的flag:requires_grad,正是因为这个flag的存在,使得在计算梯度时所用的“图”可以有很精细的划分(例如某一个Tensor不参与计算梯度)并且可以提高效率。
如果某一个操作只有一个输入,并且这个输入的requires_grad是True的话,那么输入的requires_grad也是True。反过来,只有在所有的输入requires_grad都是False时,输出的requires_grad才是False,如果所有Tensor都requires_grad=False的话,反向传播就不会发生。
>>> x = torch.randn(5, 5) # requires_grad=False by default
>>> y = torch.randn(5, 5) # requires_grad=False by default
>>> z = torch.randn((5, 5), requires_grad=True)
>>> a = x + y
>>> a.requires_grad
False
>>> b = a + z
>>> b.requires_grad
True
那么这个东西都设置成True不就行了么?False有什么存在的意义么?当然有!当你想冻结部分模型的参数时,或者设计网络时就知道不会使用到某些参数的梯度,就需要把requires_grad设置为False。例如:
要用别人训练好的网络模块,可以把该模块的参数冻结,这部分参数就不会参与梯度计算,也不会消耗内存来储存中间结果,之后自己接在后边的网络层才会进行梯度的计算,用以调整相应的参数。
model = torchvision.models.resnet18(pretrained=True)
for param in model.parameters():
param.requires_grad = False
# Replace the last fully-connected layer
# Parameters of newly constructed modules have requires_grad=True by default
model.fc = nn.Linear(512, 100)
# Optimize only the classifier
optimizer = optim.SGD(model.fc.parameters(), lr=1e-2, momentum=0.9)
How autograd encodes the history
Autograd(自动求导)是一个反向的自动微分系统,它把产生数据的操作都记录到一个图中,形成了一个输入tensor作为leaf,输出tensor作为root的无环图,通过从root到leaf的跟踪,可以通过链式法则自动求导。
这个计算图由一系列的Function组成,当进行前向传播时,autograd在计算传播结果的同时建立起了计算梯度的函数组成的计算图,当前向传播结束后,就可以通过反向传播计算梯度。每一个Tensor的.grad_fn属性就是接入这个图的入口。
重要的一点是计算图在每次迭代时都是重新生成的,这就使你可以使用任意的控制流(动态图)
In-place operations with autograd
不推荐使用
AUTOMATIC DIFFERENTIATION PACKAGE - TORCH.AUTOGRAD
该功能包提供了用于自动求导的class、function,这里的class主要是指Variable,但是新版本的pytorch中已不需要这个类了,其功能整合进tensor里了。
1.每一个参与通过运算得到的tensor都会有.grad_fn这个属性,记录了用来计算导数的公式
2.只有执行了反向传播,tensor的.grad才会有内容
import torch
x = torch.ones(2, 2, requires_grad=True)
print(x)
y = x + 2
print(y)
#y是通过运算得到的,所以有grad_fn这个属性
print(y.grad_fn)
<AddBackward0 object at 0x7f2a0a4116d0>
print(x.grad_fn)
None
#继续向前传播
z = y * y * 3
out = z.mean()
print(z, out)
print(x.grad)
None
#向后传播,如果不执行向后传播,x.grad是不会有内容的
out.backward()
print(x.grad)
tensor([[ 4.5000, 4.5000],
[ 4.5000, 4.5000]])
#能够自动计算前向传播这个很复杂过程在各个阶段的导数
3.通过运算得到的tensor,其requires_grad、.grad_fn属性由输入tensor决定,
#创建一个tensor,requires_grad默认为False
a = torch.randn(2, 2)
a = ((a * 3) / (a - 1))
print(a.requires_grad)
#手动修改为True
a.requires_grad_(True)
print(a.requires_grad)
#b是通过运算得到的tensor,如果之前a.requires_grad为True,这里b.grad_fn就会自动计算,如果之前为False,这里不会自动计算b.grad_fn
b = (a * a).sum()
print(b.grad_fn)
4.当不是标量间的计算,而是进行了矩阵运算时,autograd是计算一个向量与雅克比矩阵的乘积
#当输出不是标量时,torch.autograd计算的是一个向量与雅克比矩阵的乘积
x = torch.randn(3, requires_grad=True)
tensor([-1.0154, 1.0822, -0.4681])
y = x * 2
tensor([-2.0308, 2.1644, -0.9362])
while y.data.norm() < 1000:
y = y * 2
print(y)
tensor([-1039.7651, 1108.1805, -479.3367])
#输出y已经不是标量了
v = torch.tensor([0.1, 1.0, 0.0001], dtype=torch.float)
#把扰动v进行反向传播,能算出v和雅克比的乘积vT⋅J
y.backward(v)
print(x.grad)
tensor([ 102.4000, 1024.0000, 0.1024])