用Tensor训练网络很方便,但从上篇博客最后的线性回归例子来看,反向传播过程需要自己手动实现。这对于像线性回归等较为简单的模型来说,还可以应付,但实际使用中经常出现非常复杂的网络结构,此时如果手动实现反向传播,不仅费时费力,而且容易出错,难以检查。torch.autograd就是为方便用户使用,而专门开发的一套自动求导引擎,它能够根据输入和前向传播过程自动构建计算图,并执行反向传播。
计算图(Computation Graph)是现代深度学习框架如PyTorch和TensorFlow等的核心,其为高效自动求导算法——反向传播(Back Propogation)提供了理论支持,了解计算图在实际写程序过程中会有极大的帮助。本节将涉及一些基础的计算图知识,但并不要求大家事先对此有深入的了解。关于计算图的基础知识推荐阅读Christopher Olah的文章http://colah.github.io/posts/2015-08-Backprop/↩。
目录
1. requires_grad
PyTorch在autograd模块中实现了计算图的相关功能,autograd中的核心数据结构是Variable。从v0.4版本起,Variable和Tensor合并。我们可以认为需要求导(requires_grad=True)的tensor即为Variable. autograd记录对tensor的操作记录用来构建计算图。
Variable提供了大部分tensor支持的函数,但其不支持部分inplace
函数,因这些函数会修改tensor自身,而在反向传播中,variable需要缓存原来的tensor来计算反向传播梯度。如果想要计算各个Variable的梯度,只需调用根节点variable的backward
方法,autograd会自动沿着计算图反向传播,计算每一个叶子节点的梯度。
from __future__ import print_function
import torch
#在创建tensor的时候指定requires_grad
a = torch.randn(3,4, requires_grad=True)
# 或者
a = torch.randn(3,4).requires_grad_()
# 或者
a = torch.randn(3,4)
a.requires_grad=True
a
b = torch.zeros(3,4).requires_grad_()
print(b)
c = a.add(b) #c = a + b
print(c)
d = c.sum()
d.backward()# 反向传播
print(d) # d还是一个requires_grad=True的tensor,对它的操作需要慎重
print(d.requires_grad)
print(a.grad)
# 此处虽然没有指定c需要求导,但c依赖于a,而a需要求导,
# 因此c的requires_grad属性会自动设为True
print(a.requires_grad, b.requires_grad, c.requires_grad)
# 由用户创建的variable a,b属于叶子节点,对应的grad_fn是None;c不是叶子节点 grad_fn不是None
print(a.is_leaf, b.is_leaf, c.is_leaf)
# c.grad是None, 因c不是叶子节点,它的梯度是用来计算a的梯度
# 所以虽然c.requires_grad = True,但其梯度计算完之后即被释放
c.grad is None
def f(x):
y = x**2 * torch.exp(x)
return y
def gradf(x):
#手动求导函数
dx = 2*x*torch.exp(x)+x**2*torch.exp(x)
return dx
x = torch.randn(3,4, requires_grad = True)
y = f(x)
print(y)
y.backward(torch.ones(y.size())) #gradient的形状与y一致 神经网络中loss.backward()不需要参数 是因为loss是一个数值 dloss默认为1省略
#y是一个2维tensor 所以需要传入dy 形状与y一致 值全为1
print(x.grad)
#autograd的计算结果与利用公式手动计算的结果一致
print(gradf(x))
2. 计算图
PyTorch中autograd
的底层采用了计算图,计算图是一种特殊的有向无环图(DAG),用于记录算子与变量之间的关系。一般用矩形表示算子,椭圆形表示变量。如表达式z=wx+b可分解为y=wx和z=y+b,其计算图如下所示,图中MUL
,ADD
都是算子,w,x,b即变量。
如上有向无环图中,x和b是叶子节点(leaf node),这些节点通常由用户自己创建,不依赖于其他变量。z称为根节点,是计算图的最终目标。利用链式法则很容易求得各个叶子节点的梯度。
而有了计算图,上述链式求导即可利用计算图的反向传播自动完成,其过程如下图所示:
在PyTorch实现中,autograd会随着用户的操作,记录生成当前variable的所有操作,并由此建立一个有向无环图。用户每进行一个操作,相应的计算图就会发生改变。更底层的实现中,图中记录了操作Function
,每一个变量在图中的位置可通过其grad_fn
属性在图中的位置推测得到。在反向传播过程中,autograd沿着这个图从当前变量(根节点z)溯源,可以利用链式求导法则计算所有叶子节点的梯度。每一个前向传播操作的函数都有与之对应的反向传播函数用来计算输入的各个variable的梯度,