本文最后更新于:2024年5月7日 下午
本文介绍张量自动求导的基本知识 。
参考 深入浅出PyTorch ,系统补齐基础知识。
本节目录
- autograd的求导机制
- 梯度的反向传播
前言
PyTorch 中,所有神经网络的核心是 autograd
包。autograd
包为张量上的所有操作提供了自动求导机制。它是一个在运行时定义 ( define-by-run )的框架,这意味着反向传播是根据代码如何运行来决定的,并且每次迭代可以是不同的。
Autograd
torch.Tensor
是这个包的核心类。如果设置它的属性 .requires_grad
为 True
,那么它将会追踪对于该张量的所有操作。当完成计算后可以通过调用 .backward()
,来自动计算所有的梯度。这个张量的所有梯度将会自动累加到.grad
属性。
注意:在
y.backward()
时,如果 y 是标量,则不需要为backward()
传入任何参数;否则,需要传入一个与 y 同形的 Tensor。
阻止梯度
要阻止一个张量被跟踪历史,可以调用.detach()
方法将其与计算历史分离,并阻止它未来的计算记录被跟踪。为了防止跟踪历史记录(和使用内存),可以将代码块包装在 with torch.no_grad():
中。在评估模型时特别有用,因为模型可能具有 requires_grad = True
的可训练的参数,但是我们不需要在此过程中对他们进行梯度计算。
Function
还有一个类对于autograd
的实现非常重要:Function
。Tensor
和 Function
互相连接生成了一个无环图 (acyclic graph),它编码了完整的计算历史。每个张量都有一个.grad_fn
属性,该属性引用了创建 Tensor
自身的Function
(除非这个张量是用户手动创建的,即这个张量的grad_fn
是 None
)。下面给出的例子中,张量由用户手动创建,因此grad_fn返回结果是None。
1 |
|
计算导数
如果需要计算导数,可以在 Tensor
上调用 .backward()
。如果 Tensor
是一个标量(即它包含一个元素的数据),则不需要为 backward()
指定任何参数,但是如果它有更多的元素,则需要指定一个gradient
参数,该参数是形状匹配的张量。
创建一个张量并设置requires_grad=True
用来追踪其计算历史
1 |
|
对这个张量做一次运算:
1 |
|
y
是计算的结果,所以它有grad_fn
属性。
1 |
|
对 y 进行更多操作
1 |
|
requires_grad_
.requires_grad_(...)
原地改变了现有张量的requires_grad
标志。如果没有指定的话,默认输入的这个标志是 False
。
1 |
|
梯度
现在开始进行反向传播,因为 out
是一个标量,因此out.backward()
和 out.backward(torch.tensor(1.))
等价。
1 |
|
输出导数 d(out)/dx
1 |
|
数学上,若有向量函数 $ \vec{y}=f(\vec{x}) $ ,那么 $ \vec{y} $ 关于 $ \vec{x} $ 的梯度就是一个雅可比矩阵 :
$ J=\left(\begin{array}{ccc}\frac{\partial y_{1}}{\partial x_{1}} & \cdots & \frac{\partial y_{1}}{\partial x_{n}} \ \vdots & \ddots & \vdots \ \frac{\partial y_{m}}{\partial x_{1}} & \cdots & \frac{\partial y_{m}}{\partial x_{n}}\end{array}\right) $ 而 torch.autograd
这个包就是用来计算一些雅可比矩阵的乘积的。例如,如果
$ v $ 是一个标量函数 $ l=g(\vec{y}) $ 的梯度:$ v=\left(\begin{array}{lll}\frac{\partial l}{\partial y_{1}} & \cdots & \frac{\partial l}{\partial y_{m}}\end{array}\right) $ 由链式法则,我们可以得到:
$$
v J=\left(\begin{array}{lll}
\frac{\partial l}{\partial y_{1}} & \cdots & \frac{\partial l}{\partial y_{m}}
\end{array}\right)\left(\begin{array}{ccc}
\frac{\partial y 1}{\partial x_{1}} & \cdots & \frac{\partial y_{1}}{\partial x_{n}} \
\vdots & \ddots & \vdots \
\frac{\partial y_{m}}{\partial x_{1}} & \cdots & \frac{\partial y_{m}}{\partial x_{n}}
\end{array}\right)=\left(\begin{array}{lll}
\frac{\partial l}{\partial x_{1}} & \cdots & \frac{\partial l}{\partial x_{n}}
\end{array}\right)
$$
注意:grad 在反向传播过程中是累加的(accumulated),这意味着每一次运行反向传播,梯度都会累加之前的梯度,所以一般在反向传播之前需把梯度清零。
1 |
|
现在我们来看一个雅可比向量积的例子:
1 |
|
在这种情况下,y
不再是标量。torch.autograd
不能直接计算完整的雅可比矩阵,但是如果我们只想要雅可比向量积,只需将这个向量作为参数传给 backward:
1 |
|
也可以通过将代码块包装在 with torch.no_grad():
中,来阻止 autograd 跟踪设置了.requires_grad=True
的张量的历史记录。
1 |
|
如果我们想要修改 tensor 的数值,但是又不希望被 autograd 记录(即不会影响反向传播), 那么我们可以对 tensor.data 进行操作。
1 |
|
雅克比矩阵
向量对向量的求导结果是雅克比矩阵,比如如下代码:
1 |
|
可以看到,有 $ \vec{X} =[x_1,x_2,x_3] = [1,2,3]$ ,$ \vec{Y} = 2 \vec{X}^2 $
$$
\vec{Y} '= 4 \vec{X}
$$
因此雅克比矩阵为:
$$
\frac{\partial \vec{Y}}{\partial \vec{X}}=\left(\begin{array}{ccc}\frac{\partial y_{1}}{\partial x_{1}} & \frac{\partial y_{1}}{\partial x_{2}} & \frac{\partial y_{1}}{\partial x_{3}} \ \frac{\partial y_{2}}{\partial x_{1}} & \frac{\partial y_{2}}{\partial x_{2}} & \frac{\partial y_{2}}{\partial x_{3}} \ \frac{\partial y_{3}}{\partial x_{1}} & \frac{\partial y_{3}}{\partial x_{2}} & \frac{\partial y_{3}}{\partial x_{3}}\end{array}\right)= \left(\begin{array}{ccc} 4&0&0\0 &8&0\0&0&12 \end{array}\right)
$$
然而这个雅可比矩阵无法直接落到 $\vec{X}$ 的梯度分量上,维度都不一样,因此需要降维,降维也得有个依据啊,因此需要用户输入与 $\vec{Y}$ 维度相同的权重向量,左乘到雅可比矩阵上。
$$
\left{\begin{array}{l}\frac{\partial o u t}{\partial a_{1}}=k_{1} * \frac{\partial \text { out }{1}}{\partial a{1}}+k_{2} * \frac{\partial o u t_{2}}{\partial a_{1}}+k_{3} * \frac{\partial o u t_{3}}{\partial a_{1}}+\ldots+k_{n} * \frac{\partial o u t_{n}}{\partial a_{1}} \ \frac{\partial o u t}{\partial a_{2}}=k_{1} * \frac{\partial o u_{1}}{\partial a_{2}}+k_{2} * \frac{\partial o u t_{2}}{\partial a_{2}}+k_{3} * \frac{\partial o u t_{3}}{\partial a_{2}}+\ldots+k_{n} * \frac{\partial o u t_{n}}{\partial a_{2}} \ \cdots \ \frac{\partial o u t}{\partial a_{n}}=k_{1} * \frac{\partial \text { out }{1}}{\partial a{n}}+k_{2} * \frac{\partial o u t_{2}}{\partial a_{n}}+k_{3} * \frac{\partial o u t_{3}}{\partial a_{n}}+\ldots+k_{n} * \frac{\partial o u t_{n}}{\partial a_{n}}\end{array}\right.
$$
因此对 $y$ 求导 backward
时,需要输入权重张量。
默认情况下求导后导数信息会被清空,其中 retain_graph
参数则可以保持梯度信息不丢失。
参考资料
- https://datawhalechina.github.io/thorough-pytorch/第二章/2.2 自动求导.html
- https://blog.csdn.net/sen873591769/article/details/89819762
文章链接:
https://www.zywvvd.com/notes/study/deep-learning/pytorch/torch-learning/torch-learning-2/
“觉得不错的话,给点打赏吧 ୧(๑•̀⌄•́๑)૭”
微信支付
支付宝支付