在前面的博客人工神經網絡入門和訓練深度神經網絡,也介紹了與本文相似的內容。前面的兩篇博客側重的是如何使用TensorFlow實現,而本文側重相關數學公式及其推導。html
一個神經元就是一個計算單元,傳入$n$個輸入,產生一個輸出,再應用於激活函數。記$n$維輸入向量爲$x$,$n$維權重矩陣向量是$w$,偏置項爲$b$,激活函數爲sigmoid,最終激活後的輸出爲$a$:web
\begin{align*}
a = \frac{1}{1 + \exp(-(w^T x + b))}
\end{align*}安全
將權重和偏置項組合在一塊兒,獲得以下公式:網絡
\begin{align*}
a = \frac{1}{1 + \exp(-[w^T \quad b] \cdot [x \quad 1])}
\end{align*}函數
圖1更直觀地描述了該公式:翻譯
圖1 單個神經元的輸入及輸出htm
將單個神經元擴展到一層,共$m$個神經元,每一個神經元的輸入都是$x$,權重記作$\{ w^{(i)}, \cdots, w^{(m)} \}$,偏置項記作$\{ b^{(i)}, \cdots, b^{(m)} \}$,則每一個神經元激活後的輸出:blog
\begin{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*}遞歸
下面咱們定義更抽象的形式,以便用於複雜的神經網絡:ci
\begin{align*}
&W = \begin{bmatrix} - & w^{(1)T} & -\\ & \cdots & \\ - & w^{(m)T} & - \end{bmatrix} \in \mathbb{R}^{m \times n} \\
&b = \begin{bmatrix} b_1\\ \vdots \\ b_m \end{bmatrix} \in \mathbb{R}^m \\
&z = Wx + b \\
&\sigma(z) = \begin{bmatrix} \frac{1}{1 + \exp(-z_1)}\\ \vdots \\ \frac{1}{1 + \exp(-z_m)} \end{bmatrix} \\
&\begin{bmatrix} a^{(1)}\\ \vdots \\ a^{(m)} \end{bmatrix} = \sigma(z) = \sigma(Wx + b)
\end{align*}
圖2 簡單的前饋神經網絡
如圖2所示的神經網絡,只有1個隱層,輸出:
\begin{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 \times 20},b \in \mathbb{R}^{8},U \in \mathbb{R}^{8 \times 1},s \in \mathbb{R}$。
最大間隔目標函數的思想是,確保正樣本的分值高於負樣本的分值。這與SVM的目標韓式較爲類似。正樣本經神經網絡計算,獲得的分值記作$s$,負樣本記作$s_c$,目標函數就是最大化$(s - s_c)$,或者最小化$(s_c - s)$。只有在$s_c > s$時才須要更新神經網絡的參數。所以,若是$s_c > s$,則損失是$s_c - s$;不然,損失是0。所以損失函數:
\begin{align*}
J = \max(s_c-s,0)
\end{align*}
訓練神經網絡的目標是使得$J$最小。
爲了獲得一個更安全的邊界,咱們但願正樣本分值比負樣本分值大出$\Delta$(大於0),所以:
\begin{align*}
J = \max(s_c - s + \Delta ,0)
\end{align*}
其實爲了簡化公式,能夠直接取$\Delta = 1$(根據SVM提到的知識,對函數距離的縮放,不會影響最終結果。$s$和$s_c$都是函數距離),模型在訓練過程當中其參數會自動適應這一約束,且不影響最終結果。此時目標函數:
\begin{align*}
J = \max(s_c - s + 1 ,0)
\end{align*}
咱們須要求得損失函數關於每一個參數的偏導數,而後使用梯度降低更新參數:
\begin{align*}
\theta^{(t+1)} = \theta^{(t)} - \alpha \nabla_{\theta^{(t)}} J
\end{align*}
反向傳播使用鏈式求導法則,求得損失函數關於每一個參數的偏導數。爲了進一步理解這一技術,首先看一下圖3的神經網絡:
圖3
上圖的神經網絡只有一個隱層,一個輸出。爲簡單起見,定義如下機率:
圖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$的偏導數:
\begin{align*}
\frac{\partial J}{\partial s} = -\frac{\partial J}{\partial s_c} = -1
\end{align*}
爲簡單起見,咱們只計算$\frac{\partial s}{\partial w^{(1)}_{ij}}$:
\begin{align*}
\frac{\partial s}{\partial w^{(1)}_{ij}} &= \frac{\partial W^{(2)} a^{(2)}}{\partial w^{(1)}_{ij}} \tag{1} \\
&= \frac{\partial W^{(2)}_i a^{(2)}_i}{\partial w^{(1)}_{ij}} \tag{2} \\
&= W^{(2)}_i \frac{\partial a^{(2)}_i}{\partial w^{(1)}_{ij}} \tag{3} \\
\end{align*}
第(1)步很直觀,由於$s = W^{(2)} a^{(2)}$。第(2)步是由於,只有在計算標量$a^{(2)}_i$時,纔會用到向量$W^{(1)}_i$。第(3)步也很直觀,咱們是在求關於$W^{(1)}_i$的偏導數,$W^{(2)}_i$直接看作常數。
而後應用鏈式法則:
\begin{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(這種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)}$的偏差傳播
用向量化的代碼取代for循環,有助於提升代碼的執行效率(能夠充分利用GPU加速吧?)。
上面咱們給出瞭如何計算一個參數的梯度,如今咱們介紹更通常化的方法,一次性地更新整個權重矩陣和偏置向量。這一簡單的擴張有助於爲咱們創建一種直覺,偏差傳播能夠抽象到矩陣-向量級別。
給出一個權重$W^{(k)}_{ij}$,咱們定義其偏差梯度爲$\delta^{(k+1)}_i \cdot a^{(k)}_j$。$W^{(k)}$是將$a^{(k)}$映射爲$z^{(k+1)}$的權重矩陣。咱們能夠創建整個矩陣$W^{(k)}$的偏差梯度:
\begin{align*}
\nabla_{W^{(k)}} = \begin{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}$,這能夠通常化爲以下的矩陣形式:
\begin{align*}
\delta^{(k)} = f'(z^{(k)}) \circ (W^{(k)T}\delta^{(k+1)}_i)
\end{align*}
其中,$\circ$運算符是指矩陣點乘($\mathbb{R}^N \circ \mathbb{R}^N \rightarrow \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,對應該課程的第四、5節。筆記的後半部分還介紹了神經網絡的一些實踐經驗以及技巧,這與先前的博客訓練深度神經網絡存在較大重疊,因此並無寫在這裏。