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

李沐动手学深度学习Pytorch-v2笔记【07自动求导代码实现】

文章目录

    • 前言
    • 自动求导实现
    • 非标量变量的反向传播
    • 分离计算
    • Python控制流的梯度计算

前言

关于走动求导的理论知识个人有点难以理解,推荐大家去看https://blog.csdn.net/weixin_42831564/article/details/135658138这篇文章,讲的很好。

自动求导实现

import torchx = torch.arange(4.0)
print(x)

在这里插入图片描述

x.requires_grad_(True)

在这里插入图片描述

print(x.grad)

在这里插入图片描述

#计算y标量
y = 2 * torch.dot(x, x)
print(y)

在这里插入图片描述

y.backward()
print(x.grad)

在这里插入图片描述

print(x.grad == 4 * x)

在这里插入图片描述

x.grad.zero_(),x

在这里插入图片描述

y = x.sum()
y

在这里插入图片描述

y.backward(),y

在这里插入图片描述

print(x.grad)

在这里插入图片描述
为什么 y = x.sum() 的梯度是全 1?

在这里插入图片描述

非标量变量的反向传播

x.grad.zero_()
y = x * x
y.sum().backward()
print(x.grad)
x.grad == 2 * x

在这里插入图片描述
在这里插入图片描述

为什么 y = x * x 后需要 y.sum().backward()

在 PyTorch 中,backward() 只能对标量(scalar,即 0 维张量)进行反向传播,而不能直接对向量/矩阵进行反向传播。因此:

y = x * x 是一个逐元素乘法,得到的 y 仍然是和 x 形状相同的张量(如 [x₁², x₂², x₃², x₄²])。

如果直接调用 y.backward(),PyTorch 会报错,因为 y 不是标量。

解决方法:y.sum().backward()
为了计算 yx 的梯度,我们需要:

y 变成一个标量(通过 sum()mean() 或其他聚合操作)。

y.sum() 计算 x₁² + x₂² + x₃² + x₄²,得到一个标量。

调用 backward(),PyTorch 会自动计算梯度并存储到 x.grad

分离计算

在某层网络需要把参数固定的时候,会用到这个功能

在PyTorch中,y.detach()是一个用于从计算图中分离张量的方法。计算图是PyTorch用于自动微分的关键概念,用于构建和跟踪张量之间的操作。在计算图中,张量的计算历史被记录下来,以便在反向传播时计算梯度。但有时我们希望从计算图中分离出某个张量,使其不再与原始的计算历史关联。这在某些情况下是很有用的,例如当我们只关心使用该张量进行正向传播的结果,并且不需要计算梯度时。

当调用y.detach()时,将返回一个与y相同数据但不再与计算图关联的新张量。这意味着对返回的张量进行操作不会对原始计算图产生任何影响,也不会计算任何梯度。该方法可用于将张量作为输入传递给不允许梯度传播的函数或模型。

在这里插入图片描述

x.grad.zero_()
y = x * x
u = y.detach()  # 把y看成一个常数赋给u u与x无关,是一个常数
print(u)
z = u * x  # z对x的导数 
z.sum().backward()
print(x.grad)
print(u == x.grad)

在这里插入图片描述

# u是常数不是x的函数,y还是x的函数,还是可以对y求x的导数
x.grad.zero_()
y.sum().backward()
print(x.grad)
print(x.grad == 2*x)

在这里插入图片描述

Python控制流的梯度计算

在这里插入图片描述

def f(a):b = a * 2          # (1) b = 2awhile b.norm() < 1000:b = b * 2      # (2) 循环翻倍,直到 ||b|| ≥ 1000if b.sum() > 0:    # (3) 如果 b 的和 > 0,c = b;否则 c = 100*bc = belse:c = 100 * breturn c           # (4) 返回 ca = torch.randn(size=(), requires_grad=True)  # 随机初始化一个标量 a
d = f(a)                                     # 计算 d = f(a)
d.backward()                                 # 反向传播计算梯度
print(a)        # 打印 a 的值
print(d)        # 打印 d 的值
print(a.grad)   # 打印 a 的梯度
print(d / a)    # 计算 d / a
print(a.grad == d / a)  # 判断梯度是否等于 d / a

torch.randn()是一个用于生成服从标准正态分布(均值为0,标准差为1)的随机数的函数

为什么 a.grad == d / a?
关键在于 da 的关系:

da 的线性函数

无论 while 循环执行多少次,b 都是 a 的倍数(b = a * 2 * 2 * ... * 2)。

if-else 分支只是决定 c = bc = 100 * b,所以 c(即 d)仍然是 a 的倍数。

因此,d 可以表示为:d = k⋅a,其中 k 是一个常数(由 while 循环和 if-else 分支决定)。

梯度计算:

d = k * a 求导:在这里插入图片描述
由于 d = k * a,所以 k = d / a

因此:在这里插入图片描述
PyTorch 的 a.grad 存储的就是这个梯度值,所以 a.grad == d / a

http://www.dtcms.com/a/274094.html

相关文章:

  • 进程管理中的队列调度与内存交换机制
  • Jenkins 系统管理与配置
  • 排序算法与前端交互优化
  • 持续集成 简介环境搭建
  • 14 TryHackMe 靶场 Wireshark: The Basics
  • CIU32L051系列 DMA串口无阻塞性收发的实现
  • CentOS 安装 JDK+ NGINX+ Tomcat + Redis + MySQL搭建项目环境
  • Redis5.0.5 漏洞
  • redis的一些疑问
  • windows下安装 redis
  • Redis全栈技术导航:从基础架构到实战案例的完整指南
  • 创客匠人:AI 时代创始人 IP 打造与知识变现的范式迁移
  • 什么是IP关联?跨境卖家如何有效避免IP关联?
  • LeetCode--43.字符串相乘
  • 软件过程模型核心特征与开发流程对照表
  • Android Glide使用与底层机制详解
  • 上位机知识篇---安装包架构
  • imx6ull-系统移植篇2—— U-Boot 命令使用(上)
  • Java 中线程通信方式笔记
  • tailwindCSS === 使用插件自动类名排序
  • ssm框架整合全攻略:从环境搭建到功能实现
  • 什么是Podman?能否替代Docker?Podman快速入门
  • dockerfile 笔记
  • STM32-DAC数模转换
  • 将英语转化为语音 英文转音频 英语转语音朗读
  • 嵌入式八股文之 GPIO
  • RISC-V:开源芯浪潮下的技术突围与职业新赛道 (三)RISC-V架构深度解剖(下)
  • FPGA实现SDI转LVDS视频发送,基于GTX+OSERDES2原语架构,提供2套工程源码和技术支持
  • Spring注解IoC与JUnit整合实战
  • MyBatis-Plus通用中等、大量数据分批查询和处理