当前位置:   article > 正文

翻译: 2.5. 自动微分 深入神经网络 pytorch_神经网络的自动微分

神经网络的自动微分

正如我们在第 2.4 节中所解释的,微分是几乎所有深度学习优化算法中的关键步骤。虽然获取这些导数的计算很简单,只需要一些基本的微积分,但对于复杂的模型,手动计算更新可能会很痛苦(而且通常容易出错)。

深度学习框架通过自动计算导数来加速这项工作,即自动微分。在实践中,基于我们设计的模型,系统构建了一个计算图,跟踪哪些数据通过哪些操作组合以产生输出。自动微分使系统能够随后反向传播梯度。在这里,反向传播只是意味着跟踪计算图,填充关于每个参数的偏导数。

2.5.1 一个简单的例子

在这里插入图片描述

import torch

x = torch.arange(4.0)
x
  • 1
  • 2
  • 3
  • 4
tensor([0., 1., 2., 3.])

  • 1
  • 2

在我们计算梯度之前y关于x ,我们需要一个地方来存放它。重要的是,我们不要在每次对参数求导时分配新内存,因为我们经常会更新相同的参数数千或数百万次,并且可能会很快耗尽内存。请注意,标量值函数相对于向量的梯度 x本身是向量值的并且具有相同的形状x .

x.requires_grad_(True)  # Same as `x = torch.arange(4.0, requires_grad=True)`
x.grad  # The default value is None
  • 1
  • 2

现在让我们计算y.

y = 2 * torch.dot(x, x)
y
  • 1
  • 2
tensor(28., grad_fn=<MulBackward0>)

  • 1
  • 2

由于x是长度为 4 的向量,因此执行x和的点积x ,产生我们分配给 的标量输出y。接下来,我们可以通过调用反向传播函数并打印梯度来自动计算y相对于每个分量的梯度。

y.backward()
x.grad
  • 1
  • 2
tensor([ 0.,  4.,  8., 12.])

  • 1
  • 2

在这里插入图片描述

x.grad == 4 * x

  • 1
  • 2
tensor([True, True, True, True])

  • 1
  • 2

现在让我们计算 的另一个函数x。

# PyTorch accumulates the gradient in default, we need to clear the previous
# values
x.grad.zero_()
y = x.sum()
y.backward()
x.grad
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
tensor([1., 1., 1., 1.])

  • 1
  • 2

2.5.2. 非标量变量的反向传播

从技术上讲,当y不是标量时,向量y对向量的微分最自然的解释x 是矩阵。对于高阶和高维y和x,微分结果可能是高阶张量。

然而,虽然这些更奇特的对象确实出现在高级机器学习(包括深度学习)中,但更常见的是,当我们对向量进行反向传播调用时,我们会尝试计算批次中每个组成部分的损失函数的导数。训练示例。在这里,我们的目的不是计算微分矩阵,而是为批处理中的每个示例单独计算的偏导数之和。

# Invoking `backward` on a non-scalar requires passing in a `gradient` argument
# which specifies the gradient of the differentiated function w.r.t `self`.
# In our case, we simply want to sum the partial derivatives, so passing
# in a gradient of ones is appropriate
x.grad.zero_()
y = x * x
# y.backward(torch.ones(len(x))) equivalent to the below
y.sum().backward()
x.grad
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
tensor([0., 2., 4., 6.])

  • 1
  • 2

2.5.3. 分离计算

有时,我们希望将一些计算移到记录的计算图之外。例如,假设y是作为 的函数计算的x,并且随后z是作为 和 的函数计算y的x。现在,假设我们想计算z相对于的梯度x,但出于某种原因想将y其视为一个常数,并且只考虑计算x后y所起的作用。

在这里,我们可以分离y以返回一个新变量u,该变量具有相同的值,y但会丢弃有关如何y在计算图中计算的任何信息。换句话说,梯度不会倒流u到x。因此,以下反向传播函数计算 相对于的偏导数,同时将其视为常数,而不是相对于 的偏导数。z = u * xuz = x * x * x

x.grad.zero_()
y = x * x
u = y.detach()
z = u * x

z.sum().backward()
x.grad == u
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
tensor([True, True, True, True])
  • 1

由于y记录了 的计算,我们随后可以调用反向传播y来获得 的导数,即。y = x * x

x.grad.zero_()
y.sum().backward()
x.grad == 2 * x
  • 1
  • 2
  • 3
tensor([True, True, True, True])
  • 1

2.5.4。计算 Python 控制流的梯度

使用自动微分的一个好处是,即使构建函数的计算图需要通过 Python 控制流的迷宫(例如,条件、循环和任意函数调用),我们仍然可以计算结果变量的梯度。在下面的代码片段中,请注意 while循环的迭代次数和语句的评估if都取决于 input 的值a。

def f(a):
    b = a * 2
    while b.norm() < 1000:
        b = b * 2
    if b.sum() > 0:
        c = b
    else:
        c = 100 * b
    return c
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

让我们计算梯度。

a = torch.randn(size=(), requires_grad=True)
d = f(a)
d.backward()
  • 1
  • 2
  • 3

我们现在可以分析f上面定义的函数。请注意,它的输入是分段线性的a。换句话说,对于任何a存在一些常数标量k这样,其中 的值取决于输入。因此,我们可以验证梯度是否正确。f(a) = d / a

a.grad == d / a
  • 1
tensor(True)
  • 1

2.5.5 概括

深度学习框架可以自动计算导数。为了使用它,我们首先将梯度附加到我们希望偏导数的那些变量上。然后我们记录目标值的计算,执行它的反向传播函数,并访问结果梯度。

2.5.6。练习

在这里插入图片描述

参考

https://d2l.ai/chapter_preliminaries/autograd.html

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Monodyee/article/detail/288287
推荐阅读
相关标签
  

闽ICP备14008679号