pytorch 梯度计算相关内容总结
Posted mazinkaiser1991
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了pytorch 梯度计算相关内容总结相关的知识,希望对你有一定的参考价值。
一、梯度计算准备工作
调用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=<SumBackward0>)
>>> 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是一个函数。
以上是关于pytorch 梯度计算相关内容总结的主要内容,如果未能解决你的问题,请参考以下文章