2.6 使用计算图求导数
这节将讲述如何利用计算图计算出函数J的导数,用到的公式如下:
流程图如图2-15所示。
图2-15 流程计算图
假设要计算,那要怎么算呢?把这个v值拿过来,改变一下,那么J的值会怎么变呢?定义J=3v,现在v=11,如果让v增加一点,比如到11.001,那么J=3v=33.003,即v增加了0.001,最终结果是J上升到原来的3倍,所以。因为对于任何v的增量,J都会有3倍增量,f(a)=3a,然后推导出,所以有J=3v,,这里J扮演f的角色。
如果想计算最后输出变量的导数,则使用v的导数,那么就做完了一步反向传播,在这个流程图中是一个反向步。
来看另一个例子,是多少呢?变量a=5,让它增加到5.001,那么对v的影响就是a+u,之前v=11,现在变成11.001,J就变成33.003,所以看到的是如果a增加0.001,J就增加0.003。如果把这个5换成某个新值,那么a的改变量就会传播到流程图的最右,所以J的增量是3乘以a的增量,意味着这个导数是3。
解释这个计算过程,如果改变了a,那么也会改变v,通过改变v,也会改变J,所以当提升这个值(0.001),即把a值提高一点点时,这就是J的净变化量(0.003)。
首先a增加了,v也会增加,那么v增加多少呢?这取决于,然后v的变化导致J也在增加,这在微积分里叫作链式法则。如果a影响到v,v影响到J,那么当a变大时,J的变化量就是v的变化量乘以改变v时J的变化量,在微积分里这叫链式法则。
如果让a增加0.001,则v也会变化相同的大小,所以。事实上,代入后得到,,,所以乘积3×1实际上就给出了正确答案,。
当编程实现反向传播时,通常会有一个最终输出值是要关心的,即最终的输出变量。在这种情况下最终的输出变量是J,即流程图里的最后一个符号,有很多计算尝试计算输出变量的导数,所以输出变量对某个变量的导数就用dvar命名。在很多计算中需要计算最终输出结果的导数,在本例中是J,还有各种中间变量,比如a、b、c、u、v,在python里实现时,变量可以用dJ_dvar,但因为一直对J求导,即对这个最终输出变量求导,所以这里介绍一个新符号,当用户在程序里编程时,就使用变量名dvar来表示那个量。
所以在程序里dvar表示导数,即关心的最终变量J的导数,有时最后是L对代码中各种中间量的导数,所以用dv表示这个值,dv=3,代码表示就是da=3。
继续计算导数,看这个值u,那么是多少呢?通过之前类似的计算,现在从u=6出发,如果令u增加到6.001,那么v之前是11,现在变成11.001,J从33变成33.003,所以J的增量是3倍,即。对u的分析类似于对a的分析,实际上这计算起来就是,如此可以算出,,最终结果是3×1=3。故还有一步反向传播,最终计算出du=3,这里的du就是。
那么呢?想象一下,如果改变了b的值,想要让它变化一点,让J值到达最大或最小,那么当稍微改变b值之后,导数,即这个J函数的斜率是什么呢?事实上,使用微积分链式法则,可以写成两者的乘积,就是,理由是如果改变b一点点,比如说变为3.001,那么它影响J的方式是:首先会影响u,u的定义是b·c,所以b=3时u=6,现在就成6.002了,因为例子中c=2,所以。当b增加0.001时,u变为原来的两倍。因此,现在u的增加量已经是b的两倍,那么等于3,让这两部分相乘,发现。
当计算所有这些导数时,最有效率的办法是从右向左计算,特别是当第一次计算对v的导数时,之后在计算对a的导数时就可以用到。对u的导数可以帮助计算对b的导数,然后再计算对c的导数。