Pytorch学习(一)---Tensor与Autograd

前言

在神经网络中,一个重要的内容就是进行参数学习,而参数学习离不开求导,那么pytorch是如何进行求导的呢?
现在大部分深度学习架构都有自动求导的功能,pytorch也不例外,torch.autograd包就是用来自动求导的。Autograd包为张量上的所有操作提供了自动求导功能,torch.Tensor 和torch.Function为Autograd的两个核心类,它们相互连接并生成一个有向非循环图

自动求导要点

为了实现对Tensor自动求导,需考虑如下事项:

  1. 创建叶子节点的Tensor,使用requires_grad参数指定是否记录对其的操作,以便之后利用backward()方法进行梯度求解。requires_grad参数的缺省值为False,如果要对其求导需设置为true,然后与之有依赖关系的节点会自动变为True。
  2. 可利用requires_grad()方法修改Tensor的requires_grad属性。
    可以调用.detach()或者with torch.no_grad:,将不再计算张量的梯度、跟踪张量的历史记录。这点在评估模型、测试模型阶段中常常用到。
  3. 通过运算创建的Tensor(即非叶子节点),会被自动赋予grad_fn属性,该属性表示梯度函数。叶子节点的grad_fn为None。
  4. 最后得到的Tensor执行backward(),此时自动计算各变量的梯度,并将结果保存的grad属性中。计算完成后,非叶子节点的梯度自动释放。
  5. backward()函数接收参数,该参数应和调用backward()函数的Tensor的维度相同,或者是可broadcast的维度。如果求导的Tensor为标量(即是一个数字),backward中的参数可省略。
  6. 反向传播的中间缓存会被清空,如果需要多次反向传播,需要指定backward中的参数retain_graph=True。多次反向传播,梯度是累加的。
  7. 非叶子节点的梯度backward调用后即被清空
  8. 可以通过 torch.no_grad()包裹代码块的形式,来阻止autograd去跟踪那些标记为.requires_grad=True的张量的历史记录。这步在测试阶段经常使用。
    在整个过程中,pytorch采用计算图的形式进行组织,该计算图为动态图,且在每次前向传播时将重新构建。其他深度学习框架,如tensorflow,keras一般为静态图。

计算图

计算图是一种有向无环图像,用图形方式来表示算子与变量之间的关系,直观高效。如图所示,圆形表示变量,矩形表示算子。
在这里插入图片描述如表达式: z = w x + b {\rm{z}} = wx + b z=wx+b,可写成两个表达式: y = w x y = wx y=wx,则 z = y + b z = y + b z=y+b,其中x、w、b为变量,是用户创建的变量,不依赖与其他变量,故又称为叶子节点。为了计算各叶子节点的梯度,需要把对应的张量参数requires_grad属性设置为True,这样就可以自动跟踪其历史记录。y、z是计算得到的变量,非叶子节点,z为根节点。mul和add是算子(或操作或函数)。由这些变量及算子,就构成一个完整的计算过程或前向传播过程。

我们的目标是更新各叶子节点的梯度,根据符合函数链式求导法则,不难算出各叶子节点的梯度。
∂ z ∂ x = ∂ z ∂ y ∂ y ∂ x = w \frac{{\partial z}}{{\partial x}} = \frac{{\partial z}}{{\partial y}}\frac{{\partial y}}{{\partial x}} = w xz=yzxy=w
∂ z ∂ w = ∂ z ∂ y ∂ y ∂ w = x \frac{{\partial z}}{{\partial w}} = \frac{{\partial z}}{{\partial y}}\frac{{\partial y}}{{\partial w}} = x wz=yzwy=x
∂ z ∂ b = 1 \frac{{\partial z}}{{\partial b}} = 1 bz=1
pyorch调用 backward()方法,将自动计算个节点的梯度,这是一个反向传播过程,从当前根节点z反向溯源,利用倒数链式法则,计算所有叶子节点的梯度,其梯度值将累加到grad属性中。对非叶子节点的操作或function记录在grad_fn属性中,叶子节点的grad_fn为None。

标量反向传播

假设x、w、b都是标量, z = w x + b {\rm{z}} = wx + b z=wx+b,对标量z调用backward()方法,此时无需对backward()传入参数。以下是实现自动求导的主要步骤:

  1. 定义叶子节点及算子节点
import torch

# 定义输入张量
x = torch.Tensor([2])
# 初始化权重参数w 偏移量b 并设置require_grad的属性为True,为自动求导
w = torch.randn(1,requires_grad=True)
b = torch.randn(1,requires_grad=True)
# 实现前向传播
y = torch.mul(w,x)
z = torch.add(y,b)
# 查看x,w,b叶子节点的requires_grad属性
print("x,w,b的requires_grad属性分别为:{},{},{}".format(x.requires_grad,w.requires_grad,b.requires_grad))
# 运行结果:x,w,b的requires_grad属性分别为:False,True,True
  1. 查看叶子节点、非叶子节点的其他属性
# 查看叶子节点、非叶子节点的其他属性
print("y,z的requires_grad属性分别为:{},{}".format(y.requires_grad,z.requires_grad))

# 查看各节点是否为叶子节点
print("x,w,b,y,z的是否为叶子节点{},{},{},{},{}".format(x.is_leaf,w.is_leaf,b.is_leaf,y.is_leaf,z.is_leaf))
# 查看叶子节点的grad_fn属性
print("x,w,b的grad_fn属性:{},{},{}".format(x.grad_fn,w.grad_fn,b.grad_fn))
# 查看非叶子节点的grad_fn属性
print("y,z的grad_fn属性:{},{}".format(y.grad_fn,z.grad_fn))
  1. 自动求导,实现梯度反向传播
z.backward()
# 查看叶子节点的梯度,x是叶子节点,但他无需求导,所以其梯度为none
print("w,b的梯度分别为{},{},{}".format(w.grad,b.grad,x.grad))
# 查看非叶子节点的梯度,执行backward后,会自动清空
print("y,z的梯度分别为{},{}".format(y.grad,z.grad))

# 输出为
#x,w,b,y,z的是否为叶子节点True,True,True,False,False
#x,w,b的grad_fn属性:None,None,None
#y,z的grad_fn属性:<MulBackward0 object at 0x000002545C745BE0>,<AddBackward0 object at 0x000002545C745C88>
#w,b的梯度分别为tensor([2.]),tensor([1.]),None
#y,z的梯度分别为None,None
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值