pytorch中反向传播,梯度保留,梯度分离,梯度清零,梯度累加等相关问题解析

Posted 千禧皓月

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了pytorch中反向传播,梯度保留,梯度分离,梯度清零,梯度累加等相关问题解析相关的知识,希望对你有一定的参考价值。

梯度保留

Q(question):pytorch autograd 非叶子节点的梯度会保留么?
A(answer):
 1. 只有叶子节点的梯度得到保留,中间变量的梯度默认不保留
 2. 叶子节点的值在求梯度前不允许更改	  

示例

>>> a = torch.tensor(2.0, requires_grad=True)
>>> b = torch.tensor(3.0)
>>> c=a*a*5
>>> d=c*c*b
>>> d.backward()
>>> a.grad
tensor(2400.)

注:复制完代码请删去>>>和输出结果再运行,建议手打一遍,这样更能加深理解
默认创建的张量(tensor)为常量,常量无法计算,需要设置requires_grad=True才能计算梯度,所以a,c,d可以计算梯度,b不可以计算梯度。反向传播后,只有叶子节点可以保留梯度,所以这里只有a的梯度会保留,如果需要保留c,d的梯度,需要使用retain_grad()函数
注:大家可以尝试一下计算图的可视化,我这里暂时就不加了,有空补上

>>> a = torch.tensor(2.0, requires_grad=True)
>>> b = torch.tensor(3.0)
>>> c=a*a*5
>>> d=c*c*b
>>> c.retain_grad()
>>> d.retain_grad()
>>> d.backward()
>>> a.grad         #求导后2*b*c*10*a,把c用a*a*5来代,即2*b*a*a*5*10*a为2400
tensor(2400.)     
>>> c.grad        #求导后2*b*c,把c用a*a*5来代,即2*b*a*a*5为120
tensor(120.)    
>>> d.grad
tensor(1.)

注:复制完代码请删去>>>输出结果再运行,建议手打一遍,这样更能加深理解
d.backward() 只能计算一次,因为执行一次 d.backward() 后,计算图的缓冲区已经被释放,再次执行将报如下错误:

RuntimeError: Trying to backward through the graph a second time (or directly access saved tensors after they have already been freed). 

如果需要再次使用,则需要在前一次执行backward()时加上retain_graph=True,此时会对之前求到的梯度进行叠加

a = torch.tensor(2.0, requires_grad=True)
b = torch.tensor(3.0)
c=a*a*5
d=c*c*b
c.retain_grad()
d.retain_grad()
d.backward(retain_graph=True)
print(a.grad)
print(c.grad)
print(d.grad)
'''
输出
tensor(2400.)
tensor(120.)
tensor(1.)
'''
d.backward()
print(a.grad)
print(c.grad)
print(d.grad)
'''
输出
tensor(4800.)
tensor(240.)
tensor(2.)
'''

原文链接:
https://blog.csdn.net/liangjiu2009/article/details/106980762
https://blog.csdn.net/go___on/article/details/124294061
https://blog.csdn.net/weixin_43479947/article/details/126989990

梯度清零

在PyTorch中,对模型参数的梯度置0时通常使用两种方式:model.zero_grad()和optimizer.zero_grad()。二者在训练代码都很常见,那么二者的区别在哪里呢?

model.zero_grad()
model.zero_grad()的作用是将所有模型参数的梯度置为0。其源码如下:

for p in self.parameters():
    if p.grad is not None:
        p.grad.detach_()
        p.grad.zero_()

optimizer.zero_grad()
optimizer.zero_grad()的作用是清除所有可训练的torch.Tensor的梯度。其源码如下:

for group in self.param_groups:
    for p in group['params']:
        if p.grad is not None:
            p.grad.detach_()
            p.grad.zero_()

总结

1.因此,当使用optimizer=optim.Optimizer(net.parameters())设置优化器时,此时优化器中的param_groups等于模型中的parameters(),此时,二者是等效的,从二者的源码中也可以看出来。

2.当多个模型使用同一个优化器时,二者是不同的,此时需要根据实际情况选择梯度的清除方式。

梯度分离

detach()函数,返回一个新的tensor,是从当前计算图中分离下来的,但是仍指向原变量的存放位置,其grad_fn=Nonerequires_grad=False,得到的这个tensor永远不需要计算其梯度,不具有梯度grad,即使之后重新将它的requires_grad置为true,它也不会具有梯度grad。

注意:返回的tensor和原始的tensor共享同一内存数据。in-place函数修改会在两个tensor上同时体现(因为它们共享内存数据),此时当要对其调用backward()时可能会导致错误。

原文链接:https://blog.csdn.net/qq_36605433/article/details/120617031

梯度累加

原文链接 https://blog.csdn.net/ltochange/article/details/123629686
原文链接:https://www.cnblogs.com/lart/p/11628696.html

以上是关于pytorch中反向传播,梯度保留,梯度分离,梯度清零,梯度累加等相关问题解析的主要内容,如果未能解决你的问题,请参考以下文章

Torch反向传播时出错或者梯度为NaN的问题排查

PyTorch 线性代数梯度

pytorch学习笔记第三篇———自动梯度(torch.autograd)

自动求梯度(pytorch版本)——2.20

神经网络反向传播梯度计算数学原理

反向传播梯度求解推导