查看原文
其他

【他山之石】Pytorch Autograd与计算图

“他山之石,可以攻玉”,站在巨人的肩膀才能看得更高,走得更远。在科研的道路上,更需借助东风才能更快前行。为此,我们特别搜集整理了一些实用的代码链接,数据集,软件,编程技巧等,开辟“他山之石”专栏,助你乘风破浪,一路奋勇向前,敬请关注。

作者:知乎—宫水诸叶

地址:https://www.zhihu.com/people/zhi-zuo-ren-23

import torch

默认读者掌握偏导数计算,链式法则,雅可比行列式等数学基础


01

与计算图相关的属性
  • data:该Tensor节点存储的数值

  • requires_grad:表示是否需要计算此tensor的梯度,默认False。当创建时指明为True,或者该tensor由其它该属性为True的tensor计算得到时,设置为True。通过.requires_grad_()来就地改变

  • grad:存储梯度的值,初始为None。当计算图后方有一个tensor(out)调用backward()时,存储 d(out)/ d(tensor_now)

  • grad_fn:反向传播时,用来计算梯度的函数。若tensor是通过初始化建立,或作为操作数tensor的requires_grad属性全为为False时为None,若是存在由其它requires_grad为True的tensor计算得到的为有特定函数

  • is_leaf:所有requires_grad=False的Tensors都为叶子节点,所有用户显示初始化的Tensors也为叶子节点

Example 1

x = torch.ones((2,2), requires_grad = True)print(x)print(x.grad_fn) #初始化建立的Tensor为Noneprint(x.is_leaf) #初始化建立为叶子节点y = x + 5print(y.grad_fn) #由requires_grad为True的tensor计算得到,有相应函数print(y.is_leaf) #由requires_grad为True的tensor计算得到,requires_grad为True,非叶子节点tensor([[1., 1.], [1., 1.]], requires_grad=True)NoneTrue<AddBackward0 object at 0x000001EBCDA654A8>False

Example 2

x = torch.ones((2,2), requires_grad = True)y = torch.rand((2,2)) #默认为Falsez = x + y #操作数有一个为True,则结果也为Trueprint(y.requires_grad)print(z.requires_grad)FalseTrue

Example 3

x = torch.randn(3,3) # 缺失情况下默认 requires_grad = Falsey = x ** 3print(x.requires_grad) # 默认 requires_grad = Falseprint(y.requires_grad) #算得y的所有操作数requires_grad = False,则y也为Falseprint(y.grad_fn) #算得y的所有操作数requires_grad = False,则y该属性为Noneprint(y.is_leaf) #算得y的所有操作数requires_grad = False,则y是叶子节点print()
x.requires_grad_(True) #就地修改x是否需要记录梯度print(x.requires_grad) # Trueprint()
print(y.requires_grad) # 算得y的所有操作数不会自动改变,仍是原来的x,所以仍为Fasleprint(y.grad_fn) #Noneprint()
y = x ** 3print(y.requires_grad) # 算得y的所有操作数x重置,requires_grad = True,则y也为Trueprint(y.grad_fn) #存在FalseFalseNoneTrue
True
FalseNone
True<PowBackward0 object at 0x00000251D73BB710>

02

计算梯度

完成计算后,可以调用.backward()来完成所有梯度计算。此Tensor的梯度将累积到叶子节点的.grad属性中,所以每次计算都要将梯度清零。若想要保留中间节点的梯度,则需要在调用.backward()方法前调用.retain_grad()方法。另一方面,.backward()只能完成标量对标量或者标量对张量的求导,所以在使用y.backward()时,如果y是标量,则不需要为backward()传入任何参数;否则,需要传入一个与y同形的Tensor,假设y由自变量x计算而来,w是和y同形的张量,则y.backward(w)的含义是:先计算l = torch.sum(y * w),则l是个标量,然后求l对自变量x的导数。

Example 1(from CS231n Lecture 4)

x = torch.tensor([-2],dtype = torch.float, requires_grad = True) #只有浮点数才能进入backward运算,神经网络均要求浮点数y = torch.tensor([5],dtype = torch.float, requires_grad = True)z = torch.tensor([-4],dtype = torch.float, requires_grad = True)
q = x + yout = q * z
out.backward(torch.tensor([1.])) #浮点数的简单设置形式print(out.grad) #非叶子节点不保留中间梯度print(q.grad) #非叶子节点不保留中间梯度print(x.grad.item())print(y.grad.item())print(z.grad.item())NoneNone-4.0-4.03.0

Example 2(from CS231n Lecture 4)

W = torch.tensor([[1.,1.],[2.,2.]], requires_grad = True)x = torch.ones((2,1), requires_grad = True)out = W @ xprint(W)print(x)print(out)out.backward(torch.ones_like(out)) #保留非叶子节点的梯度print(out.grad)print(W.grad)print(x.grad)tensor([[1., 1.], [2., 2.]], requires_grad=True)tensor([[1.], [1.]], requires_grad=True)tensor([[2.], [4.]], grad_fn=<MmBackward>)Nonetensor([[1., 1.], [1., 1.]])tensor([[3.], [3.]])
Example 3(中断梯度)
x = torch.tensor(1., requires_grad=True)y1 = x ** 2 with torch.no_grad(): #将不需要记录梯度的代码块放入,常用 y2 = x ** 3y3 = y1 + y2
print(x.requires_grad)print(y1, y1.requires_grad) # Trueprint(y2, y2.requires_grad) # Falseprint(y3, y3.requires_grad) # True
y1.retain_grad() #保留非叶子节点的梯度y2.retain_grad() #保留非叶子节点的梯度y3.retain_grad() #保留非叶子节点的梯度
y3.backward()print()print(y3.grad)print(y2.grad)print(y1.grad)print(x.grad) #y2路径不被计算,所以不是5,而是2Truetensor(1., grad_fn=<PowBackward0>) Truetensor(1.) Falsetensor(2., grad_fn=<AddBackward0>) True
tensor(1.)Nonetensor(1.)tensor(2.)

03

神经网络中的实例
Example 1
model = ...optim = torch.optim.SGD(model.parameters()) #将模型参数放入优化器迭代out = model(input)loss = (out - label).sum()model.zero_grad() #清除梯度,不然backward会累加loss.backward() #梯度计算optim.step() #梯度迭代
Example 2 冻结网络
for param in model.parameters(): param.requires_grad = False #将model的所有可训练参数全部冻结,之后就不会计算,减小计算开销model.Dense = nn.Linear(512, 10) #修改需要调试的层,这层的参数是现在唯一可训练的optimizer = optim.SGD(model.Dense.parameters()) #仅仅改变Dense层的参数

本文目的在于学术交流,并不代表本公众号赞同其观点或对其内容真实性负责,版权归原作者所有,如有侵权请告知删除。


“他山之石”历史文章


更多他山之石专栏文章,

请点击文章底部“阅读原文”查看



分享、点赞、在看,给个三连击呗!

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存