注意
单击此处下载完整的示例代码
了解基础知识 ||快速入门 ||张量 ||数据集和数据加载器 ||变换 ||构建模型 ||Autograd ||优化 ||保存并加载模型
自动微分torch.autograd
¶
创建时间: 2021年2月10日 |上次更新时间:2024 年 1 月 16 日 |上次验证: Nov 05, 2024
在训练神经网络时,最常用的算法是反向传播。在此算法中,参数 (模型权重) 为 根据 loss 函数的梯度进行调整 添加到给定的参数中。
为了计算这些梯度,PyTorch 有一个内置的微分引擎
叫。它支持自动计算任何
计算图。torch.autograd
考虑最简单的单层神经网络,输入 ,
parameters 和 ,以及一些 loss 函数。它可以在
PyTorch 的调用方法如下:x
w
b
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)
张量、函数和计算图¶
此代码定义以下计算图:
在这个网络中,和 是参数,我们需要
优化。因此,我们需要能够计算损失的梯度
函数。为此,我们设置
这些张量的属性。w
b
requires_grad
注意
您可以在创建
Tensor 或更高版本的 USING METHOD。requires_grad
x.requires_grad_(True)
我们应用于张量以构建计算图的函数是
实际上是类 .此对象知道如何
计算正向的函数,以及如何计算
它在向后传播步骤中的导数。对
向后传播函数存储在
张肌。您可以在
文档。Function
grad_fn
Function
Gradient function for z = <AddBackward0 object at 0x7f9c83065180>
Gradient function for loss = <BinaryCrossEntropyWithLogitsBackward0 object at 0x7f9c8118fca0>
计算梯度¶
为了优化神经网络中参数的权重,我们需要
计算我们的损失函数关于参数的导数,
也就是说,在一些固定的 和 值下,我们需要 \(\frac{\partial loss}{\partial w}\) 和 \(\frac{\partial loss}{\partial b}\) 。为了计算这些导数,我们调用 ,然后从 和 中检索值:x
y
loss.backward()
w.grad
b.grad
loss.backward()
print(w.grad)
print(b.grad)
tensor([[0.3313, 0.0626, 0.2530],
[0.3313, 0.0626, 0.2530],
[0.3313, 0.0626, 0.2530],
[0.3313, 0.0626, 0.2530],
[0.3313, 0.0626, 0.2530]])
tensor([0.3313, 0.0626, 0.2530])
注意
我们只能获取叶子的属性 计算图的节点,其属性 设置为 。对于图中的所有其他节点,梯度不会是 可用。
grad
requires_grad
True
出于性能原因,我们只能在给定的图形上使用 once 执行梯度计算。如果我们需要 要对同一张图执行多次调用,我们需要传递给调用。
backward
backward
retain_graph=True
backward
禁用渐变跟踪¶
默认情况下,所有具有 的张量都在跟踪其
计算历史并支持梯度计算。然而,那里
在某些情况下,我们不需要这样做,例如,当我们有
训练了模型,只想将其应用于一些输入数据,即我们
只想通过网络进行前向计算。我们可以停止
通过使用 Block 包围我们的计算代码来跟踪计算: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)
True
False
实现相同结果的另一种方法是使用
在 Tensor 上:detach()
False
- 出于多种原因,您可能希望禁用渐变跟踪:
将神经网络中的某些参数标记为冻结参数。
在仅执行前向传递时加快计算速度,因为对执行 不跟踪渐变会更有效。
有关计算图的更多信息¶
从概念上讲,autograd 会保留数据(张量)的记录,并执行所有数据 操作(以及生成的新张量)在 Directed Acyclic 中 由 Function 对象组成的图形 (DAG)。在这个 DAG 中,叶子是输入张量,根是输出 张。通过从根到叶跟踪此图,您可以 使用链式规则自动计算梯度。
在前向传递中,autograd 同时执行两件事:
运行请求的操作以计算生成的 Tensor
在 DAG 中保持操作的 gradient 函数。
在 DAG 上调用时,将启动向后传递
根。 然后:.backward()
autograd
计算每个 的梯度 ,
.grad_fn
将它们累积到相应 Tensor 的属性中
.grad
使用 chain rule,一直传播到 Leaf Tensors。
注意
DAG 在 PyTorch 中是动态的需要注意的重要一点是,该图是从头开始重新创建的;每次调用后,Autograd 都会开始填充新图表。这是
确切地说,是什么允许您在模型中使用控制流语句;
如果满足以下条件,则可以在每次迭代中更改形状、大小和操作
需要。.backward()
可选阅读材料:张量梯度和雅可比积¶
在很多情况下,我们有一个标量损失函数,我们需要计算 相对于某些参数的梯度。但是,也有情况 当 output 函数是任意张量时。在本例中,PyTorch 允许您计算所谓的雅可比积,而不是实际的 梯度。
对于向量函数\(\vec{y}=f(\vec{x})\),其中\(\vec{x}=\langle x_1,\dots,x_n\rangle\)和\(\vec{y}=\langle y_1,\dots,y_m\rangle\),\(\vec{y}\)相对于\(\vec{x}\)的梯度由雅可比式给出 矩阵:
PyTorch 允许您
计算给定输入向量 \(v=(v_1 \dots v_m)\) 的雅可比乘积 \(v^T\cdot J\)。这是通过使用 \(v\) 作为参数进行调用来实现的。\(v\) 的大小应与
原始张量的大小,我们想要
计算乘积:backward
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}")
First call
tensor([[4., 2., 2., 2., 2.],
[2., 4., 2., 2., 2.],
[2., 2., 4., 2., 2.],
[2., 2., 2., 4., 2.]])
Second call
tensor([[8., 4., 4., 4., 4.],
[4., 8., 4., 4., 4.],
[4., 4., 8., 4., 4.],
[4., 4., 4., 8., 4.]])
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.]])
请注意,当我们使用相同的
参数,则 gradient 的值不同。发生这种情况是因为
在执行传播时,PyTorch 会累积
gradients,即计算出的 gradients 的值被添加到计算图的所有叶节点的属性中。如果需要帮助,
要计算正确的梯度,您需要先将 property 归零。在实际训练中,优化器可以帮助我们完成
这。backward
backward
grad
grad
注意
以前,我们调用 function 而不调用
参数。这本质上等同于调用 ,这是计算
标量值函数时的梯度,例如
神经网络训练。backward()
backward(torch.tensor(1.0))