Pytorch教程

【注意】pytorch求导注意-反复backward与梯度累加

作者 : 老饼 发表日期 : 2023-12-01 15:35:51 更新日期 : 2024-01-19 08:31:40
本站原创文章,转载请说明来自《老饼讲解-深度学习》www.bbbdata.com



在pytorch中使用自动求梯度时,往往需要反复backward

本文讲解如何反复backward以及多次backward时需要注意清空梯度




一、pytorch求导注意事项-必须保留计算图才能反复backward 



本节指出pytorch中backward默认是一次性的,以及如何实现多次backward



01.backward是一次性的

pytorch的计算图是动态图,它在每次调用backward后就会释放,

也就是默认情况下backward是一次性的,再次backward会报错

示例如下:

import torch
x = torch.tensor([1,2],dtype=(float),requires_grad=True)   # 定义参数x
A = torch.tensor([2,3],dtype=(float))                      # 常量A,不需梯度
y = A@(x*x)                                                # 定义y
y.backward()                                               # 向后传播,更新梯度
#-----再次传播------------------------
y.backward()                                               # 再次向后传播,会报错

运行结果报错如下:

RuntimeError: Trying to backward through the graph a second time (or directly access saved tensors after they have already been freed). 
Saved intermediate values of the graph are freed when you call .backward() or autograd.grad(). 
Specify retain_graph=True if you need to backward through the graph a second time or if you need to access saved tensors after calling backward.


02.保留计算图才能多次backward

如果要让pytroch的backward反复使用,必须在backward时使用retain_graph=True来保留计算图,

在本次backward时使用了retain_graph=True,下次就可以继续使用backward,实现多次反复backward

示例如下:

import torch
x = torch.tensor([1,2],dtype=(float),requires_grad=True)   # 定义参数x
A = torch.tensor([2,3],dtype=(float))                      # 常量A,不需梯度
y = A@(x*x)                                                # 定义y
y.backward(retain_graph=True)                              # 向后传播,并保留计算图
y.backward()                                               # 再次向后传播

可以看到,示例中共运行了两次backward,但不会报错





二、pytorch求导注意事项-反复backward时梯度的累加性



本节指出反复backward时,需要注意清空梯度



01.反复backward时梯度的累加性

在将backward设置为retain_graph=True后,可以实现多次重复backward

但必须注意的是,每次backward时梯度都是累加的

示例如下:

import torch
x = torch.tensor([1,2],dtype=(float),requires_grad=True)   # 定义参数x
A = torch.tensor([2,3],dtype=(float))                      # 常量A,不需梯度
y = A@(x*x)                                                # 定义y
y.backward(retain_graph=True)                              # 向后传播,并保留计算图
print('x.grad:',x.grad)                                    # 打印x的梯度
#-----多次传播------------------------
y.backward(retain_graph=True)                              # 再次向后传播
print('x.grad:',x.grad)                                    # 打印x的梯度
y.backward(retain_graph=True)                              # 再再次向后传播
print('x.grad:',x.grad)                                    # 打印x的梯度

运行结果如下:

x.grad: tensor([ 4., 12.], dtype=torch.float64)
x.grad: tensor([ 8., 24.], dtype=torch.float64)
x.grad: tensor([12., 36.], dtype=torch.float64)

可以看到,在多次backward时,每一次都是把自变量的梯度累加到自变量的原有梯度上

因此,在pytorch中,如果需要多次backward,且每次计算的是当前梯度,则需要先用grad.zero_()把梯度进行清空

示例如下:

import torch
x = torch.tensor([1,2],dtype=(float),requires_grad=True)   # 定义参数x
A = torch.tensor([2,3],dtype=(float))                      # 常量A,不需梯度
y = A@(x*x)                                                # 定义y
y.backward(retain_graph=True)                              # 向后传播,并保留计算图
print('x.grad:',x.grad)                                    # 打印x的梯度
#-----多次传播------------------------
x.grad.zero_()                                             # 清空x的梯度
y.backward(retain_graph=True)                              # 再次向后传播
print('x.grad:',x.grad)                                    # 打印x的梯度
x.grad.zero_()                                             # 清空x的梯度
y.backward(retain_graph=True)                              # 再再次向后传播
print('x.grad:',x.grad)                                    # 打印x的梯度

运行结果如下:

x.grad: tensor([ 4., 12.], dtype=torch.float64)
x.grad: tensor([ 4., 12.], dtype=torch.float64)
x.grad: tensor([ 4., 12.], dtype=torch.float64)

从结果可以看到,使用grad.zero_()把梯度清空后,每次backward后得到的梯度值才是当前的梯度值






好了,以上就是pytorch中求导时如何反复backward以及需要注意梯度累加性,需要清零梯度的全部内容了~







 End 






联系老饼