PyTorch作为深度学习计算框架引擎自诞生之日起就备受瞩目。 其设计接口简单功能丰富,深藏那些复杂实现,是其受欢迎的主要原因之一,成为深度学习框架广受学术和工业界的热爱构建深度学习应用的工具。本文主要介绍一下Torch的核心自动计算导数原理及应用。
Pytorch 自动梯度计算引擎Autograd
Autograd是使PyTorch灵活和快速构建机器学习项目的原因之一。对一个复杂的计算,它能够快速和简单地计算偏导数(partial derivation-也称梯度)。这个操作是基于回溯学习神经网络(backpropagation-based)学习的关键。
这种自动梯度计算的能力来自于它能在运行时动态跟踪计算,这意味着如果你的模型有决策分支,或是循环次数直到运行时确定,这种计算依旧能被正确的跟踪记录,并且你将能得到正确的梯度用于驱动学习。 这种能力加上你的模型用Python构建,比起那些需要用静态分析严格且僵硬的模型计算梯度的框架更具灵活性。
Autograd的作用
机器学习模型是个函数,有输入和输出。为了讨论,我们将设输入为维向量,元素。我们然后能用M表示这个输入的向量值函数:(模型M的输出值也表示为一个向量,是因为一般来说一个模型或许会有多个输出)。
因为我们将主要讨论在训练(training)情况下的autograd,我们感兴趣的输出将是模型的损失(Loss)函数输出。 损失函数(loss function)是模型输出的单值标量函数(single-valued scalar function)。这个函数表示的是模型的预测和特定输入值理想输出的差。
在训练一个模型中,我们想使损失值最小化。在一个完美模型的理想情况下,那意味着调整学习权重参数,也就是说调整这个函数的参数,比如对所有的输入来说损失为零。在现实情况下,这意味着一个迭代进程用于调整学习权重参数直到我们看到一个对广泛的输入可以得到可容忍的损失值。
我们怎么能确定和在那个方向上调整权重参数?我们要最小化损失值,意味着要使对应输入的一阶导数等于0:。
回忆一下,尽管,损失值不是直接从输入获取到而是一个模型输出的函数(它是输入的函数),。根据微积分的链规则(Chain Rule),我们可以得到:。
是是事情变的复杂起来的地方。对应模型输入的偏导数输出,如果我们试着用导数链规则(chain rule)扩展这个式子,我们会涉及到很多的局部偏导数的计算,比如模型中众多的每个层的学习参数导数,激活函数的导数,和每次数学变换的导数。每个这样的偏导数的完整表达式是通过计算图的每个可能路径的局部梯度的乘积之和,该计算图以我们试图测量其梯度的变量结束。
我们尤其感兴趣的事学习权重参数的梯度,他们告诉我们改变那个方向的权重可以使损失函数(Loss Function)接近于零。
因为这种局部导数的数量随着神经网络的深度成指数级增长,因此计算他们的复杂度也在增大。 这就是Autograd的价值所在:它能跟踪每次计算。每次在你的PyTorch模型计算的tensor都承载着它的输入Tensor的历史和用于创建它的函数信息。结合作用于Tensor的PyTorch的函数都会有它们自己计算导数的实现,这就极大地加快了为了学习计算局部导数的速度。
Autograd应用的例子
对简单函数参数求导
这个例子是单输入单输出的,比较简单我们求解对的导数,首先我们声明变量和 并且指定其属性require_grad=True以告诉系统自动计算其导数。从分析得知公式的对求导公式是。
import torch
import torch.nn as nn
x=torch.tensor(2,dtype=torch.float,requires_grad=True)
b=torch.tensor(1)
y=2*x**2+b
y.backward()
print(y)
print(x.grad)
对应的输出是:
tensor(9., grad_fn=<AddBackward0>)
tensor(8.)
其中,x赋值是标量2,所以公式计算结果,其对应的导数,导数的计算是在回溯的时候自动计算出来的。 这个结果和我们通过分析方法求解导数的计算结果一致,注意叶子节点在我们声明其时指定属性requires_grad=True是告诉程序在回溯计算时自动保留导数。这个例子较为简单,让我们看一个稍微复杂的例子。
多维线性函数梯度计算
这个稍微复杂的梯度计算,输入是向量,参数是多维和输出是标量的梯度计算。和上个例子一样,这个是对参数的梯度计算,主要是输入确定情况下,参数和对输出的影响率。完整的公式.公式中,和分别为参数矩阵,输入向量和偏置向量。以下代码是借助于Torch的Autograd机制,获取对和梯度。
标量输出对矩阵参数的偏导(Scalar-by-matrix)
如何计算输出对参数矩阵偏导数,矩阵3x4$矩阵,如下所示。
对矩阵求偏导,得到梯度矩阵。 其中向量和
标量输出对矩阵参数的偏导(Scalar-by-vector)
计算输出对向量的偏导数是比较简单的,如下公式。
import torch
import torch.nn as nn
x=torch.tensor([3.0,2.0,4.0,5.0],dtype=torch.float,requires_grad=False)
W=torch.tensor([[1,2,3,4],[1,2,3,4],[1,2,3,4]],dtype=torch.float,requires_grad=True)
b=torch.tensor([4.0,5.0,8.0],dtype=torch.float,requires_grad=True)
output=torch.matmul(W,x)+b
y=output.sum()
print(f"the y grad is {output.grad_fn}")
print(y)
output.backward()
print(x.grad)
print(W.grad)
print(b.grad)
对应的输出:
the y grad is <AddBackward0 object at 0x110e30730>
tensor(134., grad_fn=<SumBackward0>)
None
tensor([[3., 2., 4., 5.],
[3., 2., 4., 5.],
[3., 2., 4., 5.]])
tensor([1., 1., 1.])
公式, 注意到的属性required_grad=False的设置,表示不必计算对应x的导数,但其他的两个和需要跟踪计算其导数。注意到的导数输出为None,响应了x的属性required_grad=Flase的设置,不计算其导数。而和,则在定义变量时指定其属性required_grad=True在上面的程序中,输出结果中可以看到其对应的偏导数矩阵和偏导数向量,响应的了其设置。 可以看到计算分了两个阶段,一个是正向传播(forward)计算,一个是反向传播(backward)计算。正向传播有时也会称推理(reason),可以根据输入值得到输出结论。反向传播则是为计算导数或偏导数并为更新参数做准备。y.backward()函数就是用于回溯传播导数计算。
总结
本文介绍了PyTorch框架中的Autograd机制,和利用代码求解导数和偏导数的方法。这些自动化机制已经深嵌到PyTorch的框架中,极大帮助深度学习应用者设计自己的深度学习应用。在以后的文章里会介绍如何利用Autograd机制和PyTorch其他的一些机制,设计自己的深度学习应用。