热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

pytorch梯度计算相关内容总结

一、梯度计算准备工作调用backward()函数前,叶子非叶子节点的grad属性均为none,无论是否设置了requires_gradTrue

一、梯度计算准备工作

调用backward()函数前,叶子/非叶子节点的grad属性均为none,无论是否设置了requires_grad=True(叶子节点),或者调用了retain_grad()(非叶子节点),非叶子节点不能设置requires_grad=True,否则会报错:“RuntimeError: you can only change requires_grad flags of leaf variables.”)。

叶子/非叶子节点定义:

1.叶子节点:

  • 所有requires_grad为false的tensor都是叶子节点,也即is_leaf属性返回true。
  • 若tensor的requires_grad为true,同时是由用户创建,则该tensor为叶子节点。也即不是operation结果的tensor,叶子节点的grad_fn为none。
  • 由requires_grad为false的节点通过operation产生的节点还是叶子节点,此时设置requires_grad为true,不影响是否为叶子节点,但会影响后续节点是否为叶子节点。猜想这么设计的原因是:由于无法判断是否是由operation产生的节点,因此通过设置requires_grad也就无法更新是否为叶子节点。

示例如下:

>>> a = torch.randn((3, 4))
>>> a
tensor([[-1.0351, -0.2712, 2.4718, 0.4248],[ 0.9309, 0.7676, -0.1888, -0.0586],[-0.4290, 0.2478, -0.0056, 0.8502]])
>>> b = torch.randn((3, 4))
>>> b
tensor([[ 0.5519, 0.3557, 0.2577, -0.6338],[ 1.2905, 2.1761, -0.1334, -1.3477],[ 0.8308, 0.1957, 0.1915, 0.1244]])
>>> c = a + b
>>> c.requires_grad
False
>>> c.is_leaf
True
>>> c.requires_grad = True
>>> c.is_leaf
True
// c的requires_grad为true,同时是由operation产生,但是仍为叶子节点,说明requires_grad状态的改变并不能影响是否为叶子节点。

2.非叶子节点

  • 由requires_grad为true的节点通过operation产生,同时operation是可以求导的操作,否则仍为叶子节点。此处对于多元operation,有一个输入是requires_grad为true的节点即可获得非叶子节点。

示例如下:

>>> c
tensor([[-0.4832, 0.0846, 2.7295, -0.2089],[ 2.2214, 2.9437, -0.3222, -1.4063],[ 0.4018, 0.4435, 0.1859, 0.9746]], requires_grad=True)
>>> g = c > 0
>>> g
tensor([[False, True, True, False],[ True, True, False, False],[ True, True, True, True]])
>>> g.is_leaf
True
>>> c
tensor([[-0.4832, 0.0846, 2.7295, -0.2089],[ 2.2214, 2.9437, -0.3222, -1.4063],[ 0.4018, 0.4435, 0.1859, 0.9746]], requires_grad=True)
>>> g = c.sum()
>>> g
tensor(7.5644, grad_fn=)
>>> g.is_leaf
False

叶子/非叶子节点获得grad的方法:

1.叶子节点

  • 设置requires_grad为true

2.非叶子节点

  • 调用retain_grad()。

仅叶子节点,调用backward()后,存在grad。

若某个节点的输入节点的requires_grad为True,则该节点的grad_fn必不为none,该节点的梯度就可以通过调用backward()自动计算。loss计算梯度依据的条件同样如此。

二、梯度计算相关内容

1.使用backward()计算梯度

调用backward()函数后,

  • 对于叶子节点,若设置了requires_grad为true,则可以获得梯度,否则梯度为none。
  • 对于非叶子节点,若调用了retain_grad(),则可以获得梯度,否则梯度也为none。

若计算图中没有一个节点设置了requires_grad为true,则经过loss函数计算的结果仅包含计算结果,grad_fn为none,此时调用backward()函数会出错,错误提示信息如下:

“RuntimeError: element 0 of tensors does not require grad and does not have a grad_fn”

解决方法:将计算图中任意一个节点,设置为requires_grad为true,或者调用retain_grad(),并重新计算loss。

若仅设置requires_grad为true,或者调用retain_grad(),不重新计算loss,loss的grad_fn还是none,此时调用backward(),还是会出现上面的错误。

2.调用backward()函数需要注意的问题:

  • 仅标量可以通过backward()计算梯度,若为非标量调用backward(),会出现以下错误:“RuntimeError: grad can be implicitly created only for scalar outputs”
  • 在调用backward()函数时需要将retain_graph设置为True,否则梯度只能计算一次,第二次再调用backward时,会出现如下错误:“RuntimeError: Trying to backward through the graph a second time, but the saved intermediate results have already been freed. Specify retain_graph=True when calling backward the first time.”
  • 对于tensor可以使用.grad.zero_()对梯度清零。
  • 在使用torch.tensor()初始化tensor时需要保证数据为float或者为复数类型,否则无法设置requires_grad为True。错误提示如下:“RuntimeError: Only Tensors of floating point and complex dtype can require gradients”。
  • 在初始化tensor时如果是使用torch.Tensor初始化则没有requires_grad参数,torch.Tensor是一个类,是torch.FloatTensor的别名,因此默认初始化数据为float32类型,但是requires_grad需要单独设置。如果使用torch.tensor初始化,则可以直接设置requires_grad这一参数,但需要注意使用float类型数据,torch.tensor是一个函数。

 

 

 


推荐阅读
author-avatar
wwhh47123_829
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有