自动微分及Tensorflow实现

  几乎所有机器学习算法在训练或预测时都可以归结为最优化问题的求解,如果目标函数可导,该问题转换为训练函数驻点求解问题。通常情况下无法得到驻点的解析解,因此只能采用数值优化算法,如梯度下降法,牛顿法,拟牛顿法。这些数值优化算法都依赖于函数的一阶导数值或二阶导数值,包括梯度与Hessian矩阵。因此需要解决如何求一个复杂函数的导数问题,本文讲述的自动微分技术是解决此问题的一种通用方法,目前基本所有主流的深度学习框架都支持自动微分,如Tensorflo,Pytorch等。

  在介绍自动微分之前,先介绍一下目前使用编程计算函数导数值的几种方法:

  • 手动微分

      手动微分,顾名思义就是在使用程序计算导函数值之前,手动计算目标函数的导函数,对于表达式比较复杂的函数,这显然是比较容易出错的,并且通用性和灵活性都较差,现在较少使用。

  • 符号微分

      符号微分(Symbolic Differentiation)属于符号计算的范畴,基于初等函数的求导公式和四则运算法则及链式法则搭建在计算搭建求导系统求出导函数再代入变量,即可求出某点的导数值。显然,只是将人工计算导数的过程进行程序化。符号微分计算出的表达式需要用字符串或其他数据结构存储,如表达式树。常用数学软件Mathematica,Maple,Matlab都使用了这种技术。对于深层复合函数,如神经网络的映射函数,符号微分算法得到的导数计算公式将会非常冗长。称为表达式膨胀(expression swell)。对于机器学习中的应用,不需要得到导数的表达式,而只需计算函数在某一点处的导数值。因此存在计算上的冗余且成本高昂。

      以深度学习中最为常见的Sigmoid激活函数为例:,则有,神经网络本质上他是一个函数逼近器,理论上可以以任意精度逼近任何函数。对于一个含有三个隐层的网络,假设三个隐层的激活函数都是Sigmoid激活函数,网络的输入为,输入对应标签为y,输出为,则有,损失函数,将的函数表达式带入上式由链式法则即可计算出导函数,显然,因为可以预见的是,这个导函数一定不是一个形式比较简洁的函数,在此就不展开计算,有兴趣的读者可以自行验证,而这也就是上文所说的表达式膨胀。当然,可能有读者会觉得,因为Sigmoid函数的表达式本身就不太简洁,复合之后的表达式肯定会更为复杂,如果激活函数为三角函数和幂函数这类函数,其导函数还是较为简洁的。那为什么不能使用这类函数作为激活函数呢,原因有二,首先,一般来说三角函数和幂函数这类函数是不能用于网络的激活函数的(这一点目前仍有争议,不过在实际应用中基本不会使用非单调非有界的函数作为单调函数,这里有读者肯定以Google的新型激活函数Swish反驳,但是不可否认的是,Swish目前仍有很大争议并且被使用的较少),这涉及到深度学习激活函数的深入讨论,这里只简单提及一点:三角函数不是单调函数,幂函数不是有界函数。其次,对于一些有成千上万隐层的大型神经网络,因为进行了成千上万次的迭代,其导函数必然会出现表达式膨胀(需要注意的是,隐层的激活函数一般不可能取同一个函数)。

    Swish是谷歌大脑团队提出的一种新型激活方法数:,并通过实验表明其在绝大多数环境中可以替代当前比较流行的 ReLU 函数。不过在 Reddit 论坛上,该激活函数的性能与优点还是有些争议的,有的开发者发现该激活函数很多情况下可以比标准的 ReLU 获得更高的性能,而有些开发者则认为 Swish 激活函数并没有什么新意,我们应该关注于更加基础的研究。

  • 数值微分

      符号微分用于求解数学中的公式解(也称解析解),得到解的表达式而非具体的数值。在实际应用中还需进一步代入数值得到数值解,需要经过一次转换。那么,有没有一种方法能直接求出目标函数的导数,而不是求出导函数之后再带入数值得到导数。这种方法就是数值微分,我们先来看一下导数的定义:

    当足够小时,可将该点的导数可以近似为,在实际计算时,一般使用它的变式,有着更小的误差和更好的稳定性。当然,由于进行了近似操作,得到的导数值肯定是有误差的,所幸误差还在能接受的范围内。按上面推导的式子计算导数,对每个自变量求导时都需要计算两次函数值,因此也会导致计算量的问题。所以,数值微分通常只用于检验其他算法的结果的正确性,例如在实现反向传播算法的时候用数值微分算法检验反向传播算法所求导数的正确性。

  • 自动微分

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

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

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

  • Tensorflow使用自动微分搭建模型

    待续

你可能感兴趣的:(自动微分及Tensorflow实现)