当前位置: 首页 > 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.lryc.cn/news/585619.html

相关文章:

  • 标准化模型格式ONNX介绍:打通AI模型从训练到部署的环节
  • 第十五章 STL(stack、queue、list、set、map容器使用)
  • Nginx 添加 Stream 模块(不覆盖已安装内容)
  • Java 中使用 Stream 将 List 转换为 Map 实战笔记(生产级版)
  • 【Freertos实战】零基础制作基于stm32的物联网温湿度检测(教程非常简易)持续更新中.........
  • 计算机网络第三章(5)——数据链路层《广域网》
  • 【网络编程】KCP——可靠的 UDP 传输协议——的知识汇总
  • 触控屏gt1947
  • 数据治理到底是什么?搞清这四件事,你就彻底明白了!
  • 【C++】内联函数inline以及 C++入门(4)
  • 静态路由综合配置实验报告
  • python实现DoIP基本通信(收发报文)
  • 深入探索Kafka Streams:企业级实时数据处理实践指南
  • 外媒:蚂蚁数科等科技公司在香港数字资产枢纽建设中显身手
  • 基于强化学习的智能推荐系统优化实践
  • 星云穿越与超光速飞行特效的前端实现原理与实践
  • 运筹说 第140期 | 从直觉到算法:这些奠基人如何塑造了启发式方法的科学根基?
  • 分享|2025年机器学习工程师职业技术证书报考指南
  • ABP VNext + Microsoft YARP:自定义反向代理与请求路由
  • 七牛云运维面试题及参考答案
  • RabbitMQ 之顺序性保障
  • 单链表,咕咕咕
  • 鸿蒙系统安全机制全解:安全启动 + 沙箱 + 动态权限实战落地指南
  • C语言易错点(二)
  • SEQUENCE在RAC多实例开启CACHE的NEXTVAL数值乱序问题
  • 打破内网壁垒,轻松实现安防视频的云端汇聚与P2P超低延迟播放
  • 【unity编辑器开发与拓展EditorGUILayoyt和GUILayoyt】
  • 数据蓝海里的合规漩涡
  • Windows GNU Radio避坑
  • CUDA程序中的Benchmark耗时测量方法与工具推荐