目录

了解基础知识 ||快速入门 ||张量 ||数据集和数据加载器 ||变换 ||构建模型 ||Autograd ||优化 ||保存并加载模型

自动微分torch.autograd

创建时间: 2021年2月10日 |上次更新时间:2024 年 1 月 16 日 |上次验证: Nov 05, 2024

在训练神经网络时,最常用的算法是反向传播。在此算法中,参数 (模型权重) 为 根据 loss 函数的梯度进行调整 添加到给定的参数中。

为了计算这些梯度,PyTorch 有一个内置的微分引擎 叫。它支持自动计算任何 计算图。torch.autograd

考虑最简单的单层神经网络,输入 , parameters 和 ,以及一些 loss 函数。它可以在 PyTorch 的调用方法如下:xwb

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)

张量、函数和计算图

此代码定义以下计算图

在这个网络中,和 是参数,我们需要 优化。因此,我们需要能够计算损失的梯度 函数。为此,我们设置 这些张量的属性。wbrequires_grad

注意

您可以在创建 Tensor 或更高版本的 USING METHOD。requires_gradx.requires_grad_(True)

我们应用于张量以构建计算图的函数是 实际上是类 .此对象知道如何 计算正向的函数,以及如何计算 它在向后传播步骤中的导数。对 向后传播函数存储在 张肌。您可以在 文档Functiongrad_fnFunction

print(f"Gradient function for z = {z.grad_fn}")
print(f"Gradient function for loss = {loss.grad_fn}")
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}\) 。为了计算这些导数,我们调用 ,然后从 和 中检索值:xyloss.backward()w.gradb.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])

注意

  • 我们只能获取叶子的属性 计算图的节点,其属性 设置为 。对于图中的所有其他节点,梯度不会是 可用。gradrequires_gradTrue

  • 出于性能原因,我们只能在给定的图形上使用 once 执行梯度计算。如果我们需要 要对同一张图执行多次调用,我们需要传递给调用。backwardbackwardretain_graph=Truebackward

禁用渐变跟踪

默认情况下,所有具有 的张量都在跟踪其 计算历史并支持梯度计算。然而,那里 在某些情况下,我们不需要这样做,例如,当我们有 训练了模型,只想将其应用于一些输入数据,即我们 只想通过网络进行前向计算。我们可以停止 通过使用 Block 包围我们的计算代码来跟踪计算:requires_grad=Truetorch.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()

z = torch.matmul(x, w)+b
z_det = z.detach()
print(z_det.requires_grad)
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}\)的梯度由雅可比式给出 矩阵

\[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)\]

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 归零。在实际训练中,优化器可以帮助我们完成 这。backwardbackwardgradgrad

注意

以前,我们调用 function 而不调用 参数。这本质上等同于调用 ,这是计算 标量值函数时的梯度,例如 神经网络训练。backward()backward(torch.tensor(1.0))


文档

访问 PyTorch 的全面开发人员文档

查看文档

教程

获取面向初学者和高级开发人员的深入教程

查看教程

资源

查找开发资源并解答您的问题

查看资源