PyTorch autograd教程
学习目标
通过本课程,学员可掌握PyTorch自动微分机制的核心原理:理解requires_grad属性构建动态计算图的原理,通过grad_fn追踪梯度路径;掌握反向传播流程,学会用loss.backward()计算梯度及优化器的梯度清零操作;针对模型推理、参数冻结等场景,学习使用torch.no_grad()和detach()方法禁用梯度跟踪,有效提升计算效率与资源利用率。
相关知识点
- 自动微分与计算图
- 梯度计算与反向传播控制
学习内容
1 自动微分与计算图
在深度学习领域,自动微分是一项至关重要的技术,它为神经网络的训练提供了高效的梯度计算方法。在传统的神经网络训练中,反向传播算法用于计算损失函数关于模型参数(如权重和偏置)的梯度,以便通过梯度下降等优化算法来调整参数,使模型的预测结果更接近真实值。
%matplotlib inline
1.1 使用torch.autograd实现自动微分
在训练神经网络时,最常用的算法是
反向传播。在该算法中,参数(模型权重)为
根据损失函数的梯度调整
到给定参数。
为了计算这些梯度,PyTorch有一个内置的微分
名为torch.autograd的引擎。它支持自动计算
任何计算图的梯度。
考虑最简单的一层神经网络,输入x
参数w和b,以及一些损失函数。它可以定义在
PyTorch的使用方式如下
import torch
x = torch.ones(5) # input tensor
y = torch.zeros(3) # expected output
w = torch.randn(5, 3, requires_grad=True)
b = torch.randn(3, requires_grad=True)
z = torch.matmul(x, w)+b
loss = torch.nn.functional.binary_cross_entropy_with_logits(z, y)
1.2 张量、函数和计算图
这段代码定义了以下计算图:

图1 计算图
在这个网络中,w和b是参数,我们需要
因此,我们需要能够计算损失的梯度
函数关于这些变量。为了做到这一点,我们设置
这些张量的requires_grad属性。
<div style="background-color: #54c7ec; color: #fff; font-weight: 700; padding-left: 10px; padding-top: 5px; padding-bottom: 5px"><strong>NOTE:</strong></div>
<div style="background-color: #f3f4f7; padding-left: 10px; padding-top: 10px; padding-bottom: 10px; padding-right: 10px">
<p>您可以在创建张量时设置<code>requires_grad</code>的值,也可以在以后使用<code>x.requires_grad_(True)</code>方法。</p>
</div>
我们应用于张量来构造计算图的函数是
实际上是Function类的对象。这个对象知道如何计算
向前方向的函数,以及如何计算其
在反向传播步骤中的导数。引用了
反向传播函数存储在的grad_fn属性中
张量。你可以在文档中找到Function
。
print(f"Gradient function for z = {z.grad_fn}")
print(f"Gradient function for loss = {loss.grad_fn}")
1.3 更多关于计算图的信息
从概念上讲,autograd保存了一个数据记录(张量),所有执行有向无圈中的运算(以及生成的新张量)图形(DAG)由
函数
对象。在这个DAG中,叶子是输入张量,根是输出张量。通过从根到叶跟踪此图,可以使用链式法则自动计算梯度。
在正向传递中,autograd同时做两件事:
- 运行请求的操作以计算生成的张量。
- 在DAG中维护操作的梯度函数。
当在DAG上调用.backward()时,向后传递开始
根。autograd然后:
- 计算每个
.grad_fn的梯度。 - 将它们累加到相应张量的
.grad属性中。 - 使用链式法则,一直传播到叶张量。
需要注意的一点是,图是从头开始重新创建的,在每次.backward()调用之后,autograd开始填充一个新的图。这正是允许您在模型中使用控制流语句的原因;如果需要,您可以在每次迭代中更改形状、大小和操作。
2 梯度计算与反向传播控制
为了优化神经网络中参数的权重,我们需要计算损失函数相对于参数的导数,即,我们需要
∂
l
o
s
s
∂
w
\frac{\partial loss}{\partial w}
∂w∂loss和
∂
l
o
s
s
∂
b
\frac{\partial loss}{\partial b}
∂b∂loss在x的一些固定值下“y”。为了计算这些导数,我们调用“loss.backward()”,然后从w.grad和b.grad中检索值:
loss.backward()
print(w.grad)
print(b.grad)
- 我们只能获得计算图的叶子节点的
grad属性,这些叶子节点的requires_grad属性设置为True。对于我们图中的所有其他节点,梯度将不可用。-出于性能原因,我们只能在给定图中使用backward执行一次梯度计算。如果我们需要在同一个图上执行多个backward调用,我们需要将retain_graph=True传递给back调用。
2.1 张量梯度和雅可比积(可选)
在很多情况下,我们有一个标量损失函数,我们需要计算
相对于某些参数的梯度。然而,也有一些案例
当输出函数是任意张量时。在这种情况下,PyTorch
允许您计算所谓的雅可比乘积,而不是实际的
梯度。
对于向量函数
y
⃗
=
f
(
x
⃗
)
\vec{y}=f(\vec{x})
y=f(x),其中
x
⃗
=
⟨
x
1
,
…
,
x
n
⟩
\vec{x}=\langle x_1,\dots,x_n\rangle
x=⟨x1,…,xn⟩和
y
⃗
=
⟨
y
1
,
…
,
y
m
⟩
\vec{y}=\langle y_1,\dots,y_m\rangle
y=⟨y1,…,ym⟩,一个梯度
y
⃗
\vec{y}
y
关于
x
⃗
\vec{x}
x由雅可比矩阵给出:
J = ( ∂ y 1 ∂ x 1 ⋯ ∂ y 1 ∂ x n ⋮ ⋱ ⋮ ∂ y m ∂ x 1 ⋯ ∂ y m ∂ x n ) \begin{aligned} 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) \end{aligned} J= ∂x1∂y1⋮∂x1∂ym⋯⋱⋯∂xn∂y1⋮∂xn∂ym
而不是计算Jacobian矩阵本身,PyTorch允许你
对给定的输入向量计算Jacobian积
v
T
⋅
J
v^T\cdot J
vT⋅J。
v
=
(
v
1
…
v
m
)
v=(v_1 \dots v_m)
v=(v1…vm)这是通过使用
v
v
v作为调用backward来实现的
的争论。
v
v
v的大小应该与
原始张量,我们要计算其乘积:
inp = torch.eye(4, 5, requires_grad=True)
out = (inp+1).pow(2).t()
out.backward(torch.ones_like(out), retain_graph=True)
print(f"First call\n{inp.grad}")
out.backward(torch.ones_like(out), retain_graph=True)
print(f"\nSecond call\n{inp.grad}")
inp.grad.zero_()
out.backward(torch.ones_like(out), retain_graph=True)
print(f"\nCall after zeroing gradients\n{inp.grad}")
注意,当我们第二次调用`backward`时,同样
参数,梯度的值是不同的。发生这种情况是因为
在进行`backward`传播时,PyTorch **会累积
Grads**,即将计算的梯度值添加到`grad`
计算图的所有叶子节点的属性。如果你想
计算适当的梯度,需要将`grad`属性归零
在实际训练中,*优化器*帮助我们做到这一点。
之前我们调用的是不带参数的backward()函数。这本质上等同于调用backward(torch.tensor(1.0)),这是在标量值函数的情况下(例如神经网络训练期间的损失)计算梯度的有用方法。
2.2 禁用梯度跟踪
在深度学习模型的训练和推理过程中,有时我们并不需要跟踪所有张量的梯度信息。例如,当模型训练完成后,我们只想将其应用于新的输入数据进行推理,此时只需要进行前向传播计算,不需要计算梯度,跟踪梯度信息反而会消耗额外的内存和计算资源。另外,在某些情况下,我们可能希望将神经网络中的某些参数标记为冻结参数,即不希望这些参数在训练过程中更新,这也需要禁用这些参数的梯度跟踪。
默认情况下,所有具有requires_grad=True的张量都在跟踪它们的
计算历史,并支持梯度计算。然而,有
在某些情况下,我们不需要这样做,例如,当我们
训练了模型,只是想将其应用于一些输入数据,即我们
只希望通过网络进行转发计算。我们可以停止
通过将我们的计算代码包围为
torch.no_grad()块:
z = torch.matmul(x, w)+b
print(z.requires_grad)
with torch.no_grad():
z = torch.matmul(x, w)+b
print(z.requires_grad)
实现相同结果的另一种方法是使用detach()方法
在张量上:
z = torch.matmul(x, w)+b
z_det = z.detach()
print(z_det.requires_grad)
您可能需要禁用梯度跟踪的原因如下:
- 将神经网络中的某些参数标记为冻结参数。
- 当您只执行前向传递时,可以加快计算速度,因为在不跟踪梯度的张量上进行计算会更高效。
1465

被折叠的 条评论
为什么被折叠?



