赞
踩
正如我们在第 2.4 节中所解释的,微分是几乎所有深度学习优化算法中的关键步骤。虽然获取这些导数的计算很简单,只需要一些基本的微积分,但对于复杂的模型,手动计算更新可能会很痛苦(而且通常容易出错)。
深度学习框架通过自动计算导数来加速这项工作,即自动微分。在实践中,基于我们设计的模型,系统构建了一个计算图,跟踪哪些数据通过哪些操作组合以产生输出。自动微分使系统能够随后反向传播梯度。在这里,反向传播只是意味着跟踪计算图,填充关于每个参数的偏导数。
import torch
x = torch.arange(4.0)
x
tensor([0., 1., 2., 3.])
在我们计算梯度之前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
现在让我们计算y.
y = 2 * torch.dot(x, x)
y
tensor(28., grad_fn=<MulBackward0>)
由于x是长度为 4 的向量,因此执行x和的点积x ,产生我们分配给 的标量输出y。接下来,我们可以通过调用反向传播函数并打印梯度来自动计算y相对于每个分量的梯度。
y.backward()
x.grad
tensor([ 0., 4., 8., 12.])
x.grad == 4 * x
tensor([True, True, True, True])
现在让我们计算 的另一个函数x。
# PyTorch accumulates the gradient in default, we need to clear the previous
# values
x.grad.zero_()
y = x.sum()
y.backward()
x.grad
tensor([1., 1., 1., 1.])
从技术上讲,当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
tensor([0., 2., 4., 6.])
有时,我们希望将一些计算移到记录的计算图之外。例如,假设y是作为 的函数计算的x,并且随后z是作为 和 的函数计算y的x。现在,假设我们想计算z相对于的梯度x,但出于某种原因想将y其视为一个常数,并且只考虑计算x后y所起的作用。
在这里,我们可以分离y以返回一个新变量u,该变量具有相同的值,y但会丢弃有关如何y在计算图中计算的任何信息。换句话说,梯度不会倒流u到x。因此,以下反向传播函数计算 相对于的偏导数,同时将其视为常数,而不是相对于 的偏导数。z = u * x
, uz = x * x * x
x.grad.zero_()
y = x * x
u = y.detach()
z = u * x
z.sum().backward()
x.grad == u
tensor([True, True, True, True])
由于y记录了 的计算,我们随后可以调用反向传播y来获得 的导数,即。y = x * x
x.grad.zero_()
y.sum().backward()
x.grad == 2 * x
tensor([True, True, True, True])
使用自动微分的一个好处是,即使构建函数的计算图需要通过 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
让我们计算梯度。
a = torch.randn(size=(), requires_grad=True)
d = f(a)
d.backward()
我们现在可以分析f上面定义的函数。请注意,它的输入是分段线性的a。换句话说,对于任何a存在一些常数标量k这样,其中 的值取决于输入。因此,我们可以验证梯度是否正确。f(a) = d / a
a.grad == d / a
tensor(True)
深度学习框架可以自动计算导数。为了使用它,我们首先将梯度附加到我们希望偏导数的那些变量上。然后我们记录目标值的计算,执行它的反向传播函数,并访问结果梯度。
https://d2l.ai/chapter_preliminaries/autograd.html
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。