1. 程式人生 > >附錄D——自動微分(Autodiff)

附錄D——自動微分(Autodiff)

cal war 意思 出了 三種 依賴 虛表 ever 我想

本文介紹了五種微分方式,最後兩種才是自動微分。

前兩種方法求出了原函數對應的導函數,後三種方法只是求出了某一點的導數。

假設原函數是$f(x,y) = x^2y + y +2$,需要求其偏導數$\frac{\partial f}{\partial x}$和$\frac{\partial f}{\partial y}$,以便應用於梯度下降等算法。

1、手工求導

該方法比較簡單,就是自備紙筆,應用基本的求導規則,以及鏈式求導法則,人工求導。缺點是對於復雜函數容易出錯。幸運的是,這一計算過程可由計算機幫我們完成,這就是符號微分。

2、符號微分(Symbolic Differentiation)

如圖D-1所示,使用符號微分的方法,計算函數$g(x,y) = 5 + xy$的偏導數。該圖左側代表函數$g(x,y)$,右側代表$g(x,y)$關於$x$的偏導數$\frac{\partial g}{\partial x} = 0 + (0 \times x + y \times 1) = y$(同樣的,可以求得$\frac{\partial g}{\partial y}$)。

技術分享圖片

圖D-1 符號微分

該算法首先求葉子節點關於$x$的偏導數,然後沿著樹向上,求得其他節點關於自變量的偏導數。這與手工求導所使用的規則是一樣的。

如果函數復雜,該算法生成的樹將十分龐大,性能不高。而且無法對很隨意的代碼求導,例如:

def my_func(a, b):
    z = 0
    for i in range(100):
        z = a * np.cos(z + i) + z * np.sin(b - i)
    return z 

3、數值微分(Numerical Differentiation)

這是根據導數的定義來求解的。函數$h(x)$在$x_0$點的導數為:

$h‘(x) = \lim_{\varepsilon \rightarrow 0} \frac{h(x_0 + \varepsilon) - h(x_0)}{\varepsilon}$

我們取一個很小的$\varepsilon$,帶入公式進行計算即可。該方法所得結果不夠精確,參數過多時計算量也比較大。但是計算起來很簡單,可用於校驗手工算出的導數是否正確。

如果有1000個參數,至少需要調用$h(x)$1001詞,來求得所有偏導數。

4、前向自動微分(Forward-Mode Autodiff)

該算法依賴一個對偶數(dual numbers,這讓我想起來oracle的虛表。難度dual可以表示虛無的意思?) $\varepsilon$,滿足$\varepsilon^2 = 0$但是$\varepsilon \neq 0$(姑且理解為一階無窮小吧)。

由於$\varepsilon$是無窮小,因此滿足$h(a + b \varepsilon) = h(a) + b \times h‘(a)\varepsilon$。因此,算出$h(a + \varepsilon) $可以同時得到$h(a)$和$h‘(a)$,如圖D-2所示。

技術分享圖片

圖D-2 前向自動微分

上圖值計算了$\frac{\partial f}{\partial x}(3,4)$,同樣的方法可以算的$\frac{\partial f}{\partial y}(3,4)$。

如果有1000個參數,需要遍歷上圖1000次,來求得所有偏導數。

5、反向自動微分(Reverse-Mode Autodiff)

這是TensorFlow所采用的自動微分算法。如圖D-3所示,該算法首先前向(也就是從輸入到輸出)計算每個節點的值,然後反向(從輸出到輸入)計算所有的偏導數。

技術分享圖片

圖D-3 反向自動微分

反向計算時應用鏈式求導法則:

$\frac{\partial f}{\partial x} = \frac{\partial f}{\partial n_i} \times \frac{\partial n_i}{\partial x}$

由於$n_7$就是輸出節點,$f = n_7$,因此$\frac{\partial f}{\partial n_7} = 1$。

該算法強大且精確,尤其是輸入很多,輸出很少時。假如函數有10個輸出(不管輸入是1千,2萬還是更多),求得所有偏導數需要對上圖遍歷11次。

各個算法比較:

技術分享圖片

附錄D——自動微分(Autodiff)