天天看點

神經網絡及其訓練1 神經網絡基礎

在前面的部落格人工神經網絡入門和訓練深度神經網絡,也介紹了與本文類似的内容。前面的兩篇部落格側重的是如何使用TensorFlow實作,而本文側重相關數學公式及其推導。

1 神經網絡基礎

1.1 單個神經元

一個神經元就是一個計算單元,傳入$n$個輸入,産生一個輸出,再應用于激活函數。記$n$維輸入向量為$x$,$n$維權重矩陣向量是$w$,偏置項為$b$,激活函數為sigmoid,最終激活後的輸出為$a$:

egin{align*}

a = frac{1}{1 + exp(-(w^T x + b))}

end{align*}

将權重和偏置項組合在一起,得到如下公式:

egin{align*}

a = frac{1}{1 + exp(-[w^T quad b] cdot [x quad 1])}

end{align*}

圖1更直覺地描述了該公式:

圖1 單個神經元的輸入及輸出

1.2 單層神經元

将單個神經元擴充到一層,共$m$個神經元,每個神經元的輸入都是$x$,權重記做${ w^{(i)}, cdots, w^{(m)} }$,偏置項記做${ b^{(i)}, cdots, b^{(m)} }$,則每個神經元激活後的輸出:

egin{align*}

a_1 &= frac{1}{1 + exp(-((w^{(1)})^T x + b_1))} \

&vdots \

a_m &= frac{1}{1 + exp(-((w^{(m)})^T x + b_m))}

end{align*}

下面我們定義更抽象的形式,以便用于複雜的神經網絡:

egin{align*}

&W = egin{bmatrix} - & w^{(1)T} & -\ & cdots & \ - & w^{(m)T} & - end{bmatrix} in mathbb{R}^{m imes n} \

&b = egin{bmatrix} b_1\ vdots \ b_m end{bmatrix} in mathbb{R}^m \

&z = Wx + b \

&sigma(z) = egin{bmatrix} frac{1}{1 + exp(-z_1)}\ vdots \ frac{1}{1 + exp(-z_m)} end{bmatrix} \

&egin{bmatrix} a^{(1)}\ vdots \ a^{(m)} end{bmatrix} = sigma(z) = sigma(Wx + b)

end{align*}

1.3 前饋計算

圖2 簡單的前饋神經網絡

如圖2所示的神經網絡,隻有1個隐層,輸出:

egin{align*}

s = U^T a = U^T f(Wx + b)

end{align*}

其中,$f$是激活函數。

次元分析:假設詞向量次元為2,一次使用5個詞作為輸入,則輸入$x in mathbb{R}^{20}$。如果隐層有8個sigmoid神經元,并在輸出層産生1個未規範化的分值,那麼$W in mathbb{R}^{8 imes 20},b in mathbb{R}^{8},U in mathbb{R}^{8 imes 1},s in mathbb{R}$。

1.4 最大間隔目标函數

最大間隔目标函數的思想是,確定正樣本的分值高于負樣本的分值。這與SVM的目标韓式較為相似。正樣本經神經網絡計算,得到的分值記做$s$,負樣本記做$s_c$,目标函數就是最大化$(s - s_c)$,或者最小化$(s_c - s)$。隻有在$s_c > s$時才需要更新神經網絡的參數。是以,如果$s_c > s$,則損失是$s_c - s$;否則,損失是0。是以損失函數:

egin{align*}

J = max(s_c-s,0)

end{align*}

訓練神經網絡的目标是使得$J$最小。

為了得到一個更安全的邊界,我們希望正樣本分值比負樣本分值大出$Delta$(大于0),是以:

egin{align*}

J = max(s_c - s + Delta ,0)

end{align*}

其實為了簡化公式,可以直接取$Delta = 1$(根據SVM提到的知識,對函數距離的縮放,不會影響最終結果。$s$和$s_c$都是函數距離),模型在訓練過程中其參數會自動适應這一限制,且不影響最終結果。此時目标函數:

egin{align*}

J = max(s_c - s + 1 ,0)

end{align*}

1.5 反向傳播訓練模型

我們需要求得損失函數關于每個參數的偏導數,然後使用梯度下降更新參數:

egin{align*}

heta^{(t+1)} = heta^{(t)} - alpha

abla_{ heta^{(t)}} J

end{align*}

反向傳播使用鍊式求導法則,求得損失函數關于每個參數的偏導數。為了進一步了解這一技術,首先看一下圖3的神經網絡:

圖3

上圖的神經網絡隻有一個隐層,一個輸出。為簡單起見,定義以下機率:

  • $x_i$是神經網絡的一個輸入
  • $s$是神經網絡的輸出
  • 第$k$層的第$j$個神經元接受标量輸入$z^{(k)}_j$并産生标量激活輸出$a^{(k)}_j$
  • 将$z^{(k)}_j$算出的反向傳播誤差記做$delta^{(k)}_j$
  • 第一層是指輸入層,而不是第一個隐層。對于輸入層,$x_j = z^{(1)}_j = a^{(1)}_j$
  • $W^{(k)}$是轉移矩陣,将第$k$層的輸出映射為第$k+1$層的輸入

圖4 與更新$W^{(1)}_{14}$相關的部分

如圖4,如果要更新$W^{(1)}_{14}$,首先要意識到,隻有在計算$z^{(2)}_1$時才會用到$W^{(1)}_{14}$。$z^{(2)}_1$僅僅用于計算了$a^{(2)}_1$,$a^{(2)}_1$與$W^{(2)}_1$用于計算最終的分值。首先有算是函數關于$s$和$s_c$的偏導數:

egin{align*}

frac{partial J}{partial s} = -frac{partial J}{partial s_c} = -1

end{align*}

為簡單起見,我們隻計算$frac{partial s}{partial w^{(1)}_{ij}}$:

egin{align*}

frac{partial s}{partial w^{(1)}_{ij}} &= frac{partial W^{(2)} a^{(2)}}{partial w^{(1)}_{ij}} ag{1} \

&= frac{partial W^{(2)}_i a^{(2)}_i}{partial w^{(1)}_{ij}} ag{2} \

&= W^{(2)}_i frac{partial a^{(2)}_i}{partial w^{(1)}_{ij}} ag{3} \

end{align*}

第(1)步很直覺,因為$s = W^{(2)} a^{(2)}$。第(2)步是因為,隻有在計算标量$a^{(2)}_i$時,才會用到向量$W^{(1)}_i$。第(3)步也很直覺,我們是在求關于$W^{(1)}_i$的偏導數,$W^{(2)}_i$直接看做常數。

然後應用鍊式法則:

egin{align*}

W^{(2)}_i frac{partial a^{(2)}_i}{partial w^{(1)}_{ij}} &= W^{(2)}_i frac{partial a^{(2)}_i}{partial z^{(2)}_i} frac{partial z^{(2)}_i}{partial w^{(1)}_{ij}} \

&= W^{(2)}_i frac{partial f(z^{(2)}_i)}{partial z^{(2)}_i} frac{partial z^{(2)}_i}{partial w^{(1)}_{ij}} \

&= W^{(2)}_i f'(z^{(2)}_i) frac{partial z^{(2)}_i}{partial w^{(1)}_{ij}} \

&= W^{(2)}_i f'(z^{(2)}_i) frac{partial}{partial w^{(1)}_{ij}} (b^{(1)}_i + a^{(1)}_1W^{(1)}_{i1} + a^{(1)}_2W^{(1)}_{i2} + a^{(1)}_3W^{(1)}_{i3} + a^{(1)}_4W^{(1)}_{i4}) \

&= W^{(2)}_i f'(z^{(2)}_i) a^{(1)}_j \

&= delta^{(2)}_i cdot a^{(1)}_j

end{align*}

$delta^{(2)}_i$本質上是第2層第$i$個神經元反向傳回的誤差。

現在我們換一種方式,用誤差配置設定和反向傳播來讨論如何更新圖4中的更新$W^{(1)}_{14}$:

  1. 首先從$a^{(3)}_1$反向傳回誤差1
  2. 這個誤差乘以把$z^{(3)}_1$映射到$a^{(3)}_1$的神經元的導數。由于輸出層沒有激活函數,$z^{(3)}_1 = a^{(3)}_1$,是以這一導數剛好是1,乘積依然是1,也就是$delta^{(3)}_1 = 1$
  3. 目前誤差已經反向傳播到了$z^{(3)}_1 = 1$,我們需要将誤差“公平配置設定”給$a^{(2)}_1 = 1$
  4. 到達$a^{(2)}_1$的誤差是$delta^{(3)}_1 imes W^{(2)}_1 = W^{(2)}_1$
  5. 和第2不一樣,求得将$z^{(2)}_1$映射到a^{(2)}_1$的神經元的導數$f'(z^{(2)}_1)$
  6. 到達$z^{(2)}_1$的誤差$f'(z^{(2)}_1)W^{(2)}_1$,也就是$delta^{(2)}_1 = 1$
  7. 然後需要将誤差“公平配置設定”給$W^{(1)}_{14}$,也就是上一步的誤差乘以$a^{(4)}_1$
  8. 是以,最終得到損失函數關于$W^{(1)}_{14}$的偏導數$a^{(4)}_1 f'(z^{(2)}_1) W^{(2)}_{1}$

以上我們用鍊式法則和誤差配置設定反向傳播得到的結果是一樣的。

偏置項更新:偏置項也可以看成輸入向量的一個次元,隻不過這個次元始終為1(這種1.1小節中的第二個公式就可以看出)。是以,第$k$層第$i$個神經元偏置項的偏導數直接就是$delta^{(k)}_i$。例如,在上面我們是要更新$b^{(1)}_1$,而不是$W^{(1)}_{14}$,那麼梯度直接就是$f'(z^{(2)}_1) W^{(2)}_{1}$。

将$delta^{(k)}$到$delta^{(k-1)}$的誤差計算一般化:

圖5 從$delta^{(k)}$到$delta^{(k-1)}$的誤差傳播

  1. 我們已經有了從$z^{(k)}_i$傳回的誤差$delta^{(k)}$,如圖5左側所示
  2. 計算反向傳給$a^{(k-1)}_j$的誤差,通過$delta^{(k)}_i$乘以路徑權重$W^{(k-1)}_{ij}$
  3. 是以,$a^{(k-1)}_j$接收到的誤差就是$delta^{(k)}_i W^{(k-1)}_{ij}$
  4. 然而,$a^{(k-1)}_j$可能會前饋到下一層的多個節點,如圖5右側所示。這樣的話,$a^{(k-1)}_j$還要接收從$k$層的節點$m$反向傳回的誤差。
  5. 一次,$a^{(k-1)}_j$接收到的誤差是$delta^{(k)}_i W^{(k-1)}_{ij} + delta^{(k)}_m W^{(k-1)}_{mj}$
  6. 事實上,這可以一般化為$sum_i delta^{(k)}_i W^{(k-1)}_{ij}$
  7. 現在已經有了$a^{(k-1)}_j$的誤差,并且$a^{(k-1)}_j$關于$z^{(k-1)}_j$的導數為$f'(z^{(k-1)}_j)$
  8. 是以,誤差傳到了$z^{(k-1)}_j$,記做$delta^{(k-1)}_j$,大小為$f'(z^{(k-1)}_j) sum_i delta^{(k)}_i W^{(k-1)}_{ij}$

1.6 反向傳播訓練向量化

用向量化的代碼取代for循環,有助于提高代碼的執行效率(可以充分利用GPU加速吧?)。

上面我們給出了如何計算一個參數的梯度,現在我們介紹更一般化的方法,一次性地更新整個權重矩陣和偏置向量。這一簡單的擴張有助于為我們建立一種直覺,誤差傳播可以抽象到矩陣-向量級别。

給出一個權重$W^{(k)}_{ij}$,我們定義其誤差梯度為$delta^{(k+1)}_i cdot a^{(k)}_j$。$W^{(k)}$是将$a^{(k)}$映射為$z^{(k+1)}$的權重矩陣。我們可以建立整個矩陣$W^{(k)}$的誤差梯度:

egin{align*}

abla_{W^{(k)}} = egin{bmatrix}

delta^{(k+1)}_1 a^{(k)}_1 & delta^{(k+1)}_1 a^{(k)}_2 &cdots \

delta^{(k+1)}_2 a^{(k)}_1 & delta^{(k+1)}_2 a^{(k)}_2 &cdots \

vdots & vdots & ddots

end{bmatrix}

= delta^{(k+1)} a^{(k)T}

end{align*}

下面我們來看如何計算誤差向量$delta^{(k)}$。在圖5中我們已經知道,$delta^{(k)}_j = f'(z^{(k)}_j) sum_i delta^{(k+1)}_i W^{(k)}_{ij}$,這可以一般化為如下的矩陣形式:

egin{align*}

delta^{(k)} = f'(z^{(k)}) circ (W^{(k)T}delta^{(k+1)}_i)

end{align*}

其中,$circ$運算符是指矩陣點乘($mathbb{R}^N circ mathbb{R}^N

ightarrow mathbb{R}^N$)。

計算效率:我們探索了基于元素的更新和基于矩陣的更新。我們必須意識到,向量化的實作在科學運算環境裡效率更高,比如MATLAB和Python的NumPy/SciPy包。是以,我們應該使用向量化的實作。更進一步,在反向傳播時應該避免重複計算。比如,$delta^{(k)}$直接依賴于$delta^{(k+1)}$。我們應該確定,在使用$delta^{(k+1)}$更新完$W^{(k)}$之後,不能丢棄,而是要儲存訓練,用于後面計算$delta^{(k)}$。重複這一過程$(k-1) cdots (1)$。最終得到了一個計算上還負擔得起的遞歸過程。

本文翻譯自CS224n課程的官方筆記3,對應該課程的第4、5節。筆記的後半部分還介紹了神經網絡的一些實踐經驗以及技巧,這與先前的部落格訓練深度神經網絡存在較大重疊,是以并沒有寫在這裡。