当前位置: 首页 > article >正文

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()后,非叶子节点的导数计算完成之后就会被清空。
计算图保留
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相加,以上代码效果等同于

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

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的结构,因此会报错。
使用

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

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
import torch
x=torch.tensor(3,dtype=torch.float,requires_grad=True)
y=x*x
z=2*y
z.backward()
print(z.grad)  # none

修改后代码:

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

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()的区别
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()

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

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}")

结果

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
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 也不会被更新

输出

x.grad: None
y.grad: None
out.grad: tensor([66., 66., 66.])
out.leaf: True

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

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 也不会被更新

结果为

x.grad: None
y.grad: None
out.grad: tensor([100., 100., 100.])
out.leaf: True

http://www.kler.cn/a/414686.html

相关文章:

  • ATTCK红队评估实战靶场(二)
  • Ubuntu问题 -- 使用scp将本机文件传输至ubuntu服务器中
  • 环境崩溃后的重新建立
  • 导入100道注会cpa题的方法,导入试题,自己刷题
  • 2024年12月计划(ue5太阳系+ue独立游戏+freex+GPU精粹泛读催眠)
  • 数据结构(Java版)第四期:ArrayLIst和顺序表(上)
  • C++初阶(十六)--STL--list的模拟实现
  • QT QFormLayout控件 全面详解
  • C++中的volatile关键字
  • torch.set_printoptions
  • 房屋出租出售预约系统支持微信小程序+H5+APP
  • 【含开题报告+文档+PPT+源码】基于SpringBoot的艺术培训学校管理系统的设计与实现
  • vue2 中使用 Ag-grid-enterprise 企业版
  • 解决数据传送问题:内网http传输
  • LeetCode 力扣 热题 100道(十)回文链表(C++)
  • 【青牛科技】D1671 75Ω 带4级低通滤波的单通道视频放大电 路芯片介绍
  • 【论文阅读】三平面相关与变体
  • oracle 12c查看执行过的sql及当前正在执行的sql
  • 爬虫cookie反爬------加速乐(jsl)
  • 第三十三章 UDP 客户端 服务器通信 - IPv4 和 IPv6
  • 【软考速通笔记】系统架构设计师⑦——系统架构设计基础知识
  • 亚马逊开发视频人工智能模型,The Information 报道
  • c++类模板成员函数的特化
  • 高防服务器HOT:网络安全的无形盾牌,护航业务稳定
  • Android 是否支持AB分区
  • PPT不能编辑,按钮都是灰色,怎么办?