持续创造,加快生长!这是我参与「日新计划 10 月更文挑战」的第27天,点击查看活动详情


当咱们练习神经网络的时分,最常用的便是运用反向传达。在反向传达进程中,参数变化只和给定参数经过loss函数核算的梯度(gradient)有关。

PyTorch的torch.autograd提供了主动梯度核算,能够用于主动核算任何核算图的梯度。

举个简单的比如嗷,假设咱们只有一层神经网络。输入为xx,权重是ww,bias是bb,这儿运用二元穿插熵(binary_cross_entropy)丢失进行核算。

咱们直接上核算图,图是pytorch官网的一张图:

深扒torch.autograd原理

这儿zz往前这一块是单层神经网络的核算,yy是你的ground truth,用zzyy核算二元穿插熵丢失。

上面的整个进程能够用如下代码完成。

  • 写法一:

    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)
    
  • 写法二:

    import torch
    x = torch.ones(5)  # input tensor
    y = torch.zeros(3)  # expected output
    w = torch.randn(5, 3)
    b = torch.randn(3)
    z = torch.matmul(x, w)+b
    loss = torch.nn.functional.binary_cross_entropy_with_logits(z, y)
    w.requires_grad_(True)
    b.requires_grad_(True)
    

wwbb是咱们的可学习参数,所以这儿咱们要手动将其设置一下,告知pytorch说咱们需求核算这两个参数的梯度。

两个写法的差异便是你要在声明变量的时分直接运用requires_grad = True设置仍是声明之后单独设置x.requires_grad_(True)

咱们运用Function目标结构核算图,它能够核算forward进程,并直接核算器在反向传达进程中的导数,反向传达进程存储在属性grad_fn中。

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 0x7ff369b0d310>
Gradient function for loss = <BinaryCrossEntropyWithLogitsBackward0 object at 0x7ff3772319d0>

核算梯度

咱们让神经网络学习的目的是想要优化网络的参数,也便是在学习进程中咱们要运用丢失函数核算参数的导数。咱们要核算的有∂loss∂w\frac{\partial loss}{\partial w}∂loss∂b\frac{\partial loss}{\partial b}

这一步需求咱们手动调用loss.backward()进行核算,之后咱们就能够运用w.gradb.grad查看两个参数的导数值。

loss.backward()
print(w.grad)
print(b.grad)

这儿有几点注意事项:

  • 咱们只能运用grad来求核算图中设置了requires_grad = True的叶子节点的梯度,没办法获取其他节点的梯度。

  • 咱们对一个核算图运用loss.backward()的时分只能用一次,但是为了模型的性能,咱们可能要多次对同一个图进行loss.backward(),咱们需求在调用loss.backward()的时分传入参数retain_graph=True

封闭梯度追寻

默认状况下,当你设置了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

还有另一种方法,不过不太常见,运用detach(),写法如下:

z = torch.matmul(x, w)+b
z_det = z.detach()
print(z_det.requires_grad)

False

封闭梯度追寻的状况:

  • 想冻住住你模型的一部分,将其中一些参数变为冻住参数。比如你微调预练习模型的时分。

  • 加快核算,当你只想核算前向核算进程的时分,便是我上边提到的状况,不运用梯度追寻能够让核算更高效。


关于autograd的更多的常识

深扒torch.autograd原理

仍是看这张图,从概念上讲,autograd在一个由Function目标组成的有向无环图(DAG)中保存数据(张量)和所有履行的操作(以及发生的新张量)的记载。

在这个有向无环图中,叶子节点是是输入张量,根节点是输出张量。经过从根到叶追寻这个图的导数核算,能够运用链式法则主动核算梯度。

forward进程中,autograd一起在做两件事:

  • 按照要求的操作核算,并获得最终的结果张量

  • 维持有向无环图中核算的梯度函数

backward进程中,autograd做的是:

  • 从之前保存的.grad_fn中核算梯度

  • 将结果累积存储在张量的.grad属性中

  • 运用链式法则进行反向传达,直到叶子节点。