在这一节里面,我们将带大家一起复习一下高等数学中的微分知识,同时向大家通过程序求微分的四种主要方法,使大家对自动微分在今后深度学习和人工智能中的重要性有一个感性的认识。
在程序中求导数和微分一般有四种方式:
1. 手动求微分:采用纯人工方式,与计算机无关,这种我们不进行讨论
2. 数值方法:这种方式利用导数的定义,直接求解微分值
3. 符号微分法:通过解析式找到函数导数的表达式,将其转化为计算机程序
4. 自动微分:采用类似有向图的计算来求解微分值
在这里我们以一个较为复杂的多项式函数为例,来说明三种计算方式的不同。函数定义为:
程序中可以定义为:
大家注意到,我们在第1行使用了TensorFlow的一个特殊标记,是告诉TensorFlow这个是我们自定义的导数,将来TensorFlow求导时会自动调用这个函数。这个函数的作用就是求出函数的值和导数的值。具体到本例来说,就是求x的n次方的导数,结果是n乘以x的n-1次方。还需要注意的一点是,我们在第3行使用tf.pow来求x的n次方,这是因为TensorFlow Eager Execution API不能处理诸如math.pow这类函数的导数,因此需要我们定义新的函数,告诉TensorFlow怎样求导,所以我们定义了f3函数,并定义了其导数的计算方法。最后一点提醒大家注意的是如第6行的v.numpy(),这是TensorFlow新版中通过Eager Execution计算的结果是一个张量,通过numpy()可以转换为numpy对象,便于我们程序作进一步处理。
下面我们分别来看,使用前文提到的四种方法,怎样求出这个函数的导数。
- 数值方法
我们知道在高等数学中,对函数y=f(x)的导数定义为:
根据这个定义,我们可以求任意复杂函数的导数,但是对于复杂的尤其是多维函数,由于计算机数字表示的有限精度问题,这种方式存在运算量大而且精度不高的问题,通常只用于验证我们用其他方法求出的微分值的正确性。
在第2行我们定义delta_x(分子)的值为0.0001,我们知道根据导数定义,该值越小越精确,但是考虑到计算机浮点数表示的精度问题,通常不能取得太小,取一个足够小的值就可以了。
- 符号微分法
这种方法的核心是通过人工求出函数的微分的解析式,然后编制为程序,最后通过程序来计算函数的微分值。这种方式的精度最高,但是通常函数微分解析式非常复杂,求解过程中极易出错,同时这种方式仅适合可以写出解析式的情况,对于隐式方程形式,就无法求解了。
我们可以求出导数的解析表达式为:
具体的程序实现为:
- 自动微分
下面我们来用自动微分方法来求解这个问题。首先我们画出这个问题对应的计算图,如下图所示:
根据函数的递归定义:
所以l2的导数为:
同理对l3的导数为:
同理对l4的导数为:
根据上面的计算图,我们采用普通的Python程序即可计算出导数值:
如果采用TensorFlow Eager Execution API来进行计算,代码如下所示:
我们采用如下代码来调用这些微分方法:
运行结果如下所示:
由此可以看出,采用符号微分、纯Python自动微分、TensorFlow Eager Execution API求出的结果是一致的,都是比较精确的结果,而数值微分的结果会有一定的误差。
大家知道,当前流行的框架如TensorFlow是计算图模型,跟我们这里的程序是有一些差别的,在下一节中,我们将向大家介绍在计算图中的自动微分。