标签归档:自动微分

python中几种自动微分库

  简单介绍下python的几个自动求导工具,tangent、autograd、sympy;
  在各种机器学习、深度学习框架中都包含了自动微分,微分主要有这么四种:手动微分法、数值微分法、符号微分法、自动微分法,这里分别简单走马观花(hello world式)的介绍下下面几种微分框架;

sympy 强大的科学计算库,使用的是符号微分,通过生成符号表达式进行求导;求得的导数不一定为最简的,当函数较为复杂时所生成的表达式树异常复杂;

autograd自动微分先将符号微分用于基本的算子,带入数值并保存中间结果,后应用于整个函数;自动微分本质上就是图计算,容易做很多优化所以广泛应用于各种机器学习深度学习框架中;

tangent源到源(source-to-source)的自动微分框架,在计算函数f微分时他通过生成新函数f_grad来计算该函数的微分,与目前所存在的所有自动微分框架都有所不同;由于它是通过生成全新的函数来计算微分所以具有非常搞的可读性、可调式性这也是官方所说的与当前自动微分框架的重大不同;

sympy 求导

 def grad():
     # 定义表达式的变量名称
     x, y = symbols('x y')
     # 定义表达式
     z = x**2 +y**2
     # 计算z关于y对应的偏导数
     return diff(z, y)

 func = grad()

输出结果表达式z的导函数z‘=2*y

 print(func) 

把y 等于6 带入计算 结果 为12

 print(func.evalf(subs ={'y':3}))

Autograd求偏导

 import autograd.numpy as np
 from autograd import grad

 #表达式 f(x,y)=x^2+3xy+y^2
 #df/dx = 2x+3y
 #df/dy = 3x+2y
 #x=1,y=2
 #df/dx=8
 #df/dy=7
 def fun(x, y):
   z=x**2+3*x*y+y**2
   return z

 fun_grad = grad(fun)
 fun_grad(2.,1.)

输出:7.0

tangent求导

 import tangent
 def fun(x, y):
   z=x**2+3*x*y+y**2
   return z

默认为求z关于x的偏导数

 dy_dx = tangent.grad(fun)

输出偏导数值为 8 ,z’ = 2 * x,此处x传任何值都是一样的

 df(4, y=1)

可通过使用wrt参数指定求关于某个参数的偏导数,下面为求z关于y的偏导数

 df = tangent.grad(funs, wrt=([1]))

输出值为10 ,z’ = 2 *y,此处x传任何值都是一样的

 df(x=0, y=5)

上面说了那么多也没体现出tangent的核心:源到源(source-to-source)

在生成导函数的时候加入verbose=1参数,即可看到tangent为我们生成的用于计算导数的函数,默认情况下该值为0所以我们没感觉到tangent的求导与别的自动微分框架有什么区别;

 def df(x):
     z = x**2
     return z

 df = tangent.grad(df, verbose=1)
 df(x=2)

在执行完上述代码后,我们看到了tangent为我们所生成用于求导数的函数:

  def ddfdx(x, bz=1.0):
    z = x ** 2
    assert tangent.shapes_match(z, bz), 'Shape mismatch between return value (%s) and seed derivative (%s)' % (numpy.shape(z), numpy.shape(bz))
 # Grad of: z = x ** 2
  _bx = 2 * x * bz
  bx = _bx
  return bx

  ddfdx函数就是所生成的函数,从中我们也可以看到表达式z的导函数z’=2 * x,tangent就是通过执行该函数用于求得导数的;

  sympy 中的自动微分只是它强大的功能之一,autograd 从名字也可知它就是为了自动微分而生的,tangent初出茅庐2017年底Google才发布的自动微分方法也比较新颖,从17年发v0.1.8版本后也没见发版,源码更新也不够活跃;sympy、autograd比较成熟,tangent还有待观察;

微积分——自动微分

  梯度下降法(Gradient Descendent)是机器学习的核心算法之一,自动微分则是梯度下降法的核心;
  梯度下降法用于求损失函数的最优值,前面的文章中我们说过梯度下降是通过计算参数与损失函数的梯度并在梯度的方向不断迭代求得极值;但是在机器学习、深度学习中很多求导往往是很复杂的,手动使用链式法则求导很容易出错,借助于计算机也只能是硬编码;
  这时候就需要借助于自动微分了,求导主要有这么四种:手动微分法数值微分法符号微分法自动微分法,这里分别说说这几种求导方法;

手动微分法(Manual Differentiation)
  手动微分法需要我们手动编写出代价函数激活函数的求导代码,硬编码这些函数的求导方法,如果这些函数后面有调整该函数的求导方法又要重新实现,可以说是又麻烦又容易出错;
数值微分法(Numerical Differentiation)
  通过使用函数值来估计函数的导数,该方法主要是计算速度慢,精度差等问题;
符号微分法(Symbolic Differentiation)
  符号微分广泛用在各种数学软件中如Matlab、Octave等,它通关过使用符号表达式进行求导,符号微分是基于求导法则进行的;

  如表达式:f(x) = 2y + x^2
  表达式树为:
    enter image description here

  通过符号微分法求得:

  enter image description here

  符号微分有个缺陷就是得到的导数不一定是最简的,函数较为复杂时表达式树会很复杂,可能会出现表达式爆炸的情况出现;

自动微分法(Autodiff)

  自动微分法介于数值微分符号微分 之间,数值微分是直接代入数值近似求解而符号微分为直接通过表达式树对表达式进行求解;自动微分先将符号微分用于基本的算子,带入数值并保存中间结果,后应用于整个函数;自动微分本质上就是图计算,容易做很多优化所以广泛应用于各种机器学习深度学习框架中;
  自动微分又分为前向模式(Forward mode Autodiff)反向模式(Reverse-Mode Atuodiff)求导;

前向模式(Forward mode Autodiff)

  前向模式引入二元数(dual number),同时会先将表达式转换为计算图然后会依次从下往上计算每一步的导数,由于每步都使用了上一步的导数所以不会导致重复计算不会出现像符号微分一样的表达式膨胀问题,但由于深度学习的参数比较多所以前向模式的效率还是有些差;一个前向过程就可以求出其函数值与导数,下面简单举个例子:

  二元数:a+bꜫ
  a与b都是实数,ꜫ为无穷小的数,且ꜫ^2=0,并满足加分与乘法法则,且还有:
    enter image description here
  这样是要求出f( a+ꜫ)就可以得出f(a)与f(a)的导数;
  还是上面的表达式:f(x) = 2y + x^2

    enter image description here

  如上图当x=2,y=3时,我们可以得出:二元数 10+4ꜫ,即函数f(x)关于x的偏导数为:4,函数值为10;

反向模式(Reverse mode autodiff)

  反向模式为先通过正向遍历计算图求出每个节点的值,然后通过反向遍历整个图,计算出每个节点的偏导,其原理为微积分链式法则,这里所说的反向模式其实也就是我们在深度学习中所说的BP算法(反向传播算法),只需要一个前向传播、一个反向传播就可以求得所有参数的导数,所以性能很高,非常适用于深度学习中的自动求导;

          enter image description here

  上图为经过反向传播的计算图,根据链式法则:
enter image description here
enter image description here

  正如上面所说的,经过一次正向传播求出所有的节点值后再经过一次反向传播就求得了所有输入参数的导数效率很高,而且避免符号微分、数值微分所带来的问题;目前Tensoflow、MXNet等深度学习框架中也都使用了反向模式实现自动微分只是各种具体算法还是有不少差异;

参考资料:
https://blog.csdn.net/aws3217150/article/details/70214422
https://arxiv.org/pdf/1502.05767.pdf