自動微分方法(auto diff)


學習機器學習的同學在學習過程中會經常遇到一個問題,那就是對目標函數進行求微分,線性回歸這類簡單的就不說、復雜的如神經網絡類那些求導過程的酸爽。像我還是那種比較粗心的人往往有十九錯,所以說自動求導就十分有必要了,本文主要介紹幾種求導的方式。假設我們的函數為\(f(x,y)=x^2y+y+2\),目標是求出偏導\(\frac{\partial{f}}{\partial{x}}\)\(\frac{\partial{f}}{\partial{y}}\)。求導的方式主要分為以下幾種

手動求導法(Manual Differentiation)###

首先准備一張紙和一支筆,根據上學時候學到的求導法則,開始計算。最終得到的結果
\(\frac{\partial{f}}{\partial{x}}=2xy\)
\(\frac{\partial{f}}{\partial{y}}=x^2+1\)
上面這個小例子比較簡單,口算即可得到答案,但如果方程比較復雜那就難說了。幸好有自動求導的方法,例如符號求導方法。

符號求解法(Symbolic Differentiation)###

符號求導是根據一些求導法則,進行求導。例如我們大學高數學習的\((uv)\prime=u'v+v'u\)\((u+v)'=u'+v'\)\((\frac{u}{v})'=\frac{u'v-v'u}{v^2}\)等等,下圖是\(g(x,y)=5+xy\)的符號求導工作流程。

原公式在圖的左邊,求導公式在圖的右半部分,求導的過程是先求葉子節點,自下向上。最終對節點進行見之得到求導結果\(\frac{\partial{g}}{\partial{x}}=y\),這個例子固然簡單,但是對於一個更復雜的公式,那么求導符號圖將會十分的龐大(表達式膨脹),另外對於一些變化的公式(硬代碼)這種方法就無能為力了:

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

數值求導法(Numerical Differentiation)###

導數的定義是當自變量的增量趨於零時,因變量的增量與自變量的增量之商的極限。

其中\(\varepsilon\)是一個無窮小的數,所以我們可以計算在x=3,y=4這一點對x的偏導數,\(f'(x=3,y)=\frac{f(3+\varepsilon,4)-f(3,4)}{\varepsilon}\),對應的代碼如下:

def f(x, y):
    return x**2*y + y + 2 
def derivative(f, x, y, x_eps, y_eps):
    return (f(x + x_eps, y + y_eps) - f(x, y)) / (x_eps + y_eps) 
df_dx = derivative(f, 3, 4, 0.00001, 0)
df_dy = derivative(f, 3, 4, 0, 0.00001) 
>>print(df_dx) 
24.000039999805264 
>>print(df_dy)  
10.000000000331966 

通過上面的結果我們發現,得出的結果不是十分的精確,並且在對x和y求偏導的整個過程中,至少需要計算3次\(f()\),也就是說如果有1000個參數,那么至少需要計算1001次\(f()\),在神經網絡中,參數巨多,這樣計算效率會比較差。不過這種方法常被用到進行檢驗得到的求導結果是否正確。

前向自動求導法(Forward-Mode Autodiff)###

前向求導是依賴於數值求導和符號求導的一種求解方法,給定公式\(a+{\varepsilon}b\),這種被稱作dual number,其中ab是實數,\(\varepsilon\)是一個無窮小的數,並且\({\varepsilon}^2=0\),舉個栗子,\(42 + 24\varepsilon\),我們可以把它看成42.00000000...24的數值.我們可以通過這種方法(42.0,24.0)來表示,dual number滿足以下的運算法則:

  • 1.\(\lambda(a+b\varepsilon) = a\varepsilon + b{\lambda}\varepsilon\)
  • 2.\((a+b\varepsilon)+(c+d\varepsilon) = (a+c)+(b+d)\varepsilon\)
  • 3.\((a+b\varepsilon)\times(c+d\varepsilon) = ac+(ad+bc)\varepsilon+(bd){\varepsilon}^2=ac+(ad+bc)\varepsilon\)

還有一點就是\(h(a+b\varepsilon)=h(a)+b{\times}h'(a)\varepsilon\)

上圖給出了使用前向求導方法計算出\(f(x,y)\)在x=3,y=4這一點\(\frac{\partial{f}}{\partial{x}}(3,4)\)的偏導,同理求出\(\frac{\partial{f}}{\partial{y}}(3,4)\),圖中的思路很清晰就不贅述。前向求導方法相對數值求導來說准確率較高,當和數值求導方法一樣如果參數過多的時候效率會比較差,因為這種方法需要遍歷整個圖。

逆向自動求導法(Reverse-Mode Atuodiff)###

TensorFlow采用的是逆向自動求導方法,該方法首先正向遍歷整個圖,計算出每個節點的值;然后逆向(從上到下)遍歷整個圖,計算出節點的偏導值,步驟如下圖所示;節點內藍色的數值表示正向計算出的結果,為了方便表達,我們從下到上,從左到右依次標注為\(n_1\)\(n_7\),可以看到最后的值\(n_7\)(頂部節點)為42。

在逆向求導過程中使用鏈式求導方法:
\(\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\),
接着向下計算\(n_5\)\(\frac{\partial{f}}{\partial{n_5}}=\frac{\partial{f}}{\partial{n_7}}{\times}\frac{\partial{n_7}}{\partial{n_5}}\),上一步計算出\(\frac{\partial{f}}{\partial{n_7}}=1\),現在我們只需要計算\(\frac{\partial{n_7}}{\partial{n_5}}\),從圖中我們知道\(n_7=n_5+n_6\),可以得出\(\frac{\partial{f}}{\partial{x}}=1\)。所以\(\frac{\partial{f}}{\partial{n_5}}=1\),接下來的步驟可以看上面的圖,這里就不贅述了。
逆向自動求導法這種方法十分強大且准確率較高,尤其是有卻多的輸入。這種發方法僅需要正向和逆向遍歷一次即可,這種方法更強大的地方在於能夠解決符號求解法硬代碼的問題。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM