Pytorch中反向传播

【深度学习理论】一文搞透 pytorch 中的 tensor、autograd、反向传播和计算图
动态计算图

https://www.cnblogs.com/picassooo/p/13748618.html

https://www.cnblogs.com/picassooo/p/13818952.html

# Pytorch中loss.backward()和torch.autograd.grad的使用和区别

总结:

loss.backward()和torch.autograd.grad区别

相同点:

两种梯度方法都会在反向传播求导的时候释放计算图,如果需要再次做自动求导,因为计算图已经不再了,就会报错 。如果要在反向传播的时候保留计算图 ,可以设置retain_graph=True

不同点

  1. 累加
    loss.backward()会将求导结果累加在grad上。这也是为什么我们在训练每个batch的最开始,需要对梯度清零的原因。
    torch.autograd.grad不会将求导结果累加在grad上。
  2. 叶子节点梯度
    torch.autograd.grad可以获取非叶子节点的梯度。
    loss.backward()后,非叶子节点的导数计算完成之后就会被清空。
计算图保留
python 复制代码
import torch
x=torch.tensor(3,dtype=torch.float,requires_grad=True)
y=x*x
z=2*y
z.backward(retain_graph=True)
print(x.grad)   #12
z.backward()
print(x.grad) #24

不保留计算图会报错。

同一张计算图计算两次,相当于两次grad相加,以上代码效果等同于

python 复制代码
import torch
x=torch.tensor(3,dtype=torch.float,requires_grad=True)
y=x*x
z=2*y
z.backward()
print(x.grad) 
x.grad  = x.grad *2
print(x.grad) 

https://www.cnblogs.com/luckyscarlett/p/10555632.html

python 复制代码
import torch
x = torch.randn((1,4),dtype=torch.float32,requires_grad=True)
y = x ** 2
z = y * 4
print(x)
print(y)
print(z)
loss1 = z.mean()
loss2 = z.sum()
print(loss1,loss2)
loss1.backward()    # 这个代码执行正常,但是执行完中间变量都free了,所以下一个出现了问题
print(loss1,loss2)
loss2.backward()    # 这时会引发错误

计算节点数值保存了,但是计算图x-y-z结构被释放了,而计算loss2的backward仍然试图利用x-y-z的结构,因此会报错。

使用

python 复制代码
loss1.backward(retain_graph=True)# 这里参数表明保留backward后的中间参数。

即可

叶子节点

一般地,由用户自己创建的张量为叶子节点。另外,神经网络中各层的权值weight和偏差bias对应的张量也为叶子节点。由叶子节点得到的中间张量为非叶子节点。

Pytorch中的张量有一个is_leaf的属性。若一个张量为叶子节点,则其is_leaf属性就为True,若这个张量为非叶子节点,则其is_leaf属性就为False。

在反向传播中,叶子节点可以理解为不依赖其它张量的张量

requires_grad_()

只有在是叶子节点且require_grad为True的时候,反向计算才会保留计算图。

requires_grad_作用是将Tensor的require_grad属性变为True

应用的情况为手动生成的Tensor,但忘记将该属性变为True

python 复制代码
import torch
x=torch.tensor(3.) # 此处须为torch.float类型,如果不是,则需要进行如下的转换
print(x.requires_grad)  #False
# 如果在创建张量x时,没有将requires_grad参数设置为True,则可以通过下式进行设定
x.requires_grad_(True) 
grad_fn

如果一个张量是通过一些操作从其他张量创建的,那么这个张量将有一个 grad_fn 属性指向执行的函数。但是,对于叶子张量(即那些直接创建的张量,而不是通过计算得到的),它们默认没有 grad_fn

保留非叶子节点梯度

在非叶子节点之后,加上 "非叶子节点.retain_grad() 保留梯度

python 复制代码
```python
import torch
x=torch.tensor(3,dtype=torch.float,requires_grad=True)
y=x*x
z=2*y
z.backward()
print(z.grad)  # none

修改后代码:

python 复制代码
import torch
x=torch.tensor(3,dtype=torch.float,requires_grad=True)
y=x*x
z=2*y
z.retain_grad()
z.backward()
print(z.grad)  # 1
计算图可视化

20天吃掉那只Pytorch

python 复制代码
from torch import nn 
import torch 
class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.w = nn.Parameter(torch.randn(2,1))
        self.b = nn.Parameter(torch.zeros(1,1))

    def forward(self, x):
        y = x@self.w + self.b
        return y

net = Net()

net = Net()
from torch.utils.tensorboard import SummaryWriter
writer = SummaryWriter('../data/tensorboard')
writer.add_graph(net,input_to_model = torch.rand(10,2))
writer.close()
detach()和clone()的区别
python 复制代码
import torch
# 创建一个张量,并启用梯度追踪
x = torch.randn(3, requires_grad=True)
# 做一些计算
y = 2 * x  # y 参与反向传播
# z = y.clone()  # 创建 y 的副本,z 会有与 y 相同的计算图
z = y.detach()  # 创建 y 的副本,z 会有与 y 相同的计算图
# 计算一个目标函数
loss = sum(5 * x + 10 * z)
# 执行反向传播
loss.backward()
# 打印梯度
print(f"x.grad: {x.grad}")  

clone()保存计算图,detach()不保存计算图

clone().detach()和detach()

区别在于是否共享内存空间。修改一个另外的变量也会修改,但是是否有计算图有区别。

python 复制代码
import torch

# 创建一个启用了梯度追踪的张量
sub_buffer = torch.ones(3, requires_grad=True)

# 使用 detach() 断开计算图
sub_buffer_detached = sub_buffer.detach()
sub_buffer_clone = sub_buffer.clone().detach()

# 检查原始张量和 detatched 张量的计算图
print(f"sub_buffer requires_grad: {sub_buffer.requires_grad}")  # True
print(f"sub_buffer_detached requires_grad: {sub_buffer_detached.requires_grad}")  # False
print(f"sub_buffer.grad_fn: {sub_buffer.grad_fn}")  # 显示计算图信息
print(f"sub_buffer_detached.grad_fn: {sub_buffer_detached.grad_fn}")  # 显示计算图信息

# 修改 detatched 张量的值并查看是否影响原始张量
sub_buffer_detached += 1
print(f"sub_buffer: {sub_buffer}")
print(f"sub_buffer_detached: {sub_buffer_detached}")



# 修改副本的值并查看是否影响原始张量
sub_buffer_clone += 1
print(f"sub_buffer: {sub_buffer}")
print(f"sub_buffer_clone: {sub_buffer_clone}")

print(f"sub_buffer id: {id(sub_buffer)}")
print(f"sub_buffer_clone id: {id(sub_buffer_clone)}")  # 不同
print(f"sub_buffer_detached id: {id(sub_buffer_detached)}")  # 相同
#检查是否断开计算图
print(f"sub_buffer requires_grad: {sub_buffer.requires_grad}")
print(f"sub_buffer_detached requires_grad: {sub_buffer_detached.requires_grad}")

结果

python 复制代码
sub_buffer requires_grad: True
sub_buffer_detached requires_grad: False
sub_buffer.grad_fn: None
sub_buffer_detached.grad_fn: None
sub_buffer: tensor([2., 2., 2.], requires_grad=True)
sub_buffer_detached: tensor([2., 2., 2.])
sub_buffer: tensor([2., 2., 2.], requires_grad=True)
sub_buffer_clone: tensor([2., 2., 2.])
sub_buffer id: 140125728706704
sub_buffer_clone id: 140125728663328
sub_buffer_detached id: 140125728706784
sub_buffer requires_grad: True
sub_buffer_detached requires_grad: False
torch.autograd.backward
python 复制代码
import torch

# 创建两个张量,并启用梯度追踪
x = torch.randn(3, requires_grad=True)
y = torch.randn(3, requires_grad=True)

# 计算一个输出张量
out = x * y  # out 参与计算图

# 使用 detach() 从计算图中分离出一个张量
out_detached = out.detach()  # 这里使用 detach,断开 out 的计算图
out_detached.requires_grad_()
output = out_detached * 66
# 定义梯度张量,通常用于自定义梯度传播
grad_out = torch.ones_like(out_detached)  # grad_out 是给定的梯度

# 使用 backward 进行反向传播
torch.autograd.backward(tensors=output, grad_tensors=grad_out)

# 打印梯度
print(f"x.grad: {x.grad}")  # 由于 out_detached 不计算梯度,x 不会被更新
print(f"y.grad: {y.grad}")  # y 也不会被更新
print(f"out.grad: {out_detached.grad}")  # y 也不会被更新
print(f"out.leaf: {out_detached.is_leaf}")  # y 也不会被更新

输出

python 复制代码
x.grad: None
y.grad: None
out.grad: tensor([66., 66., 66.])
out.leaf: True

torch.autograd.backward反向传播,所得结果直接体现在叶子节点的梯度值上

再比如

python 复制代码
import torch

# 创建两个张量,并启用梯度追踪
x = torch.randn(3, requires_grad=True)
y = torch.randn(3, requires_grad=True)

# 计算一个输出张量
out = x * y  # out 参与计算图

# 使用 detach() 从计算图中分离出一个张量
out_detached = out.detach()  # 这里使用 detach,断开 out 的计算图
out_detached.requires_grad_()
output = out_detached * 66
output2 = out_detached * 34
# 定义梯度张量,通常用于自定义梯度传播
grad_out = torch.ones_like(out_detached)  # grad_out 是给定的梯度

# 使用 backward 进行反向传播
torch.autograd.backward(tensors=(output,output2),grad_tensors=(grad_out,grad_out))

# 打印梯度
print(f"x.grad: {x.grad}")  # 由于 out_detached 不计算梯度,x 不会被更新
print(f"y.grad: {y.grad}")  # y 也不会被更新
print(f"out.grad: {out_detached.grad}")  # y 也不会被更新
print(f"out.leaf: {out_detached.is_leaf}")  # y 也不会被更新

结果为

python 复制代码
x.grad: None
y.grad: None
out.grad: tensor([100., 100., 100.])
out.leaf: True
相关推荐
张申傲8 分钟前
大模型翻译能力评测
人工智能·ai·chatgpt·机器翻译
机器学习之心11 分钟前
RBF神经网络预测结合NSGAII多目标优化
人工智能·深度学习·神经网络
东方佑19 分钟前
HtmX 表达的创建和交互
python
大大宝的博客30 分钟前
智能化Kubernetes管理:AI与ChatGPT提升运维效率的创新实践
人工智能·chatgpt·kubernetes
说私域34 分钟前
全渠道供应链变革下“小程序 AI 智能名片 S2B2C 商城系统”的赋能与突破
人工智能·小程序·开源
湫ccc42 分钟前
《Python基础》之Python中可以转换成json数据类型的数据
python·json
Zilliz Planet1 小时前
Milvus 2.5:全文检索上线,标量过滤提速,易用性再突破!
后端·python·django·全文检索·milvus
shengjk11 小时前
从零开发操作系统-聊一聊C语言中的头文件
java·大数据·c语言·人工智能·后端·aigc·编程语言
科研online1 小时前
AI与ArcGIS Pro的地理空间分析和可视化
人工智能·arcgis
xin2cd1 小时前
LSTM卫星轨道预测(一)
人工智能·rnn·lstm