pytorch基础-使用 TORCH.AUTOGRAD 进行自动微分(5)

在训练神经网络时,最常用的算法是反向传播。PyTorch的反向传播(即tensor.backward())是通过autograd包来实现的,autograd包会根据tensor进行过的数学运算来自动计算其对应的梯度。在该算法中,参数(模型权重)根据损失函数相对于给定参数的梯度进行调整。为了计算这些梯度,Pytorch有一个名为 torch.autograd 的内置微分引擎。它支持自动计算任何计算图形的梯度。本例中考虑最简单的单层神经网络,输入x,参数w和b,以及一些损失函数。可通过以下方式在PyTorch中定义:

import torch

x = torch.ones(5)  # input tensor,返回一个全为1 的张量,形状由可变参数sizes定义
y = torch.zeros(3)  # expected output,返回一个全为标量 0 的张量
w = torch.randn(5, 3, requires_grad=True)#返回一个符合均值为0,方差为1的正态分布(标准正态分布)中填充随机数的张量
b = torch.randn(3, requires_grad=True)  #requires_grad(bool, optional) –autograd是否应该记录对返回张量的操作(说明当前量是否需要在计算中保留对应的梯度信息)。
z = torch.matmul(x, w)+b#两个张量矩阵x,w相乘
loss = torch.nn.functional.binary_cross_entropy_with_logits(z, y)#对神经网络的输出结果进行sigmoid操作,然后求交叉熵

其中torch.randn()函数返回一个符合标准正态分布填充随机数的张量参数requires_grad的含义是如果需要为张量计算梯度,则为True,否则为False。在使用pytorch创建tensor时,可以指定requires_grad为True(默认为False),更多含义参考:

torch.randn()函数_meteor,across T sky的博客-CSDN博客_torch.randn()

其中torch.matmul(input, other) 函数是计算两个张量input和other的矩阵乘积。因为matmul函数没有强制规定维度和大小,可以用利用广播机制进行不同维度的相乘操作。本例中input为一维,other为二维,则先将input的一维向量扩充到二维,然后进行矩阵乘积,得到结果后再将此维度去掉,得到的与input的维度相同。该函数的多维张量操作详见:

PyTorch疑难杂症(1)——torch.matmul()函数用法总结_wendy_ya的博客-CSDN_torch.matmul

其中torch.nn.functional.binary_cross_entropy_with_logits函数的功能是先对神经网络的输出结果进行Sigmoid激活操作,然后求交叉熵。作用是衡量真实分布和预测的分布的差异情况。参数input神经网络的预测结果(未经过sigmoid),任意形状的tensor。参数target是标签值,与input形状相同。详细的参数如下:

pytorch常用函数:torch.nn.functional.binary_cross_entropy_with_logits_xingS1992的博客-CSDN博客_torch.nn.functional.binary_cross_entropy

1、张量、函数和计算图

 以上代码可以定义以下计算图

(1)在这个网络中,w和b是需要优化的参数。 因此就要计算损失函数相对于这些变量的梯度变化。 为了做到这一点,设置了requires_grad方法查看这些张量的属性。该方法的作用是,如果需要为张量计算梯度,则为True,否则为False。(你可以在创建张量时设置requires_grad的值,或稍后使用x.requires_grad(True)方法设置。)例如:

a = torch.randn(2, 2) # 缺失情况下默认 requires_grad = False
x = ((a * 5) / (a - 5))
print(x.requires_grad) # False
a.requires_grad_(True)
print(a.requires_grad) # True

(2)接下来用张量来构造计算图的函数,该函数实际上是类函数的对象。 该对象知道如何计算正向传播的函数,以及如何在反向传播步骤中计算其导数。grad_fn方法用来记录变量是怎么来的,方便计算梯度,例如y = x*3,grad_fn记录了y由x计算的过程。

print(f"Gradient function for z = {z.grad_fn}")
print(f"Gradient function for loss = {loss.grad_fn}")
print(x.grad_fn)

 由于x是直接创建的,所以它没有grad_fn,而z是通过计算创建的,所以z有grad_fn。像x这种直接创建的称为叶子节点,叶子节点对应的grad_fn是None。如果需要在同一个图上执行多个backward调用,需要将retain_graph=True传递给backward调用。输出结果:

Gradient function for z = <AddBackward0 object at 0x000001F22FED1AF0>
Gradient function for loss = <BinaryCrossEntropyWithLogitsBackward0 object at 0x000001F22FED1AF0>
None

 2、计算梯度

为了优化神经网络中参数的权重,就需要计算损失函数对参数的导数,即需要求得x和y在某些固定值下的导数\frac {\partial loss}{\partial w}\frac{\partial loss}{\partial b}。为了计算这些导数,我们调用loss.backward()函数,然后从w.grad和b.grad中检索梯度值:

loss.backward()
print(w.grad)
print(b.grad)

其中grad方法是当执行完了backward()之后,通过x.grad查看w和b的梯度值。另外grad在反向传播过程中是累加的,这意味着每一次运行反向传播,梯度都会累加之前的梯度,所以一般在反向传播之前需把梯度清零。如果不将梯度清零的话,梯度会与上一个batch的数据相关。但是如果没有进行tensor.backward()的话,梯度值将会是None。

tensor([[0.0981, 0.2268, 0.2431],
        [0.0981, 0.2268, 0.2431],
        [0.0981, 0.2268, 0.2431],
        [0.0981, 0.2268, 0.2431],
        [0.0981, 0.2268, 0.2431]])
tensor([0.0981, 0.2268, 0.2431])

总结:如果设置tensor的requires_grads为True,那么就会开始跟踪这个tensor上面的所有运算,则w的所有上层参数(后面层的权重w)的w.grad_fn属性中就保存了对应的运算,如果做完运算后使用tensor.backward(),所有的梯度就会自动运算,会一层层的反向传播计算每个w的梯度值,tensor的梯度将会累加到 w.grad 属性里面去。 下面是计算梯度时用到的三种方法 requires_grad,grad_fn,grad的含义及使用:

requires_grad,grad_fn,grad的含义及使用_dlage的博客-CSDN博客_requires_grad_

 3、梯度跟踪禁用

默认情况下,所有requires_grad=True的张量跟踪它们的计算历史并支持梯度计算。但是也有一些情况不需要这样做,比如已经训练好了模型,只是想把它应用到一些输入数据上,只是想通过网络进行正向计算。这时可以通过使用torch.no_grad()块包围计算代码来停止跟踪计算:

z = torch.matmul(x, w)+b
print(z.requires_grad) #True
with torch.no_grad():
    z = torch.matmul(x, w)+b
print(z.requires_grad) #False

 实现相同结果的另一种方法是在张量上使用 detach()方法 :

z = torch.matmul(x, w)+b
z_det = z.detach()
print(z_det.requires_grad) #False

禁用梯度跟踪的原因有:把神经网络中的一些参数标记为冻结参数。这是对预训练网络进行微调的一个非常常见的场景。另外当只做前向传递时,可以加速计算,因为不跟踪梯度的张量的计算会更有效率。

4、AUTOGRAD 自动微分总结

从概念上讲,autograd在一个由Function对象组成的有向无环图(DAG)中保存数据(张量)和所有执行的操作(以及生成的新张量)的记录。在这个DAG中,叶是输入张量,根是输出张量。通过跟踪这个从根到叶的图,使用链式法则自动计算梯度。
(1)在前向传递中,autograd同时做两件事:运行请求的操作来计算结果张量。在DAG中维护操作的梯度函数。

(2)当在DAG根节点上调用.backward()时,后向传递开始。autograd计算每个  .grad_fn的梯度。将它们累积到各自张量的  .grad属性中。利用链式法则,一直传播到叶张量。

另外,计算图是从头再创建的,在每次 .backward()调用之后,autograd开始填充一个新图。这正是可以让我们在模型中使用控制流语句的原因,如果需要,可以在每次迭代中更改形状、大小和操作。

 5、张量梯度和雅可比积

在很多情况下,我们有一个标量损失函数,我们需要计算一些参数的梯度。然而,在某些情况下,输出函数是任意张量。在这种情况下,PyTorch允许我们计算所谓的雅可比积,而不是实际的梯度。

对于向量函数\vec{y}=f(\vec{x}),其中\vec{x}=\left \{ x_{1} \cdot \cdot \cdot x_{n}\right \}\vec{y}=\left \{ y_{1} \cdot \cdot \cdot y_{m}\right \}\vec{y}相对于\vec{x}的梯度由雅可比矩阵给出:

并不是计算雅可比矩阵本身,PyTorch允许计算雅可比矩阵积vT⋅ 对于J给定的输入向量v=(v1…vm)。这是通过将v作为参数调用 backward 来实现的。v的大小应该和原始张量的大小一样,我们要根据原始张量计算乘积:

inp = torch.eye(5, requires_grad=True)#生成对角线全1,其余部分全0的二维数组
out = (inp+1).pow(2)#计算数的次方
out.backward(torch.ones_like(inp), retain_graph=True)#根据给定张量,生成与其形状相同的全1张量
print(f"First call\n{inp.grad}")
out.backward(torch.ones_like(inp), retain_graph=True)
print(f"\nSecond call\n{inp.grad}")
inp.grad.zero_()#将模型参数的梯度置为0
out.backward(torch.ones_like(inp), retain_graph=True)
print(f"\nCall after zeroing gradients\n{inp.grad}")

其中retain_graph=True代表可以再来一次backward。如果设置为False,计算图中的中间变量在计算完后就会被释放清除。这样进行第二次backward会报错。但是在平时的使用中这个参数默认都为False从而提高效率。

tensor([[4., 2., 2., 2., 2.],
        [2., 4., 2., 2., 2.],
        [2., 2., 4., 2., 2.],
        [2., 2., 2., 4., 2.],
        [2., 2., 2., 2., 4.]])

Second call
tensor([[8., 4., 4., 4., 4.],
        [4., 8., 4., 4., 4.],
        [4., 4., 8., 4., 4.],
        [4., 4., 4., 8., 4.],
        [4., 4., 4., 4., 8.]])

Call after zeroing gradients
tensor([[4., 2., 2., 2., 2.],
        [2., 4., 2., 2., 2.],
        [2., 2., 4., 2., 2.],
        [2., 2., 2., 4., 2.],
        [2., 2., 2., 2., 4.]])

当使用相同的参数第二次backward调用时,梯度的值是不同的。这是因为当进行逆向传播时,PyTorch会累积梯度,即计算的梯度值会被添加到计算图所有叶节点的梯度属性中。如果想要计算适当的梯度,需要在之前将梯度属性归零。在现实训练中,优化器可以帮助我们做到这一点。之前调用的是没有参数的backward()函数。这本质上相当于调用backward(torch.tensor(1.0)),这也是计算标量值函数(比如神经网络训练期间的损失)的梯度的一种有用方法。

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一只小小的土拨鼠

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值