问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

【PyTorch入门】一篇弄懂Autograd(自动微分)【全网最详细】

创作时间:
作者:
@小白创作中心

【PyTorch入门】一篇弄懂Autograd(自动微分)【全网最详细】

引用
CSDN
1.
https://blog.csdn.net/m0_67656158/article/details/146025197

自动微分(Autograd)是PyTorch进行神经网络优化的核心技术。它能够自动计算出复杂的计算图中每个变量的梯度,从而帮助我们优化模型参数。本文将从基本概念出发,通过具体的数学示例和代码演示,深入浅出地讲解自动微分的工作原理、前向传播和反向传播的过程,以及在非标量输出情况下的处理方法。

1. 前言

在前一篇《PyTorch入门》系列中,我们讲解了Tensor(张量)这个对象。Tensor在PyTorch中就像Array在numPy中,两者都是最核心最底层的部分。但是Tensor不同于Array之处就在于Tensor可以使用GPU加速计算(通过x.cuda()),同时Tensor能够实现自动微分(也就是我们这篇马上要讲解的Autograd)。

2. Autograd

Autograd中文叫作自动微分,是PyTorch进行神经网络优化的核心。自动微分,顾名思义就是让PyTorch自动为我们计算微分。

2.1 微分示例

我们先来看一个直观的例子。假如有一个向量:

将它作为输入。接着,将输人乘以4得到向量z,最后求出长度并输出一个标量y,值为5.6569。从向量输入x到标量输出y的完整计算过程如下图所示:

在深度学习中,我们想要根据想要的y值去修正对应的x值。因此就需要让y对x求出偏导数,根据这个偏导数去调整x的值,从而调整y的值。

下面从数学角度推导y关于x的微分。由下图不难得到y关于x的表达式:

其中,


,所以y关于x的微分为:

这个结果能用来对x值进行调整,从而让y的值更接近0(在深度学习中,y通常都是loss,我们想要让损失无限接近0)。如果一个输人需要经过比上面例子更多的计算步骤,那么靠人工去计算微分就变得力不从心。幸运的是,PyTorch的Autograd技术可以帮助我们自动求出这些微分值。

2.2 基本原理

我们可以将上面微分示例的计算过程抽象为图像,如下图所示,x、z和y被当作节点,运行过程被抽象为信息流。复杂的计算也可以被抽象成一张图(graph)。如下图所示,一张复杂的计算图可以分成4个部分:叶子节点、中间节点、输出节点和信息流。叶子节点是图的末端,没有信息流经过,在神经网络模型中就是输人值和神经网络的参数。

Tensor在自动微分方面有3个重要属性:requires_grad、grad和grad_fn。

  • requires_grad属性是一个布尔值,默认为False。当requires_grad为True时,表示该Tensor需要自动微分。
  • grad属性用于存储Tensor的微分值。
  • grad_fn属性用于存储Tensor的微分函数。

当叶子节点的requires_grad为True时,信息流经过该节点时,所有中间节点的requires_grad属性都会变成True,只要在输出节点调用反向传播函数backward(),PyTorch就会自动求出叶子节点的微分值并更新存储在叶子节点的grad属性中。需要注意的是,只有叶子节点的grad属性能被更新。

怎么理解grad的值?

  • grad反映的是这一个参数对y输出节点整体的影响大小和方向。
  • grad结果能够为对应参数值的调整起指导作用。具体看梯度下降算法。
  • 只有叶子节点的属性能被更新

2.3 前向传播

Autograd技术可以帮助我们从叶子节点开始追踪信息流,记下整个过程使用的函数,直到输出节点,这个过程被称为前向传播。本节先初始化叶子节点x:

默认情况下,Tensor的requires_grad属性为False。因为我们要让PyTorch自动帮我们计算x的微分值,所以将x的requires_grad属性设为True:

设置完成后,打印结果会显示requires_grad=True。此时x的 grad 属性和 grad_fn 属性均为空值:

接下来,我们让x乘以4得到z。可以看到,z的grad_fn为函数:

grad_fn是微分函数,这里是乘法的反向函数。最后我们用norm()函数求z的长度得到y:

grad_fn:指示该张量是如何计算得来的。它记录了生成当前张量的操作,以及如何通过这些操作进行反向传播。例如上面是乘法得到的张量,因此在反向传播时依靠乘法法则。

可以发现,y的grad_fn是norm()的反向函数。

常见的反向传播函数grad_fn:

操作
对应的 grad_fn
用途
加法
AddBackward
计算加法操作的梯度
乘法
MulBackward
计算乘法操作的梯度
矩阵乘法
MatmulBackward
计算矩阵乘法操作的梯度
Sigmoid 激活
SigmoidBackward
计算 Sigmoid 激活函数的梯度
Tanh 激活
TanhBackward
计算 Tanh 激活函数的梯度
ReLU 激活
ReLUBackward
计算 ReLU 激活函数的梯度
批量归一化
BatchNormBackward
计算批量归一化操作的梯度
Softmax
SoftmaxBackward
计算 Softmax 操作的梯度
view 操作
ViewBackward
计算 view 操作的梯度
reshape 操作
ReshapeBackward
计算 reshape 操作的梯度
拼接操作
CatBackward
计算拼接操作的梯度
扩展操作
ExpandBackward
计算 expand 操作的梯度
求和操作
SumBackward
计算求和操作的梯度
均值操作
MeanBackward
计算均值操作的梯度
对数操作
LogBackward
计算对数操作的梯度
幂运算
PowBackward
计算幂运算的梯度
范数计算
NormBackward
计算范数操作的梯度
最大值操作
AmaxBackward
计算最大值操作的梯度

2.4 反向传播

接下来,调用输出节点的backward()函数对整个图进行反向传播,求出微分值:

同时,z.grad和y.grad结果没有发生变化,因为他们都不是叶子节点。

  1. 反向传播(Backpropagation)

反向传播是计算神经网络中每个参数(如权重和偏置)的梯度的一种方法。它利用链式法则,通过计算损失函数相对于每个参数的导数,逐层传播误差信号来调整参数。

  1. grad(梯度)

梯度(gradient)是损失函数对于模型参数(如权重和偏置)的导数。在反向传播过程中,计算梯度的过程就是通过链式法则来获得每个参数的梯度。

2.5 非标量输出

在以上讨论的例子中,输出节点是一个标量。当输出节点为非标量时,使用backward()函数就需要增加一个参数gradient。gradient的形状应该与输出节点的形状保持一致且元素值均为1。尽管在深度神经网络中很少碰到这种情况,我们还是利用下面的例子简单了解一下其操作方式。初始化矩阵X和向量z,并将矩阵与向量相乘,得到形状为2x1的向量:

特别地:

此时,调用y的backward()函数时,需要传入一个形状与y的形状相同且元素全为1的向量:

这时我们看到,x有grad结果但是z没有存储grad结果。于是我就想利用z.requires_grad=True去调整,然后重新backward求导。结果如下:

原因如下:

如果你在同一个计算图上再次调用 backward(),PyTorch 会报错是因为它无法再访问已经被释放的中间张量。如果计算图已经被释放,再次调用 backward()就相当于试图访问已经消失的数据。而且,PyTorch 默认不会重新计算前向传播,因为:

  • 计算图和中间张量已经被释放,重新计算会涉及到重新创建和计算这些中间值,这会增加计算和内存的开销。
  • 如果需要多次反向传播或者想要访问中间值,通常我们会手动保留计算图(通过 retain_graph=True),否则就假定反向传播只会执行一次,且计算图不再需要。

结果发现,z.grad仍然没有结果:

仔细思考后发现,z本质上并没有参与运算,不在计算图中。计算图中只有x和y,z是x、y直接的信息流,类似于下图中的 * 4:

3. 总结

本文详细介绍了PyTorch中自动微分(Autograd)的工作原理和使用方法。通过具体的数学示例和代码演示,帮助读者理解自动微分在神经网络优化中的重要作用。希望本文能够帮助读者更好地掌握PyTorch的自动微分机制,为深度学习实践打下坚实的基础。

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号