本文主要參考CS231N官方筆記,省略了一些基礎知識,同時做了擴展。
更好的閱讀體驗文末閱讀原文跳轉。
Introduction
下圖展示了反向傳播算法中,梯度的計算方法。其中,前向傳播從輸入計算到輸出(綠色),反向傳播從尾部開始,根據鏈式法則遞歸地向前計算梯度(顯示為紅色),一直到網絡的輸入端。
反向傳播可以看做是gate之間通過梯度信號相互通信,只要讓它們的輸入沿著梯度方向變化,無論它們自己的輸出值在何種程度上升或降低,都是為了讓整個網絡的輸出值更高(取決去梯度信號的正負)。
下圖是使用sigmoid激活函數的二維神經元的例子。輸入是[x0, x1],可學習的權重是[w0, w1, w2]。該神經元對輸入做點積,然後其激活數據被sigmoid函數縮放到[0,1]之間。
下式是sigmoid函數和求導公式(求導過程中先+1後-1的方法可以簡化導數):
代碼實現:
w = [2,-3,-3] # assume some random weights and datax = [-1, -2]# forward passdot = w[0]*x[0] + w[1]*x[1] + w[2]
f = 1.0 / (1 + math.exp(-dot)) # sigmoid function# backward pass through the neuron (backpropagation)ddot = (1 - f) * f # gradient on dot variable, using the sigmoid gradient derivationdx = [w[0] * ddot, w[1] * ddot] # backprop into xdw = [x[0] * ddot, x[1] * ddot, 1.0 * ddot] # backprop into w# we're done! we have the gradients on the inputs to the circuit
假設有以下形式的函數:
這個函數完全沒用,這裡只是用來作為實踐反向傳播的一個例子,需要強調的是,如果對 x 或 y 求微分,結果是一個複雜的表達式。然而如此複雜的運算並不必要,因為我們不需要一個明確的函數來計算梯度,只需知道如何使用反向傳播計算梯度即可。下面是構建前向傳播的代碼描述(計算下式中的dx時,代碼中的+=是梯度累加):
x = 3 # example valuesy = -4# forward passsigy = 1.0 / (1 + math.exp(-y)) # sigmoid in numerator #(1)num = x + sigy # numerator #(2)sigx = 1.0 / (1 + math.exp(-x)) # sigmoid in denominator #(3)xpy = x + y #(4)xpysqr = xpy**2 #(5)den = sigx + xpysqr # denominator #(6)invden = 1.0 / den #(7)f = num * invden # done! #(8)
# backprop f = num * invdendnum = invden # gradient on numerator #(8)dinvden = num #(8)# backprop invden = 1.0 / den dden = (-1.0 / (den**2)) * dinvden #(7)# backprop den = sigx + xpysqrdsigx = (1) * dden #(6)dxpysqr = (1) * dden #(6)# backprop xpysqr = xpy**2dxpy = (2 * xpy) * dxpysqr #(5)# backprop xpy = x + ydx = (1) * dxpy #(4)dy = (1) * dxpy #(4)# backprop sigx = 1.0 / (1 + math.exp(-x))dx += ((1 - sigx) * sigx) * dsigx # Notice += !! See notes below #(3)# backprop num = x + sigydx += (1) * dnum #(2)dsigy = (1) * dnum #(2)# backprop sigy = 1.0 / (1 + math.exp(-y))dy += ((1 - sigy) * sigy) * dsigy #(1)# done! phew
對前向傳播變量進行緩存:在計算反向傳播時,前向傳播過程中得到的一些中間變量非常有用。在實際操作中,最好代碼實現對於這些中間變量的緩存,這樣在反向傳播的時候也能用上它們。
在不同分支的梯度要相加:如果變量 x,y 在前向傳播的表達式中出現多次,那麼進行反向傳播的時候就要非常小心,使用+=而不是=來累計這些變量的梯度(不然就會造成覆寫)。這是遵循了在微積分中的多元鏈式法則(multivariable chain rule),該法則指出如果變量在線路中分支走向不同的部分,那麼梯度在回傳的時候,就應該進行累加。【This follows the multivariable chain rule in Calculus, which states that if a variable branches out to different parts of the circuit, then the gradients that flow back to it will add.】
Patterns in backward flow有趣的是,在許多情況下,可以直觀地解釋反向傳播中的梯度。例如,神經網絡中三個最常用的門(add,mul,max)在反向傳播期間的很容易解釋。
部分梯度求解(其實就是高數學的鏈式法則…注意max函數的求導規則):
以上各節只涉及單個變量,但是所有概念都可以擴展到矩陣和向量運算。需要注意的是維度和轉置操作。
矩陣-矩陣相乘梯度可能最棘手的運算:
# forward passW = np.random.randn(5, 10)
X = np.random.randn(10, 3)
D = W.dot(X)# now suppose we had the gradient on D from above in the circuitdD = np.random.randn(*D.shape) # same shape as DdW = dD.dot(X.T) #.T gives the transpose of the matrixdX = W.T.dot(dD)
以上代碼中各變量維度計算過程如下:
39頁PPT(不加注釋)
39頁PPT(加注釋)
重要的是向量乘法、矩陣乘法的求導。
向量、矩陣求偏導
計算過程如下:
4頁PDF
Derivatives, Backpropagation, and Vectorization7頁PDF
Efficient BackProp44頁PDF
Summary本文直觀地解釋了梯度的含義,梯度是如何在網絡中反向傳播的,以及它們是如何與網絡的不同部分通信並控制其升高或者降低,使得最終輸出值更高。
討論了分段計算在反向傳播的實現中的重要性。應該將函數分成不同的模塊,這樣計算局部梯度相對容易,然後基於鏈式法則將其「鏈」起來。重要的是,不需要把這些表達式寫在紙上然後演算它的完整求導公式,只需要將表達式分成不同的可以求導的模塊(模塊可以是矩陣向量乘法,取最大值,加法等),然後在反向傳播中一步一步地計算梯度。
ReferenceAutomatic differentiation in machine learning: a survey