一、前言

上一篇文章中提到PyTorch提供了两个重要的高级功能,分别是:

  • 具有强大的GPU加速的张量计算(如NumPy)
  • 包含自动求导系统的的深度神经网络

第一个特性我们会在之后的学习中用到,这里暂且按下不表,我们首先来探讨研究一下什么是自动求导Autograd、自动求导Autograd的原理是怎样的等问题。

  • 当然首先我们要保证正确导入了torch包
import torch
# 打印一下PyTorch的版本
torch.__verson__

PyTorch的Autograd模块实现了深度学习算法中的反向传播求梯度,在张量(Tensor类)上的所有操作,Autograd都能为它们自动提供微分,简化了手动计算导数的复杂过程。

在张量创建时,通过设置 requires_grad 标识为Ture来告诉Pytorch需要对该张量进行自动求导,PyTorch会记录该张量的每一步操作历史并自动计算。

  • 首先我们新建一个张量x
# requires_grad = True表示告诉Pytorch需要对该张量进行自动求导,
# 并且PyTorch会记录该张量的每一步操作历史并自动计算。
x = torch.rand(5,5,requires_grad = True)
x

PyTorch会自动追踪和记录对于张量的所有操作,当计算完成后调用.backward()方法自动计算梯度并且将计算结果保存到grad属性中。

  • 然后我们另建一个张量y
y = torch.rand(5,5,requires_grad = True)
y

二、什么是自动求导Autograd

  • 1.当涉及到大型神经网络时,我们都不擅长微积分,这是事实,我们不可能随时随地的高效的计算一个很复杂的微积分。因此通过显式求解数学方程来计算这样大的复合函数的梯度是不现实的,特别是这些曲线存在于大量的维数中,是无法理解的。这就是PyTorch的Autograd发挥作用的地方。它抽象了复杂的数学,帮助我们“神奇地”计算高维曲线的梯度,只需要几行代码。
  • 2.每个张量都有一个.grad_fn属性,如果这个张量是用户手动创建的那么这个张量的grad_fn是None
  • 3.在张量进行操作后,grad_fn已经被赋予了一个新的函数,这个函数引用了一个创建了这个Tensor类的Function对象。 Tensor和Function互相连接生成了一个非循环图,它记录并且编码了完整的计算历史。

2.1 简单的自动求导

  • 案例1
z = torch.sum(x+y)
z
  • 自动求导
z.backward()
print(x.grad,y.grad)

如果Tensor类表示的是一个标量(即它包含一个元素的张量),则不需要为backward()指定任何参数,但是如果它有更多的元素,则需要指定一个gradient参数,它是形状匹配的张量。 以上的 z.backward()相当于是z.backward(torch.tensor(1.))的简写。 这种参数常出现在图像分类中的单标签分类,输出一个标量代表图像的标签。

2.2 复杂的自动求导

  • 案例2
x = torch.rand(5, 5, requires_grad=True)
y = torch.rand(5, 5, requires_grad=True)
z= x**2+y**3
z

  • 自动求导
#我们的返回值不是一个标量,所以需要输入一个大小相同的张量作为参数,这里我们用ones_like函数根据x生成一个张量
z.backward(torch.ones_like(x))
print(x.grad)


我们可以使用with torch.no_grad()上下文管理器临时禁制对已设置requires_grad = True的张量进行自动求导,这个方法在测试集计算准确率的时候会经常用到。

with torch.no_grad():print((x + y**2).requires_grad)# 会打印False,说明临时禁制对已设置requires_grad = True的张量进行自动求导

三、自动求导Autograd过程的简单解析

为了说明Pytorch的自动求导原理,我们来尝试分析一下PyTorch的源代码,虽然Pytorch的 Tensor和 TensorBase都是使用CPP来实现的,但是可以使用一些Python的一些方法查看这些对象在Python的属性和状态。 Python的 dir() 函数返回参数的属性、方法列表。上述案例2中的z是一个Tensor变量,看看里面有哪些成员变量。

dir(z)

会打印处一大串东西来,返回很多,我们直接排除掉一些Python中特殊方法(以__开头和结束的)和私有方法(以_开头的,直接看几个比较主要的属性:

  • .is_leaf:记录是否是叶子节点。通过这个属性来确定这个变量的类型 在官方文档中所说的“graph leaves”,“leaf variables”,都是指像x,y这样的手动创建的、而非运算得到的变量,这些变量称为创建变量。 像z这样的,是通过计算后得到的结果称为结果变量
  • 判断一个Tensor变量是创建变量还是结果变量可以通过.is_leaf
print('x.is_leaf=' + str(x.is_leaf)) # 打印True
print('z.is_leaf=' + str(z.is_leaf)) # 打印False

x是手动创建的没有通过计算,所以他被认为是一个叶子节点也就是一个创建变量,而z是通过x与y的一系列计算得到的,所以不是叶子结点也就是结果变量。

  • grad_fn:每个张量都有一个grad_fn属性用于保存张量的操作,如果这个张量为用户自己创建的,则grad_fn为None。
    为什么我们执行z.backward()方法的时候会更新x.grad和y.grad呢? .grad_fn属性记录的就是这部分的操作,虽然.backward()方法也是CPP实现的,但是可以通过Python来进行简单的探索。
z.grad_fn


grad_fn是一个AddBackward0类型的变量 AddBackward0这个类也是用Cpp来写的,但是我们从名字里就能够大概知道,他是加法(ADD)的反反向传播(Backward),看看里面有些什么东西。

dir(z.grad_fn)
  • 输出结果
['__call__','__class__','__delattr__','__dir__','__doc__','__eq__','__format__','__ge__','__getattribute__','__gt__','__hash__','__init__','__init_subclass__','__le__','__lt__','__ne__','__new__','__reduce__','__reduce_ex__','__repr__','__setattr__','__sizeof__','__str__','__subclasshook__','_register_hook_dict','metadata','name','next_functions','register_hook','requires_grad']

next_functions就是grad_fn的精华

z.grad_fn.next_functions


next_functions是一个tuple of tuple of PowBackward0 and int。
为什么是2个tuple ? 因为我们的操作是z= x**2+y**3 刚才的AddBackward0是相加,而前面的操作是乘方 PowBackward0。tuple第一个元素就是x相关的操作记录

  • 查看z的grad_fn属性next_functions的第一个元组的第一个元素
xg = z.grad_fn.next_functions[0][0]
dir(xg)
  • 输出结果
['__call__','__class__','__delattr__','__dir__','__doc__','__eq__','__format__','__ge__','__getattribute__','__gt__','__hash__','__init__','__init_subclass__','__le__','__lt__','__ne__','__new__','__reduce__','__reduce_ex__','__repr__','__setattr__','__sizeof__','__str__','__subclasshook__','_register_hook_dict','metadata','name','next_functions','register_hook','requires_grad']
  • 继续剖析,查看xg的next_functions
x_leaf=xg.next_functions[0][0]
type(x_leaf)
  • 输出结果
AccumulateGrad

在PyTorch的反向图计算中,AccumulateGrad类型代表的就是叶子节点类型,也就是计算图终止节点。AccumulateGrad类中有一个.variable属性指向叶子节点。

x_leaf.variable

这个.variable的属性就是我们的生成的变量x

这样整个规程就很清晰了:

  • 1.当我们执行z.backward()的时候。这个操作将调用z里面的grad_fn这个属性,执行求导的操作。

  • 2.这个操作将遍历grad_fn的next_functions,然后分别取出里面的Function(AccumulateGrad),执行求导操作。这部分是一个递归的过程直到最后类型为叶子节点。

  • 3.计算出结果以后,将结果保存到他们对应的variable 这个变量所引用的对象(x和y)的 grad这个属性里面。

  • 4.求导结束。所有的叶节点的grad变量都得到了相应的更新
    最终当我们执行完c.backward()之后,a和b里面的grad值就得到了更新。

  • 首先我们来剖析一下这个案例,通过这个案例来进一步理解Autograd的工作流程

# 导入相应的包
import torch
import torch.autograd# 创建张量x、y
x = torch.tensor([1.0],requires_grad = True)
y  =torch.tensor([2.0],requires_grad = False)
z = x * y# 求解x的梯度
z.backward(torch.ones_like(x))
x.grad
  • 输出结果
x.grad =  tensor([2.])


图片来源:https://zhuanlan.zhihu.com/p/148669484

从计算图中可以看出,一个张量中:

  • data中保存着所存有的数据
  • grad中保存这梯度
  • requires_grad表示是否开始追踪所有的操作历史

Autograd具体工作流程:
x、y是我们自己创建的张量,让x、y执行相乘操作,生成张量z。

  • 1.当我们调用.backward()方法时,该操作会调用张量z中的grad_fn属性,前面我们说过,grad_fn属性用来保存张量的操作。如果张量是创建张量,那么该张量的grad_fn属性为None。
  • 2.这个操作将遍历grad_fn的next_functions,然后分别取出里面的Function(AccumulateGrad),执行求导操作。这部分是一个递归的过程直到最后类型为叶子节点。
  • 3.计算出结果以后,将结果保存到他们对应的variable 这个变量所引用的对象(x)的 grad属性里面。
    求导结束。所有的叶节点的grad变量都得到了相应的更新。
  • 4.最终当我们执行完z.backward()之后,x中的grad值就得到了更新。

四、扩展Autograd

如果需要自定义autograd扩展新的功能,就需要扩展Function类。因为Function使用autograd来计算结果和梯度,并对操作历史进行编码。 在Function类中最主要的方法就是forward()和backward()他们分别代表了前向传播和反向传播。

一个自定义的Function需要一下三个方法:

  • __init__ (optional):如果这个操作需要额外的参数则需要定义这个Function的构造函数,不需要的话可以忽略。

  • forward():执行前向传播的计算代码

  • backward():反向传播时梯度计算的代码。 参数的个数和forward返回值的个数一样,每个参数代表传回到此操作的梯度。

# 引入Function便于扩展
from torch.autograd.function import Function
# 定义一个乘以常数的操作(输入参数是张量)
# 方法必须是静态方法,所以要加上@staticmethod
class MulConstant(Function):@staticmethod def forward(ctx, tensor, constant):# ctx 用来保存信息这里类似self,并且ctx的属性可以在backward中调用ctx.constant=constantreturn tensor *constant@staticmethoddef backward(ctx, grad_output):# 返回的参数要与输入的参数一样.# 第一个输入为3x3的张量,第二个为一个常数# 常数的梯度必须是 None.return grad_output, None

定义完我们的新操作后,我们来进行测试

a=torch.rand(3,3,requires_grad=True)
b=MulConstant.apply(a,5)
print("a:"+str(a))
print("b:"+str(b)) # b为a的元素乘以5

反向传播,返回值不是标量,所以backward方法需要参数

b.backward(torch.ones_like(a))
a.grad

参考文献

  • https://github.com/zergtant/pytorch-handbook/blob/master/chapter2/2.1.2-pytorch-basics-autograd.ipynb
  • https://zhuanlan.zhihu.com/p/148669484

PyTorch基础(二)-----自动求导Autograd相关推荐

  1. 【PyTorch】PyTorch基础知识——自动求导

    1 Autograd简介 PyTorch 中,所有神经网络的核心是 autograd 包.autograd包为张量上的所有操作提供了自动求导机制.它是一个在运行时定义 ( define-by-run ...

  2. Pytorch多次自动求导

    在Pytorch中,通过调用 backward 我们可以进行一次自动求导,如果我们再调用一次 backward,会发现程序报错,没有办法再做一次.这是因为 PyTorch 默认做完一次自动求导之后,计 ...

  3. pytorch教程之自动求导机制(AUTOGRAD)-从梯度和Jacobian矩阵讲起

    文章目录 1. 梯度和Jacobian矩阵 2. pytorch求变量导数的过程 1. 梯度和Jacobian矩阵 设f(x)∈R1f(x)\in R^1f(x)∈R1是关于向量x∈Rnx\in R^ ...

  4. 基于pytorch实现图像分类——理解自动求导、计算图、静态图、动态图、pytorch入门

    1. pytorch入门 什么是PYTORCH? 这是一个基于Python的科学计算软件包,针对两组受众: 替代NumPy以使用GPU的功能 提供最大灵活性和速度的深度学习研究平台 1.1 开发环境 ...

  5. PyTorch 笔记Ⅱ——PyTorch 自动求导机制

    文章目录 Autograd: 自动求导机制 张量(Tensor) 梯度 使用PyTorch计算梯度数值 Autograd 简单的自动求导 复杂的自动求导 Autograd 过程解析 扩展Autogra ...

  6. 【Torch笔记】autograd自动求导系统

    [Torch笔记]autograd自动求导系统 Pytorch 提供的自动求导系统 autograd,我们不需要手动地去计算梯度,只需要搭建好前向传播的计算图,然后使用 autograd 计算梯度即可 ...

  7. 【PyTorch基础教程2】自动求导机制(学不会来打我啊)

    文章目录 第一部分:深度学习和机器学习 一.机器学习任务 二.ML和DL区别 (1)数据加载 (2)模型实现 (3)训练过程 第二部分:Pytorch部分 一.学习资源 二.自动求导机制 2.1 to ...

  8. PyTorch 1.0 中文官方教程:Autograd:自动求导

    译者:bat67 最新版会在译者仓库首先同步. PyTorch中,所有神经网络的核心是autograd包.先简单介绍一下这个包,然后训练我们的第一个的神经网络. autograd包为张量上的所有操作提 ...

  9. PyTorch定义新的自动求导(Autograd) 函数

    PyTorch定义新的自动求导(Autograd) 函数 pytorch官网提供了定义新的求导函数的方法(链接放在文章末尾了),官网举的例子,可能我比较笨,愣是反应了好一会儿才理解.这篇博客主要讲 P ...

最新文章

  1. c语言关闭其他进程tcp_tcp链接迁移
  2. 类的operator new与operator delete的重载
  3. 计算机用户的注册表,计算机上的注册表在哪里
  4. 基于easyui开发Web版Activiti流程定制器详解(一)——目录结构
  5. 引用和指针的区别都有什么_C++指针与引用的区别
  6. ASP.NET生成静态页面方法大全(2)
  7. 解决mac管理员变成普通成员
  8. 包含min函数的栈(important)
  9. PERL-MOJO写一个简单的登录验证页面
  10. 30分钟!用Django做一个迷你的Todolist!下篇!
  11. 单片机c语言程序包txt,单片机C语言应用100例(第3版)(含光盘1张) pdf epub mobi txt 下载...
  12. Python 矩形法求1/x的定积分(完美实现)
  13. 笔记本电脑装android系统安装教程,电脑上安装Android 10小白教程,大屏Android用起来...
  14. 计算机启动相机代码,如何在win7系统中启动相机
  15. 【b站雅思笔记】Charlie有好好学习 - 雅思机经8-14
  16. 前端拖拽时手型为禁用
  17. 2023年最新微信小程序获取用户openid、头像昵称的填写能力和方法原生写法
  18. android系统构建系统_构建系统简介
  19. Animator组件的使用(一)
  20. python提取json中的值,在Python中从JSON提取特定值

热门文章

  1. final关键字细节
  2. JMeter基础之—录制脚本
  3. android sqlite3_open_v2( data/data//database ,handle,1,null)
  4. 两数相加python代码实现(leetcode2)
  5. 给eth0增加一个IP
  6. GPS nmealib学习笔记
  7. getopts命令行参数处理
  8. CUDA下在Host端分配的几种内存模式
  9. 交通银行软件中心编制_国家标准研究项目区块链服务技术安全要求编制启动会顺利召开...
  10. 【Python】if else 一行写完