欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

PyTorch Autograd

程序员文章站 2022-07-13 13:18:20
...

PyTorch Autograd

Background

我们不得不认同,在涉及大型神经网络时,我们都不擅长微积分。通过显式求解数学方程式来计算此类大型复合函数的梯度是不切实际的,尤其是当这些曲线存在于大量维度中并且无法理解其具体含义时。
这就是PyTorch的autograd出现的地方。它抽象了复杂的数学,并帮助我们“神奇地”仅用几行代码即可计算出高维曲线的梯度。这篇文章试图描述autograd的魔力。

PyTorch Basics

在继续之前,我们需要了解一些基本的PyTorch概念。
张量(Tensor):简单来说,它只是PyTorch中的n维数组。张量支持一些其他增强功能,使其具有独特性:除CPU外,还可以加载GPU以进行更快的计算。设置.requires_grad = True时,它们开始形成一个向后图(backward graph),该图使用称为动态计算图(dynamic computation graph)的东西追踪应用在其上的所有操作以计算梯度(在后面进一步解释)
在PyTorch的早期版本中,使用了tortor.autograd.Variable类来创建支持梯度计算和操作跟踪的张量,但自PyTorch v0.4.0起,不推荐使用Variable类。 torch.Tensor和torch.autograd.Variable现在是同一类。更准确地说,torch.Tensor能够跟踪历史记录,其行为类似于旧的Variable

import torch
import numpy as np

x = torch.randn(2,3,requires_grad = True)

# From numpy
x = np.array([1., 2., 3.]) # 只有float类型的tensors可以require 梯度
x = torch.from_numpy(x)
# 现在可以求梯度了
x.requires_grad(True)

注意:根据PyTorch的设计,只能为浮点张量计算梯度,这就是为什么我在将其设为启用梯度的PyTorch张量之前创建了一个float类型的numpy数组的原因
Autograd:此类是计算导数的引擎(更精确地说,是Jacobian矢量积)。它记录了在启用梯度的张量上执行的所有操作的图,并创建了一个称为动态计算图的非循环图。该图的叶子是输入张量,根是输出张量。通过从根到叶跟踪图并使用链法则将每个梯度相乘来计算梯度

Neural networks and Backpropagation

神经网络无非就是经过微调(训练)以输出所需结果的复合数学函数。通过称为反向传播的出色算法来进行调整或训练。反向传播用于根据输入权重来计算损失的梯度,以便在以后更新权重并最终减少损失。
创建和训练神经网络涉及以下基本步骤:

  1. 定义架构 (Define the architecture)
  2. 使用输入数据在体系结构上进行正向传播
  3. 计算损失
  4. 反向传播以计算每个权重的梯度
  5. 使用学习率更新权重
    对于输入权重的微小变化,损失的变化称为该权重的梯度,并使用反向传播进行计算。然后使用学习速率将梯度用于更新权重,以总体上减少损失并训练神经网络。
    这些都以迭代方式完成。对于每次迭代,都会计算几个梯度,然后会建立一个称为计算图的东西来存储这些梯度函数。 PyTorch通过构建动态计算图(DCG)来实现。该图是在每次迭代中从头开始构建的,为梯度计算提供了最大的灵活性。

Dynamic Computational graph

梯度使得张量(变量)与函数(操作)结合在一起创建动态计算图。数据流和应用于数据的操作是在运行时定义的,因此可以动态构建计算图。该图由autograd类动态创建。在开始训练之前,您不必编码所有可能的路径。
一个用于将两个张量相乘的简单DCG如下所示:
PyTorch Autograd
图形中的每个虚线框是一个变量,紫色矩形框是一个操作。
每个变量对象都有几个成员,其中一些是:
数据:变量保存的数据。 x是一个1x1的张量,其值等于1.0,而y的值为2.0。 z是两个值的乘积,即2.0
require_grad:如果为true,则此成员开始跟踪所有操作历史记录并形成后向图用于梯度计算。对于任意张量a,可以按如下方式对其操作:a.requires_grad_(True)。
grad:grad保存梯度的值。如果require_grad为False,则将保留None值。即使require_grad为True,它也将保持None值,除非从其他某个节点调用.backward()函数。例如,如果您对x变量调用out.backward()函数,则x.grad将保存∂out/∂x。
grad_fn:这是用于计算梯度的backward函数。
is_leaf:一个节点是叶子具备一下3个特点:

  1. 它是通过x = torch.tensor(1.0)或x = torch.randn(1,1)之类的函数显式初始化的(基本上是本文开头讨论的所有张量初始化方法)。
  2. 它是在对具有require_grad = False的张量进行运算后创建的。
  3. 它是通过在某个张量上调用.detach()方法创建的。
    在调用 backward()时,仅对同时具有require_grad和is_leaf =True的节点计算梯度。梯度是从调用backward()函数的节点中输出的
    设置require_grad = True时, PyTorch将开始跟踪操作并存储每个步骤的梯度函数,如下所示:。
    PyTorch Autograd
    代码如下所示:
import torch

# Creating the graph
x = torch.tensor(1.0, requires_grad = True)
y = torch.tensor(2.0)
z = x * y

# Displaying
for i, name in zip([x, y, z], "xyz"):
    print(f"{name}\ndata: {i.data}\nrequires_grad: {i.requires_grad}\n\
grad: {i.grad}\ngrad_fn: {i.grad_fn}\nis_leaf: {i.is_leaf}\n")
###输出 start
x
data: 1.0
requires_grad: True
grad: None
grad_fn: None
is_leaf: True

y
data: 2.0
requires_grad: False
grad: None
grad_fn: None
is_leaf: True

z
data: 2.0
requires_grad: True
grad: None
grad_fn: <MulBackward0 object at 0x7f0809856668>
is_leaf: False
### 输出 end

Backward() function

Backward是通过将参数(默认为1x1单位张量)通过后向图形(backward graph)一直传递到从调用根张量可追溯到每个叶节点的方法来实际计算梯度的函数。然后将计算出的梯度存储在每个叶节点的.grad中。请记住,在向前传递过程中已经动态创建了反向图。向后功能仅使用已制作的图形计算梯度并将其存储在叶节点中。
让我们分析以下代码:

import torch
# Creating the graph
x = torch.tensor(1.0, requires_grad = True)
z = x ** 3
z.backward() #Computes the gradient 
print(x.grad.data) #Prints '3' which is dz/dx 

传递给后向函数的张量的作用类似于权重,用于梯度的加权输出。从数学上讲,这是矢量乘以非标量张量的雅可比矩阵(在本文中进一步讨论),因此,除非需要计算加权输出,否则它几乎总是单位张量,其尺寸应与向后张量相同。
向后图(Backward graph )是由autograd类在前向过程中自动动态创建的。 Backward()只需将其参数传递给已制成的向后图(Backward graph )即可计算梯度。

Mathematics — Jacobians and vectors

从数学上讲,autograd类只是一个雅可比矢量积计算引擎。用很简单的话来说,雅可比矩阵是代表两个向量的所有可能偏导数的矩阵。它是一个向量相对于另一个向量的梯度。
注意:在此过程中,PyTorch不会显式构造整个Jacobian。直接计算JVP(Jacobian矢量积)通常更简单,更高效。
如果向量X = [x1,x2,… xn],通过函数f用于计算其他向量f(X)= [f1,f2,… fn],则雅可比矩阵(J)仅包含所有偏导数组合,如下所示:
PyTorch Autograd
上面的矩阵表示了f(X)相对于X的梯度
假设启用了PyTorch梯度的张量X为:
X = [x1,x2,……xn](这是某些机器学习模型的权重)
X进行一些运算以形成向量Y
Y = f(X)= [y1,y2,…。 ym]
然后,使用Y来计算标量损失l。假设向量v恰好是标量损失l相对于向量Y的梯度,如下所示:
PyTorch Autograd
为了获得损失l相对于权重X的梯度,将Jacobian矩阵J与向量v相乘
PyTorch Autograd

Further reading

Backpropagation: Quick revision
PyTorch: Automatic differentiation package — torch.autograd
Autograd source code
Video: PyTorch Autograd Explained — In-depth Tutorial by Elliot Waite