PyTorch:梯度计算之反向传播函数backward()

2023-11-11

一、计算图

计算图,是一种用来描述计算的有向无环图。

我们假设一个计算过程,其中 X 1 \mathbf{X_1} X1 W 1 \mathbf{W_1} W1 W 2 \mathbf{W_2} W2 Y \mathbf{Y} Y都是 N N N维向量。
X 2 = W 1 X 1 \mathbf{X_2} = \mathbf{W_1}\mathbf{X_1} X2=W1X1
y = W 2 X 2 \mathbf{y} = \mathbf{W_2}\mathbf{X_2} y=W2X2
L = ∑ i = 1 N ( Y i − y i ) 2 L = \sum_{i=1}^N(Y_i - y_i)^2 L=i=1N(Yiyi)2
上述过程,用计算图表现出来,就是下图。

从这张图中可以看出, X 1 \mathbf{X_1} X1 W 1 \mathbf{W_1} W1 W 2 \mathbf{W_2} W2 Y \mathbf{Y} Y是直接创建的,它们是叶子节点, X 2 \mathbf{X_2} X2 y \mathbf{y} y L L L是经过计算得到的,它们不是叶子节点。

计算导数。

如果我们想跨越若干层计算导数,如计算 ∂ L ∂ X 1 \frac{\partial L}{\partial \mathbf{X_1}} X1L的值,则需要根据求导的链式法则,一层一层的计算下去。
∂ L ∂ X 1 = ∂ L ∂ y ∂ y ∂ X 2 ∂ X 2 ∂ X 1 \frac{\partial L}{\partial \mathbf{X_1}}=\frac{\partial L}{\partial \mathbf{y}}\frac{\partial \mathbf{y}}{\partial \mathbf{X_2}}\frac{\partial \mathbf{X_2}}{\partial \mathbf{X_1}} X1L=yLX2yX1X2
∂ L ∂ W 1 = ∂ L ∂ y ∂ y ∂ X 2 ∂ X 2 ∂ W 1 \frac{\partial L}{\partial \mathbf{W_1}}=\frac{\partial L}{\partial \mathbf{y}}\frac{\partial \mathbf{y}}{\partial \mathbf{X_2}}\frac{\partial \mathbf{X_2}}{\partial \mathbf{W_1}} W1L=yLX2yW1X2
∂ L ∂ W 2 = ∂ L ∂ y ∂ y ∂ W 2 \frac{\partial L}{\partial \mathbf{W_2}}=\frac{\partial L}{\partial \mathbf{y}}\frac{\partial \mathbf{y}}{\partial \mathbf{W_2}} W2L=yLW2y

二、backward()函数

PyTorch提供了autograd包来自动根据输入和前向传播构建计算图,其中,backward函数可以很轻松的计算出梯度。

Tensor在pytorch中用来表示张量,上例中的 X 1 \mathbf{X_1} X1 W 1 \mathbf{W_1} W1 W 2 \mathbf{W_2} W2都是张量,且均为直接被我们创建的。如果我们想使用autograd包让它们参与梯度计算,则需要在创建它们的时候,将.requires_grad属性指定为true。

注意,在pytorch中,只有浮点类型的数才有梯度,因此在定义张量时一定要将类型指定为float型。

x1 = torch.tensor([2, 3, 4, 5], dtype=torch.float, requires_grad=True)
print(x1)

输出为

当然对于没有指定.requires_grad属性的向量,也可以在后续进行指定,或者使用requires_grad_函数进行指定。

w1 = torch.ones(4)
w1.requires_grad = True
print(w1)

w2 = torch.Tensor([1, 2, 3, 4])
w2.requires_grad_(True)
print(w2)

输出为

接下来进行运算操作。

x2 = x1 * w1
print(x2)

输出为

在这个运算中,pytorch会构建一个动态地创建一个计算图,即动态计算图(Dynamic Computation Graph, DCG),计算图中的每一个节点,都会封装若干个属性。下图为 X 2 = X 1 W 1 \mathbf{X_2} = \mathbf{X_1}\mathbf{W_1} X2=X1W1这一计算的计算图。

解释一下各属性的含义。

  1. data:存储的Tensor的值。
  2. requires_grad:该节点是否参与反向传播图的计算,如果为True,则参与计算;如果为False则不参与。
  3. grad:存储梯度值。requires_grad为False时,该属性为None;requires_grad为True且在调用过其他节点的backward后,grad保存对这个节点的梯度值,否则为None。
  4. grad_fn:表示用于计算梯度的函数,即创建该Tensor的Function。如果该Tensor不是通过计算得到的,则grad_fn为None;如果是通过计算得到的,则返回该运算相关的对象。
    举个例子,a为直接创建的Tensor,b和c由计算得到,则a、b、c的grad_fn如下所示。
a = torch.ones(2, 2, requires_grad=True)
print(a.grad_fn)
b = a + 2
print(b.grad_fn)
c = b * b * 3
print(c.grad_fn)

输出为

  1. is_leaf:用True和False表示是否为叶子节点。

然后继续计算直到得到 L L L。这个过程是正向传播的过程,会继续动态的生成计算图。

y = x2 * w2
Y = torch.ones(2, 2, requires_grad=True)
L = (Y - y).mean()

在本例中,如果我们想求 ∂ L ∂ X 1 \frac{\partial \mathbf{L}}{\partial \mathbf{X_1}} X1L,则需要首先使用backward()函数对 L L L做反向传播。backward()实际上是通过DCG图从根张量追溯到每一个叶子节点,然后计算将计算出的梯度存入每个叶子节点的.grad属性中。由于 L L L是一个标量,因此backward()函数中不需要传入任何参数。代码如下

L.backward()

这一步后 L L L针对每一个变量的梯度都会被求出,并存放在对应节点的.grad属性中。如果我们想要 ∂ L ∂ X 1 \frac{\partial L}{\partial X_1} X1L,只需要读取x1.grad即可。

print(x1.grad)

三、backward()函数的参数grad_tensor

上面的例子中,作为输出的 L L L是一个标量,即神经网络只有一个输出,backward不需要传入参数。但如果输出是一个向量,计算梯度需要传入参数。例如

x = torch.tensor([0.0, 2.0, 8.0], requires_grad=True)
y = torch.tensor([5.0, 1.0, 7.0], requires_grad=True)
z = x * y
print(z)

结果如下。可以看出z也是一个张量。

如果想求z对x或y的梯度,则需要将一个外部梯度传递给z.backward()函数。这个额外被传入的张量就是grad_tensor。

z.backward(torch.FloatTensor([1.0, 1.0, 1.0]))

为什么要传入这个张量呢?传入这个参数,本质上是将向量对向量求梯度,通过加权求和的方式,转换成标量对向量求梯度。在数学上,向量对向量求导的结果是雅可比矩阵(Jacobian Matrix)。

如果我们再引入与向量 y \mathbf{y} y同型的向量 v {\mathbf{v}} v,标量损失用 l l l表示,则令

v = [ ∂ l ∂ y 1 . . . ∂ l ∂ y m ] = ∂ l ∂ y {\mathbf{v}}= \left[ \frac{\partial l}{\partial y_1} \quad ... \quad \frac{\partial l}{\partial y_m} \right] = \frac{\partial l}{\partial {\mathbf{y}}} v=[y1l...yml]=yl

这样反过来
l = y v T = ∑ i = 1 m v i y i l = \mathbf{y} \mathbf{v}^\text{T}=\sum_{i=1}^mv_iy_i l=yvT=i=1mviyi
所以说 l l l是向量 y \mathbf{y} y的加权和。

向backward()函数中传入的grad_tensor实际上就是向量 v {\mathbf{v}} v,求的梯度实际上就是

∂ l ∂ x = ∂ l ∂ y ∂ y ∂ x = v J {\frac{\partial l}{\partial {\mathbf{x}}}} =\frac{\partial l}{\partial {\mathbf{y}}} \frac{\partial {\mathbf{y}}}{\partial {\mathbf{x}}}=\mathbf{v}\mathbf{J} xl=ylxy=vJ
从线性代数的角度上解释,也可以理解为 ∂ l ∂ x \frac{\partial l}{\partial {\mathbf{x}}} xl就是 ∂ y ∂ x {\frac{\partial {\mathbf{y}}}{\partial {\mathbf{x}}}} xy在向量 v {\mathbf{v}} v上的投影。参考

backward不传入参数时,默认为传入backward(torch.tensor(1.0))。

另外,.grad属性在反向传播过程中是累加的,每一次反向传播梯度都会累加之前的梯度。因此每次重新计算梯度前都要将梯度清零。

x.grad.data.zero_()
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

PyTorch:梯度计算之反向传播函数backward() 的相关文章

随机推荐

  • Python安装pip

    文章转载自 https blog csdn net LCCFlccf article details 80095348 python2 7安装目录下没有Scripts文件夹 这种问题可能是你装的python安装包年代久远了 到官网下载最新的
  • 微信小程序打开pdf文件

    前言 使用原生的微信小程序打开pdf文件 注意使用的是文件流的形式 后端接口也需要流的形式来接收 wxml页面
  • window10安装minio

    1 首先第一步我们先下载minio 可以从官网上下不同的版本 下载地址 MinIO Code and downloads to create high performance object storage 2 启动minio 切记不要双击m
  • 众享比特李刘海:国密在超级账本Fabric中的应用

    导读 4月22日 超级账本Hyperledger北京Meetup在北京大学信息学院举行 本次会议由Hyperledger中国工作组和Linux基金会主办 安妮股份 版权区块链联盟 北京大学协办 专注于技术交流 实践分享 案例解析 作为Hyp
  • 六、ResNet网络详细解析(超详细哦)

    1 RestNet网络 1 1 RestNet网络结构 ResNet在2015年被提出 在ImageNet比赛classification任务上获得第一名 因为它 简单与实用 并存 之后很多方法都建立在ResNet50或者ResNet101
  • 《86盒应用于家居中控》——实现智能家居的灵动掌控

    近年来 智能家居产品受到越来越多消费者的关注 其便捷 舒适的生活方式让人们对未来生活充满期待 作为智能家居方案领域的方案商 启明智显生产设计的86盒凭借出色的性能和良好的用户体验 成功应用于家居中控系统 让家庭变得更加智能便捷 一 智能化生
  • 【Flink】flink提交到yarn时 自定义container的提交命令

    文章目录 1 概述 2 yarn 1 概述 在 Flink FLink 是如何与Yarn交互请求Container的 文章中 我们知道了Flink是如何启动ApplicationMaster的 以及如何申请Container的 然后在里面我
  • 如何做好一个BI项目的规划和需求定义?

    正所谓磨刀不误砍柴工 一个项目的启动 先得从金字塔顶端做好规划 摸清楚需求 背景 客观条件 可投入资源等 本文 BI项目详解的第一篇 先来谈谈BI项目的规划和需求定义 全文共3000字 读完需要 8分钟 一 如何启动一个BI项目 Q 一个项
  • 设计模式------适配器模式

    设计模式 适配器模式 文章目录 设计模式 适配器模式 前言 一 两种形式 二 UML图 对象的适配器模式 类的适配器模式 三 实现要点 四 应用场景 五 优点 六 缺点 七 本质 前言 适配器模式 Adapter Pattern 将一个接口
  • C语言实现两个整数相加

    思路 定义两个变量啊 存放要输入的整数 两个整数相加要存放在一个变量sum 此时可以先定义int sum 0 也可以直接int sum a b 这个意思是a b的值存放在sum 类型是int整型 输出sum即可 我犯的错误 是最后一个pri
  • OpenWrt源码目录

    分析的OpenWrt源码目录有助于分析OpenWrt的源码 一 原始目录 1 1 scripts 构建期间用到的各类脚本文件 它存放了一些脚本 使用了bash python perl等多种脚本语言 编译过程中 用于第三方软件包管理的feed
  • 开关稳压器详解(四)-Buck降压型开关稳压器自举电路

    在Buck开关中 常使用N MOS管作为功率开关管 相比于P MOS N MOS具有导通电阻低价格便宜且流过电流较大等优势 在同步结构中对于开关管的使用一般有两种方式 上管为P MOS 下管为N MOS 无需外部自举电路 上下管均为N MO
  • 建立任务,OSTaskCreate()源码解析

    想让uC OS 管理用户的任务 用户必须要先建立任务 用户可以通过传递任务地址和其它参数到以下两个函数之一来建立任务 OSTaskCreate 或 OSTaskCreateExt OSTaskCreate 与uC OS是向下兼容的 OSTa
  • matlab通过两点画线问题&&plot,line的用法和区别。

    先马 1 LINE并不等同于PLOT 我查过HELP 很多属性不同 2 对与外框的问题 PLOT可以用法BOX控制 LINE无外框 3 图形删除的问题 PLOT可用HOLD ON或OFF控制 LINE要是用DELET 因此建议使用PLOT
  • 尾行3解3D马赛克补丁

    尾行3解3D马赛克补丁 尾行3下载尾行3补丁尾行3图片尾行3去马赛克尾行3怎么玩尾行3下载尾行3视频illusion尾行3秘籍尾行3攻略秘籍 尾行3作弊单机游戏尾行3下载尾行3外挂尾行3对话单机游戏尾行3 尾行3中文补丁 尾行3黑屏补丁尾行
  • M1 Mac 安装Python及相关库|pytorch安装M1 Mac

    今天安装pytorch的时候发现安装的anaconda是x86版本的 自己的电脑是arm64架构的 所以一直安装不上 之后找到一个方法 以后可以通过命令行直接安装在arm64上运行的库了很方便 1 安装homebrew 这是一个mac上的包
  • 编译opencv.js

    opencv 支持编译多个平台 其中还支持JavaScript 不过编译需要emscripten 编译环境 centos7 Python2 7 1 下载OpenCV源码 官网 https opencv org releases 例如下载4
  • Docker如何安装seafile

    SQLite 方式 要在 Docker 中安装 Seafile 您可以按照以下步骤进行操作 安装 Docker 确保您的系统上已经安装了 Docker 您可以根据您的操作系统类型 在官方网站上找到适合您系统的 Docker 版本并进行安装
  • 数字化转型的趋势、挑战与战略【一】

    在全球经济进入数字化转型时期 数字化转型已成为传统企业必须付诸行动的必选题 企业为什么要进行数字化转型 如何把握数字化转型的时机 近日 在大华南IT高管共赢圈 大华南IT培训学院联合举办的 企业数据化转型的战略与规划 培训会上 IDC中国副
  • PyTorch:梯度计算之反向传播函数backward()

    一 计算图 计算图 是一种用来描述计算的有向无环图 我们假设一个计算过程 其中 X 1 mathbf X 1 X1 W 1