先上结论:
1、叶子结点定义:
(1)不依赖其它任何结点的张量
(2)依赖其它张量,但其依赖的所有张量的require_grad=False
# 判断方法:查看is_leaf属性
2、张量梯度是否会被计算:
require_grad=True,且依赖其的张量不全为require_grad=False,该张量梯度会被计算
# 判断方法:backward之后查看张量的.grad属性(中间变量满足上述要求的梯度肯定也会被计算,只是backward之后会被释放掉,无法查看。中间变量其下面的叶子结点梯度被计算,根据链式法则,侧面也可证明中间变量的梯度肯定被计算了,因此本文只采用叶子结点说明该规律)
3、张量梯度是否会被保存(前提: 张量梯度可以被计算):
(1)是叶子结点
(2)是非叶子结点,但retain_grad=True
# 判断方法: backward之后查看张量的.grad属性
然后看例子:
# 两个例子先证明第一条:叶子结点定义
import torch# 两个例子先证明第一条:叶子结点定义a = torch.tensor(1.,requires_grad=True)b = torch.tensor(1.,requires_grad=False)d = a+bd.backward()print(a.is_leaf) # True(不依赖其它任何张量的结点)print(b.is_leaf) # True(不依赖其它任何张量的结点)print(d.is_leaf) # False (依赖其它张量a和b,但张量a的require_grad=True,所以d不是叶子结点)c = torch.tensor(1., requires_grad=False)e = b+cprint(b.is_leaf)# True(不依赖其它任何张量的结点)print(c.is_leaf)# True(不依赖其它任何张量的结点)print(e.is_leaf)# True (依赖其它张量b和c,但张量a和c的require_grad=False,所以e仍然是叶子结点)# e.backward()会报错,因为所有结点的require_grad=False,因此不需要求梯度
# 两个例子再证明第二条:张量梯度是否会被计算
import torch# 两个例子再证明第二条:张量梯度是否会被计算# require_grad=True的张量,且依赖其的张量不全为require_grad=False梯度会被计算a = torch.tensor(1.,requires_grad=True) # a require_grad=True,因此a的梯度会被计算b = torch.tensor(1.,requires_grad=False)# b require_grad=False,因此b的梯度不会被计算d = a+b # d require_grad=True,因此d的梯度会被计算c = torch.tensor(1., requires_grad=True)# c require_grad=True,因此c的梯度会被计算e = a+c # e require_grad=True,因此e的梯度会被计算d = d.detach()print(d.requires_grad)# d require_grad=False,因此d的梯度不会被计算f = d+e f.backward()print(a.grad) # a有梯度# require_grad=True的张量,但依赖其的张量全为require_grad=False,梯度不会被计算a = torch.tensor(1.,requires_grad=True) # a require_grad=True, 因此a的梯度会被计算b = torch.tensor(1.,requires_grad=False)# b require_grad=False, 因此b的梯度不会被计算d = a+b # d require_grad=True, 因此d的梯度会被计算c = torch.tensor(1., requires_grad=True)# c require_grad=True,因此c的梯度会被计算d = d.detach()# d require_grad=False, 因此d的梯度不会被计算f = d+c # f require_grad=True,因此f的梯度会被计算f.backward()print(a.grad) # a没有梯度
# 再证明第三条: 张量梯度是否会被保存,前提是张量的梯度能被计算(既满足第二条)
import torch# 再证明第三条: 张量梯度是否会被保存,前提是张量的梯度能被计算(既满足第二条)# (1)叶子结点的梯度会被保存a = torch.tensor(1.,requires_grad=True) # a的require_grad= True,且a是一个叶子结点(a.is_leaf=True),所以backward之后a的梯度会被保存。(满足条件3)b = torch.tensor(1.,requires_grad=False)# b是一个叶子结点(b.is_leaf=True),但b的require_grad= False,所以backward之后b的梯度不会被保存.(不满足条件3)d = a+b # d的require_grad= True, 但d不是一个叶子结点,所以backward之后b的梯度不会被保存。(不满足条件3)d.backward()print(a.is_leaf)print(a.grad) # a的梯度被保存# 感悟: 神经网络各层里面的参数require_grad=True(属于Parameter类型,其初始化的时候默认require_grad=True),并且如果上层不会被断开(满足梯度可以被计算条件)。且神经网络里面各层的参数都是叶子结点(从计算图可以得知满足1里面第一条),因此满足梯度保存条件第一条。因此其梯度一定会被保存。满足了以上两条,因此backward的时候其梯度一定会被计算并且保存,从而step的时候才能用于梯度更新)# (2)非叶子结点,但retain_grad=True的张量梯度也会被保存。a = torch.tensor(1.,requires_grad=True) # a的require_grad= True,且a是一个叶子结点(a.is_leaf=True),所以backward之后a的梯度会被保存。(满足条件3)b = torch.tensor(1.,requires_grad=False)# b是一个叶子结点(b.is_leaf=True),但b的require_grad= False,所以backward之后b的梯度不会被保存.(不满足条件3)d = a+b # d的require_grad= True, 但d不是一个叶子结点,所以backward之后b的梯度不会被保存。(不满足条件3)print(d.is_leaf)# Falsed.retain_grad() # retain_grad=Trued.backward()print(d.is_leaf)# Falseprint(d.grad) # d的梯度被保存