神經網路反向傳播的數學原理

如果能二秒內在腦袋裡解出下面的問題,本文便結束了。

已知:J=(Xw-y)^T(Xw-y)=||Xw-y||^2,其中Xin R^{m 	imes n}, w in R^{n 	imes 1}, y in R^{m 	imes 1}

求:frac{partial J}{partial X} frac{partial J}{partial w} frac{partial J}{partial y}

到這裡,請耐心看完下面的公式推導,無需長久心裡建設。

首先,反向傳播的數學原理是「求導的鏈式法則」 :

fgx的可導函數,則(f circ g)

接下來介紹

  • 矩陣、向量求導的維數相容原則
  • 利用維數相容原則快速推導反向傳播
  • 編程實現前向傳播、反向傳播
  • 卷積神經網路的反向傳播

快速矩陣、向量求導

這一節展示如何使用鏈式法則、轉置、組合等技巧來快速完成對矩陣、向量的求導

一個原則維數相容,實質是多元微分基本知識,沒有在課本中找到下列內容,維數相容原則是我個人總結:

維數相容原則:通過前後換序、轉置 使求導結果滿足矩陣乘法且結果維數滿足下式:

如果xin R^{m	imes n} f(x)in R^1,那麼frac{partial f(x)}{partial x} in R^{m	imes n}

利用維數相容原則解上例:

step1:把所有參數當做實數來求導,J=(Xw-y)^2

依據鏈式法則有frac{partial J}{partial X}=2(Xw-y)wfrac{partial J}{partial w}=2(Xw-y)Xfrac{partial J}{partial y}=-2(Xw-y)

可以看出除了frac{partial J}{partial y}=-2(Xw-y)frac{partial J}{partial X}frac{partial J}{partial w}的求導結果在維數上連矩陣乘法都不能滿足。

step2:根據step1的求導結果,依據維數相容原則做調整:前後換序、轉置

依據維數相容原則frac{partial J}{partial X} in R^{m 	imes n},但frac{partial J}{partial X} in R^{m 	imes n} = 2(Xw-y)w(Xw-y)in R^{m 	imes 1}w in R^{n 	imes 1},自然得調整為frac{partial J}{partial X}=2(Xw-y)w^T

同理:frac{partial J}{partial w} in R^{n 	imes 1},但 frac{partial J}{partial w} in R^{n 	imes 1} = 2(Xw-y)X(Xw-y) in R^{m 	imes 1}X in R^{m 	imes n},那麼通過換序、轉置我們可以得到維數相容的結果2X^T(Xw-y)

對於矩陣、向量求導:

  • 「當做一維實數使用鏈式法則求導,然後做維數相容調整,使之符合矩陣乘法原則且維數相容」是快速準確的策略;

  • 「對單個元素求導、再整理成矩陣形式」這種方式整理是困難的、過程是緩慢的,結果是易出錯的(不信你試試)。

如何證明經過維數相容原則調整後的結果是正確的呢?直覺!簡單就是美...

快速反向傳播

神經網路的反向傳播求得「各層」參數Wb的導數,使用梯度下降(一階GD、SGD,二階LBFGS、共軛梯度等)優化目標函數。

接下來,展示不使用下標的記法(W_{ij}, b_iorb_j)直接對Wb求導,反向傳播是鏈式法則維數相容原則的完美體現,對每一層參數的求導利用上一層的中間結果完成。

這裡的標號,參考UFLDL教程 - Ufldl

前向傳播:

z^{(l+1)}=W^{(l)}a^{(l)}+b^{(l)} (公式1)

a^{(l+1)} =f(z^{(l+1)}) (公式2)

z^{(l)}為第l層的中間結果,a^{(l)}為第l層的激活值,其中第l+1層包含元素:輸入a^{(l)},參數W^{(l)}b^{(l)},激活函數f(),中間結果z^{(l+1)},輸出a^{(l+1)}

設神經網路的損失函數為J(W,b) in R^1(這裡不給出具體公式,可以是交叉熵、MSE等),根據鏈式法則有:

igtriangledown_{W^{(l)}}J(W,b)=frac{partial J(W,b)}{partial z^{(l+1)}} frac{partial z^{(l+1)}}{partial W^{(l)}}=delta ^{(l+1)}(a ^{(l)})^T igtriangledown_{b^{(l)}}J(W,b)=frac{partial J(W,b)}{partial z^{(l+1)}} frac{partial z^{(l+1)}}{partial b^{(l)}}=delta ^{(l+1)}

這裡記 frac{partial J(W,b)}{partial z^{(l+1)}}=delta ^{(l+1)},其中frac{partial z^{(l+1)}}{partial W^{(l)}}=a ^{(l)}frac{partial z^{(l+1)}}{partial b^{(l)}}= 1可由 公式1 得出,a ^{(l)}加轉置符號(a ^{(l)})^{T}是根據維數相容原則作出的調整。

如何求 delta ^{(l)}=frac{partial J(W,b)}{partial z^{(l)}}? 可使用如下遞推(需根據維數相容原則作出調整):

delta ^{(l)}=frac{partial J}{partial z^{(l)}}=frac{partial J}{partial z^{(l+1)}} frac{partial z^{(l+1)}}{partial a^{(l)}} frac{partial a^{(l)}}{partial z^{(l)}}= ((W^{(l)})^{T}delta ^{(l+1)}) cdot  f

其中frac{partial J}{partial z^{(l+1)}} frac{partial z^{(l+1)}}{partial a^{(l)}} = (W^{(l)})^T delta ^{(l+1)} frac{partial a^{(l)}}{partial z^{(l)}} = f

那麼我們可以從最頂層逐層往下,便可以遞推求得每一層的delta ^{(l)} = frac{partial J(W,b)}{partial z^{(l)}}

注意:frac{partial a^{(l)}}{partial z^{(l)}} = f是逐維求導,在公式中是點乘的形式。

反向傳播整個流程如下

1) 進行前向傳播計算,利用前向傳播公式,得到隱藏層和輸出層 的激活值。

2) 對輸出層(第l層),計算殘差:

delta ^{(l)} =frac{partial J(W,b)}{partial z^{(l)}}(不同損失函數,結果不同,這裡不給出具體形式)

3) 對於l-1, l-2 , ... , 2的隱藏層,計算:

delta ^{(l)}=frac{partial J}{partial z^{(l)}}=frac{partial J}{partial z^{(l+1)}} frac{partial z^{(l+1)}}{partial a^{(l)}}frac{partial a^{(l)}}{partial z^{(l)}}=((W^{(l)})^{T}delta ^{(l+1)}) cdot f

4) 計算各層參數W^{(l)}b^{(l)}偏導數:

igtriangledown_{W^{(l)}}J(W,b)=frac{partial J(W,b)}{partial z^{(l+1)}} frac{partial z^{(l+1)}}{partial W^{(l)}}=delta ^{(l+1)}(a ^{(l)})^T

igtriangledown_{b^{(l)}}J(W,b)=frac{partial J(W,b)}{partial z^{(l+1)}} frac{partial z^{(l+1)}}{partial b^{(l)}}=delta ^{(l+1)}

編程實現

大部分開源library(如:caffe,Kaldi/src/{nnet1,nnet2})的實現通常把W^{(l)}b^{(l)}作為一個layer,激活函數f()作為一個layer(如:sigmoid、relu、softplus、softmax)。

反向傳播時分清楚該層的輸入、輸出即能正確編程實現,如:

z^{(l+1)}=W^{(l)}a^{(l)}+b^{(l)} (公式1)

a^{(l+1)} =f(z^{(l+1)}) (公式2)

(1)式AffineTransform/FullConnected層,以下是偽代碼:

注: out_diff =  frac{partial J}{partial z^{(l+1)}} 是上一層(Softmax 或 Sigmoid/ReLU的 in_diff)已經求得:

in\_diff = frac{partial J}{partial a^{(l)}} = frac{partial J}{partial z^{(l+1)}} frac{partial z^{(l+1)}}{partial a^{(l)}} = W^T * out\_diff (公式 1-1)

W\_diff =frac{partial J}{partial z^{(l+1)}} frac{partial z^{(l+1)}}{partial W^{(l)}} = out\_diff * in^T (公式 1-2)

b\_diff =frac{partial J}{partial z^{(l+1)}} frac{partial z^{(l+1)}}{partial b^{(l)}} = out\_diff * 1 (公式 1-3)

(2)式激活函數層(以Sigmoid為例)

註:out_diff = frac{partial J}{partial a^{(l+1)}}是上一層AffineTransform的in_diff,已經求得,

in\_diff = frac{partial J}{partial z^{(l+1)}} = frac{partial J}{partial a^{(l+1)}} frac{partial a^{(l+1)}}{partial z^{(l+1)}} = out\_diff cdot out cdot (1-out)

在實際編程實現時,in、out可能是矩陣(通常以一行存儲一個輸入向量,矩陣的行數就是batch_size),那麼上面的C++代碼就要做出變化(改變前後順序、轉置,把函數參數的Vector換成Matrix,此時Matrix out_diff 每一行就要存儲對應一個Vector的diff,在update的時候要做這個batch的加和,這個加和可以通過矩陣相乘out_diff*input(適當的轉置)得到。

如果熟悉SVD分解的過程,通過SVD逆過程就可以輕鬆理解這種通過乘積來做加和的技巧。

丟掉那些下標記法吧!

卷積層求導

卷積怎麼求導呢?實際上卷積可以通過矩陣乘法來實現(是否旋轉無所謂的,對稱處理,caffe裡面是不是有image2col),當然也可以使用FFT在頻率域做加法。

那麼既然通過矩陣乘法,維數相容原則仍然可以運用,CNN求導比DNN複雜一些,要做些累加的操作。具體怎麼做還要看編程時選擇怎樣的策略、數據結構。

快速矩陣、向量求導之維數相容大法已成。

推薦閱讀:

ICML 2017 Best Paper Award論文解讀(1)
Coursera吳恩達《神經網路與深度學習》課程筆記(1)-- 深度學習概述
10分鐘快速入門PyTorch (3)

TAG:深度学习DeepLearning | 神经网络 | 数学 |