自动微分方法(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,其中a和b是實數,(varepsilon)是一個無窮小的數,并且({varepsilon}^2=0),舉個栗子,(42 + 24varepsilon),我們可以把它看成42.00000000...24的數值.我們可以通過這種方法(42.0,24.0)來表示,dual number滿足以下的運算法則:
1.(lambda(a+bvarepsilon) = avarepsilon + b{lambda}varepsilon)
2.((a+bvarepsilon)+(c+dvarepsilon) = (a+c)+(b+d)varepsilon)
3.((a+bvarepsilon) imes(c+dvarepsilon) = ac+(ad+bc)varepsilon+(bd){varepsilon}^2=ac+(ad+bc)varepsilon)
還有一點就是(h(a+bvarepsilon)=h(a)+b{ imes}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}}{ imes}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}}{ imes}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),接下來的步驟可以看上面的圖,這里就不贅述了。
逆向自動求導法這種方法十分強大且準確率較高,尤其是有卻多的輸入。這種發方法僅需要正向和逆向遍歷一次即可,這種方法更強大的地方在于能夠解決符號求解法中硬代碼的問題。
總結
以上是生活随笔為你收集整理的自动微分方法(auto diff)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 微信小程序走出国门,国际化将指日可待?
- 下一篇: SpringBoot集成Thymelea