4

深扒torch.autograd原理

 1 year ago
source link: https://blog.51cto.com/Lolitann/5951280
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

深扒torch.autograd原理

精选 原创

当我们训练神经网络的时候,最常用的就是使用反向传播。在反向传播过程中,参数变化只和给定参数通过loss函数计算的梯度(gradient)有关。

PyTorch的torch.autograd提供了自动梯度计算,可以用于自动计算任何计算图的梯度。

举个简单的例子嗷,假设我们只有一层神经网络。输入为xxx,权重是www,bias是bbb,这里使用二元交叉熵(binary_cross_entropy)损失进行计算。

我们直接上计算图,图是pytorch官网的一张图:

深扒torch.autograd原理_有向无环图

这里zzz往前这一块是单层神经网络的计算,yyy是你的ground truth,用zzz和yyy计算二元交叉熵损失。

上面的整个过程可以用如下代码实现。

  • 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)
    

www和bbb是我们的可学习参数,所以这里我们要手动将其设置一下,告诉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} ∂w∂loss​和∂loss∂b\frac{\partial loss}{\partial b}∂b∂loss​。

这一步需要我们手动调用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原理_神经网络_02

还是看这张图,从概念上讲,autograd在一个由Function对象组成的有向无环图(DAG)中保存数据(张量)和所有执行的操作(以及产生的新张量)的记录。

在这个有向无环图中,叶子节点是是输入张量,根节点是输出张量。通过从根到叶追踪这个图的导数计算,可以使用链式法则自动计算梯度。

forward过程中,autograd同时在做两件事:

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

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

backward过程中,autograd做的是:

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

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

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


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK