自动微分技术

小编推荐:

几乎所有机器学习算法在训练或预测时都归结为求解最优化问题,如果目标函数可导,在问题变为训练函数的驻点。通常情况下无法得到驻点的解析解,因此只能采用数值优化算法,如梯度下降法,牛顿法,拟牛顿法。这些数值优化算法都依赖于函数的一阶导数值或二阶导数值,包括梯度与Hessian矩阵。因此需要解决如何求一个复杂函数的导数问题,本文讲述的自动微分技术是解决此问题的一种通用方法。关于梯度、Hessian矩阵、雅克比矩阵,以及梯度下降法,牛顿法,拟牛顿法,各种反向传播算法的详细讲述可以阅读《机器学习与应用》,清华大学出版社,雷明著一书,或者SIGAI之前的公众号文章。对于这些内容,我们有非常清晰的讲述和推导。

简介

自动微分(Automatic Differentiation,简称AD)也称自动求导,算法能够计算可导函数在某点处的导数值的计算,是反向传播算法的一般化。自动微分要解决的核心问题是计算复杂函数,通常是多层复合函数在某一点处的导数,梯度,以及Hessian矩阵值。它对用户屏蔽了繁琐的求导细节和过程。目前知名的深度学习开源库均提供了自动微分的功能,包括TensorFlow、pytorch等。

对于编程计算目标函数的导数值,目前有4种方法:手动微分,数值微分,符号微分,以及自动微分,在接下来会分别进行介绍。

自动微分在深度学习库中的地位

自动微分技术在深度学习库中处于重要地位,是整个训练算法的核心组件之一。一个典型的深度学习库架构如下图所示。在这里忽略了网络通信,本地IO等模块。

梯度计算一般使用本文所讲述的自动微分技术,计算出梯度值给优化器使用,用于训练阶段。如果使用标准的梯度下降法进行迭代,在第k次迭代时的计算公式为

在这里

为目标函数在当前点

处的梯度值,该值的计算是自动微分所要解决的问题。

计算图

开源深度学习库Tensorflow通过计算图(Computational Graph)表述计算流程。学过数据结构的同学都不会对图的概念陌生。Tensorflow中的每一个数据都是计算图上的一个节点,节点之间的边描述了数据之间的计算即流向关系。下面是一个典型的计算图。

该图所表示的运算为

其中节点v1,v2为表示中间结果或最终结果的变量。在后面的讲述中,将会以计算图作为工具。

手动微分

手动微分的做法是先人工推导目标函数对自变量的导数计算公式,然后编程实现。这种方法费时费力,容易出错。对于每一个目标函数都需要手工进行推导,因此通用性和灵活性差。早期的神经网络库如OpenCV和Caffe采用了这种方法。

数值微分

数值微分(Numerical Differentiation)属数值计算方法,它计算导数的近似值,通常用差分作为近似。只需要给出函数值以及自变量的差值,数值微分算法就可计算出导数值。单侧差分公式根据导数的定义直接近似计算某一点处的导数值。对于一元函数,根据导数的定义,前向差分公式为

其中h为接近于0的正数,如0.00001。更准确的是中心差分(center difference approximation)公式

它比单侧差分公式有更小的误差和更好的稳定性。数值微分会导致误差,即使对于很小的h,也会有截断误差(即使用近似所带来的误差)。

对于多元函数,变量xi的中心差分公式为

按照上面的公式,对每个自变量求偏导时都需要两次计算函数值,因此有计算量的问题。数值微分通常只用于检验其他算法的结果的正确性,例如在实现反向传播算法的时候用数值微分算法检验反向传播算法所求导数的正确性。

符号微分

符号微分(Symbolic Differentiation)属符号计算的范畴,其计算结果是导函数的表达式。符号计算用于求解数学中的公式解(也称解析解),得到解的表达式而非具体的数值。

根据基本函数的求导公式以及四则运算、复合函数的求导法则,符号微分算法可以得到任意可微函数的导数表达式,与人工计算的过程类似。

以下面的函数为例

根据第1.3节介绍的求导公式,符号计算得到的对x的偏导数为

然后将自变量的值代入导数公式,得到任意点处的导数值。符号微分计算出的表达式需要用字符串或其他数据结构存储,如表达式树。数学软件如Mathematica,Maple,matlab中实现了这种技术。python语言的符号计算库也提供了这类算法。

对于深层复合函数,如神经网络的映射函数,符号微分算法得到的导数计算公式将会非常冗长。称为表达式膨胀(expression swell)。对于机器学习中的应用,不需要得到导数的表达式,而只需计算函数在某一点处的导数值。因此存在计算上的冗余且成本高昂。

以下面的函数为例

如果采用符号微分算法,当n=1,2,3,4时的ln及其导数如下表所示。

自动微分

自动微分是介于符号微分和数值微分之间的一种方法:数值微分一开始就代入数值近似求解;符号微分直接对表达式进行推导,最后才代入自变量的值得到最终解。自动微分将符号微分应用于最基本的运算(或称原子操作),如常数,幂函数,指数函数,对数函数,三角函数等基本函数,代入自变量的值得到其导数值,作为中间结果进行保留。然后再根据这些基本运算单元的求导结果计算出整个函数的导数值。

自动微分的灵活强,可实现完全向用户隐藏求导过程,由于它只对基本函数或常数运用符号微分法则,因此可以灵活地结合编程语言的循环、分支等结构,根据链式法则,借助于计算图计算出任意复杂函数的导数值。由于存在上述优点,该方法在现代深度学习库中得到广泛使用。自动微分在实现时有前向模式和反向模式两种实现方案,下面分别进行介绍。

前向模式

前向模式从计算图的起点开始,沿着计算图边的方向依次向前计算,直到到达计算图的终点。它根据自变量的值计算出计算图中每个节点的值vi以及导数值vi',并保留中间结果。直到得到整个函数的值和其导数值。整个过程对应于一元复合函数求导时从最内层逐步向外层求导。

以下面的函数函数为例,要计算其对x1的偏导数

首先将函数转化为下图所示的计算图,然后根据计算图计算出每个节点的函数值以及导数值。

计算过程如下表所示,自变量也被转化成了计算图的节点,其下标从0开始向负数进行编号,以与中间结果节点进行区分。第一列为每个节点的函数值以及计算过程,第二列为每个节点对的偏导数值以及计算过程。按照计算图中的节点编号,依次根据前面的节点计算出后续节点的函数值和导数值。在这里vi'表示vix1的偏导数。

以节点v2为例,它依赖于节点v-1v0,且

因此根据这两个前驱节点的值可以计算出v2的值

同时还需要计算其导数值,根据乘法的求导公式有

每一步的求导都利用了更早步的求导结果,因此消除了重复计算,不会产生符号微分的表达式膨胀问题。自动微分的前向模式实际上与我们在微积分里所学的求导过程一致。

前向传播算法每次只能计算对一个自变量的偏导数,对于一元函数求导是高效的。对于实数到向量的映射,即n个一元函数

同样只运行一次前向算法即可同时计算出每个函数对输入变量的导数值。对于向量到向量的映射函数

即m个n元函数,则需要运行n此前向算法才能求得对每个输入变量的偏导数。对于神经网络,一般有n>>m,用前向算法会低效。

反向模式

反向模式是反向传播算法的一般化,其思路是根据计算图从后向前计算,依次得到对每个中间变量节点的偏导数,直到到达自变量节点处。在每个节点处,根据该节点的后续节点计算其导数值。整个过程对应于多元复合函数求导时从最外层逐步向内侧求导。

对于上一节的问题,反向模式的计算过程如下表所示。在这里均指对的偏导数,与上一个表的含义不同。

表的第一列为前向计算函数值的过程,与前向计算时相同。第二列为反向计算导数值的过程。第1步计算y对v5的导数值,由于y=v5,因此有

第2步计算y对v4的导数值,v4只有一个后续节点v5v5=v4-v3,根据链式法则有

第3步计算y对v3的导数值,v3也只有一个后续节点v5v5=v4-v3,根据链式法则有

第4步计算y对v2的导数值,v2只有一个后续节点v4,根据链式法则有

第5步计算y对v1的导数值,v1只有一个后续节点v4,根据链式法则有

第6步计算y对v0的导数值,v0有2个后续节点v2v3,且

,根据链式法则有

第7步计算y对v-1的导数值,v-1有2个后续节点v1v2,且

,根据链式法则有

对于某一个节点vi,假设它在计算图中有k个直接后续节点vn1,...vnk,则根据链式法则有

因此在反向计算时需要寻找它所有的后续节点,收集这些节点的导数值

,然后计算本节点的导数值。整个计算过程中不仅利用了每个节点的后续节点的导数值,还需要利用某些节点的函数值以计算

,因此需要在前向计算时保存所有节点的值,供反向计算使用,不必重复计算。

如果要同时计算多个变量的偏导数,则可以借助雅克比矩阵完成。假设有节点x1,...xm,简写为向量x。每个节点都有直接后续节点y1,...y2,简写为向量y。这对应于如下映射函数

根据机器学习与应用》一书中已经推导的结果,有

其中

为雅克比矩阵。即通过雅克比矩阵转置与后续节点梯度值的乘积,可以得到当前节点的梯度值。

本文为SIGAI原创

如需转载,欢迎发消息到本订阅号

本文分享自微信公众号 - SIGAI(SIGAICN),作者:技术文章

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-06-21

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 理解神经网络的激活函数

    激活函数在神经网络中具有重要的地位,对于常用的函数如sigmoid,tanh,ReLU,不少读者都已经非常熟悉。但是你是否曾想过这几个问题:

    SIGAI学习与实践平台
  • 理解计算:从根号2到AlphaGo 第5季 导数的前世今生

    这段外表看起来有点像区块链地址(16进制地址)的乱码,第一次让接近神的牛顿爵士不得不以一种密码学的方式声明他对另一项重要研究的首发权,而这一次,他的对手则是当时...

    SIGAI学习与实践平台
  • 神经网络的激活函数总结

    激活函数在神经网络中具有重要的地位。在SIGAI之前的公众号文章“理解神经网络的激活函数”中,我们回答了3个关键的问题:

    SIGAI学习与实践平台
  • 机器学习中常用的5种回归损失函数,你都用过吗?

    “损失函数”是机器学习优化中至关重要的一部分。L1、L2损失函数相信大多数人都早已不陌生。那你了解Huber损失、Log-Cosh损失、以及常用于计算预测区间的...

    石晓文
  • 机器学习中常用的5种回归损失函数,你都用过吗?

    “损失函数”是机器学习优化中至关重要的一部分。L1、L2损失函数相信大多数人都早已不陌生。那你了解Huber损失、Log-Cosh损失、以及常用于计算预测区间的...

    1480
  • 机器学习大牛最常用的5个回归损失函数,你知道几个?

    大数据文摘
  • matlab常用函数

    感谢大家关注matlab爱好者,今天给大家介绍一下matlab编程中常用的函数。在聊天栏中回复“005”或“函数”即可快速获取本视频。在腾讯视频搜索“matla...

    艾木樨
  • 原 PostgreSQL的系统函数分析记录

    王果壳
  • Go 语言基础入门教程 —— 函数篇:函数的基本定义和调用

    几乎所有编程语言都支持函数,编写函数的目的在于将复杂的问题分解为一系列简单的任务来处理,此外同一个函数还可以被多次复用,这一结构在面向过程的函数式编程中至关重要...

    学院君
  • 理解神经网络的激活函数

    激活函数在神经网络中具有重要的地位,对于常用的函数如sigmoid,tanh,ReLU,不少读者都已经非常熟悉。但是你是否曾想过这几个问题:

    SIGAI学习与实践平台

扫码关注云+社区

领取腾讯云代金券