1. 程式人生 > >CS231n(四)Back Propagation Notes

CS231n(四)Back Propagation Notes

反向傳播是利用鏈式法則遞迴計算表示式的梯度的方法。

導數的意義:函式變數在某個點周圍的極小區域內變化,而導數就是變數變化導致的函式在該方向上的變化率

\frac{df(x)}{dx}= lim_{h\to 0}\frac{f(x+h)-f(x)}{h}

函式關於每個變數的導數指明瞭整個表示式對於該變數的敏感程度。

使用鏈式法則計算複合表示式

知識回顧

複合函式,比如f(x,y,z)=(x+y)z。雖然這個表達足夠簡單,可以直接微分,但是在此使用一種有助於直觀理解反向傳播的方法。將公式分成兩部分:q=x+yf=qz

因為fqz相乘,所以\displaystyle\frac{\partial f}{\partial q}=z,\frac{\partial f}{\partial z}=q,又因為qx+y,所以\displaystyle\frac{\partial q}{\partial x}=1,\frac{\partial q}{\partial y}=1。然而,並不需要關心中間量q的梯度,因為\frac{\partial f}{\partial q}沒有用。相反,函式f關於x,y,z的梯度才是需要關注的。鏈式法則指出將這些梯度表示式連結起來的正確方式是相乘,比如\displaystyle\frac{\partial f}{\partial x}=\frac{\partial f}{\partial q}\frac{\partial q}{\partial x}。 

下圖是視覺化計算過程

前向傳播從輸入計算到輸出(綠色),反向傳播從尾部開始,根據鏈式法則遞迴地向前計算梯度(顯示為紅色),一直到網路的輸入端。可以認為,梯度是從計算鏈路中迴流。

每個門單元(圖中的圓圈)都會得到一些輸入並立即計算兩個東西:1. 這個門的輸出值   2.其輸出值關於輸入值的區域性梯度。

一旦前向傳播完畢,在反向傳播的過程中,門單元門將最終獲得整個網路的最終輸出值在自己的輸出值上的梯度。

門單元應該將回傳的梯度乘以它對其的輸入的區域性梯度,從而得到整個網路的輸出對該門單元的每個輸入值的梯度。

以上圖為例子:+這個門單元為例子。它獲得*門單元傳來的梯度1,它自區域性梯度為df/dq=z=-4,所以它的梯度為-4*1=-4。

除了上文介紹的加法門,乘法門,取最大值門,還有下面這4種:


\displaystyle f(x)=\frac{1}{x} \to \frac{df}{dx}=-1/x^2
\displaystyle f_c(x)=c+x \to \frac{df}{dx}=1\displaystyle f(x)=e^x \to \frac{df}{dx}=e^x
\displaystyle f_a(x)=ax \to \frac{df}{dx}=a

舉個實際的例子:

求x和w的梯度。

這裡有個小技巧:

\displaystyle\sigma(x)=\frac{1}{1+e^{-x}}\displaystyle\to\frac{d\sigma(x)}{dx}=\frac{e^{-x}}{(1+e^{-x})^2}=(\frac{1+e^{-x}-1}{1+e^{-x}})(\frac{1}{1+e^{-x}})=(1-\sigma(x))\sigma(x)

所以我們令t=w0x0+w1x1+w2所以df/dx=df/dt*dt/dx

w = [2,-3,-3] # 假設一些隨機資料和權重
x = [-1, -2]

# 前向傳播
t = w[0]*x[0] + w[1]*x[1] + w[2]
f = 1.0 / (1 + math.exp(-t)) # sigmoid函式

# 對神經元反向傳播
dt = (1 - f) * f # 點積變數的梯度, 使用sigmoid函式求導
dx = [w[0] * dt, w[1] * dt] # 回傳到x
dw = [x[0] * dt, x[1] * dt, 1.0 * dt] # 回傳到w
# 完成!得到輸入的梯度

分段反向傳播上面的程式碼展示了在實際操作中,為了使反向傳播過程更加簡潔,把向前傳播分成不同的階段將是很有幫助的。比如我們建立了一箇中間變數t,它裝著wx的點乘結果。在反向傳播的時,就可以(反向地)計算出裝著wx等的梯度的對應的變數(比如dtdxdw)。

再舉個栗子

前向傳播:

sigy = 1.0 / (1 + math.exp(-y))

num = x + sigy  分子

sigx = 1.0 / (1 + math.exp(-x))

xpy = x + y

xpysqr = xpy**2

den = sigx + xpysqr  分母

invden = 1.0 / den 

f = num * invden

反向傳播:

dnum = invden 分子的梯度

dinvden = num

dden = (-1.0 / (den**2)) * dinvden 分母的梯度

dxpysqr = (1) * dden

dsigx = (1) * dden

dxpy = (2 * xpy) * dxpysqr

dx = (1) * dxpy 

dy = (1) * dxpy

dx += ((1 - sigx) * sigx) * dsigx  回傳 sigx = 1.0 / (1 + math.exp(-x))

dx += (1) * dnum   回傳 num = x + sigy

dx += (1) * dnum 

dsigy = (1) * dnum

dy += ((1 - sigy) * sigy) * dsigy

在不同分支的梯度要相加:如果變數x,y在前向傳播的表示式中出現多次,那麼進行反向傳播的時候就要非常小心,使用+=而不是=來累計這些變數的梯度(不然就會造成覆寫)。

總結:

討論了分段計算在反向傳播的實現中的重要性。應該將函式分成不同的模組,這樣計算區域性梯度相對容易,然後基於鏈式法則將其“鏈”起來。重要的是,不需要把這些表示式寫在紙上然後演算它的完整求導公式,因為實際上並不需要關於輸入變數的梯度的數學公式。只需要將表示式分成不同的可以求導的模組(模組可以是矩陣向量的乘法操作,或者取最大值操作,或者加法操作等),然後在反向傳播中一步一步地計算梯度。