上一章我們訓練了一個淺層神經網絡,只要兩個隱層。但如果處理復雜的問題,例如從高分辨率圖像中識別上百種類的物品,這就需要訓練一個深度DNN。也行包含十層,每層上百個神經元,幾十萬個連接。這絕不是鬧着玩的:
- 首先,需要面對梯度消失(或者相對的梯度爆炸)問題,這會導致淺層很難被訓練。
- 其次,這么大一個網絡,訓練速度很慢。
- 最后,一個包含上百萬參數的模型,存在很大過擬合的風險。
11.1 梯度消失(爆炸)問題
反向傳播算法會計算損失函數關於每一個參數的偏導數,然后使用梯度下降更新參數。不幸的是,反向傳播時,梯度經常會逐層越來越小,導致底層權重基本保持不變,從而不能得到好的訓練結果。這就是所謂的梯度消失問題。而有時候還可能發生相反的情況:很多層的權重每次修改量很大,導致不能收斂。這就是梯度爆炸問題,主要發生在RNN。更普遍的問題是,深度神經網絡往往梯度不穩定,不同的層學習速度差別很大。
這一問題知道2010才有了突破性的進展。在Xavier Glorot和Yoshua Bengio的論文Understanding the Difficulty of Training Deep Feedforward Neural Networks中,作者發現了一些造成這一問題的可能因素,包括logistic sigmoid激活函數和當時流行的隨機初始化方式(使用使用均值為0標准差為1的正態分布進行初始化)。他們表明,這一激活函數加上這種初始化方式,使得每一層輸出的方差遠大於輸入。這一情況在使用均值是0.5的logistic函數時變得更為糟糕(雙曲正切函數的均值是0,從而在深度網絡中表現優於logistic函數)。
觀察圖11-1的激活函數,隨着輸入值的絕對值變大,函數值趨近於1和0,此時導數趨近於0。因此在方向傳播時,幾乎沒有導數傳回去。

圖11-1 Logistic激活函數
11.1.1 Xavier and He Initialization
在其論文中,Glorot and Bengio提供了一種可以顯著改善該問題的建議。我們需要信號在兩個方向都可以正常的流動:在前向做出預測和反向傳播梯度時。我們不希望信號消失,也不希望它保障或者飽和(也就是趨近於某一個值)。為了使信號正常流動,作者主張每一層輸入和輸出的方差相等,並且在反向傳播時梯度的方差保持不變(如果對其中的數學細節感興趣,可以研究一下論文)。我們無法同時滿足這連個條件,不過作者提供了更為寬松的約束條件,並且在實踐中表現還不錯,各個連接的權重必須按照如下方式初始化:
正態分布均值0,標准差$\sigma = \sqrt{\frac{2}{n_{\mbox{input}} + n_{\mbox{output}}}}$
或者,$-r$到$+r$之間的均勻分布,並且$r = \sqrt{\frac{6}{n_{\mbox{input}} + n_{\mbox{output}}}}$
其中,$n_{\mbox{input}}$和$n_{\mbox{output}}$是改成輸入和輸出數量。這一初始化策略稱為Xavier初始化,或者Glorot初始化。
如果輸入和輸出數量大致相等,公式可以簡化為$\sigma = 1/\sqrt{n_{\mbox{input}}}$,$\sigma = \sqrt{3} / \sqrt{n_{\mbox{input}}}$,這正是我們在第十章使用的。
使用Xavier初始化策略可以使訓練速度得到顯著提升,這正是為當今深度學習帶來成果的一個技術手段。一些不久前的論文提供了類似的,針對其他激活函數的初始化策略,如表11-1所示。所有這些初始化策略有時被稱作He(這是中國姓氏,但不知道是何,賀,還是和)初始化。
表11-1. 針對不同激活函數的初始化策略

11.1.2 不飽和(Nonsaturating)激活函數
$f$是不飽和函數當且僅當$\mathop{\lim}\limits_{z \to \infty} |f(z)| = +\infty$
Glorot和Bengio在其2010年的論文中認為,帶來梯度消失(爆炸)問題的重要原因是可供選擇的激活函數太少。最初人們傾向於選擇sigmoid激活函數,但事實上在神經網絡中其他激活函數表現更好,尤其是ReLU。很重要的原因是ReLU對正值具有不飽和性(也就是沒有上限)。
不過ReLU也並不完美,會遇到被稱作dying ReLUs的問題:在訓練期間,有些神經元事實上死掉了,除了0之外什么也不會輸出(這里的輸出,應該指的是反向傳播時,高層對底層輸出的梯度)。有時候一半的神經元都是死的,尤其是學習率較大的情況下。一旦出現這一情況,基本上就不能恢復過來了,因為當ReLU的輸入是負數時,其梯度是0。
為了解決這一問題,可以使用ReLU的變體,比如leaky ReLU(如圖11-2)。$\mbox{LeakyReLU}_{\alpha}(z) = max(\alpha z, z)$。其中,超參數$\alpha$定義了輸入為負值時的傾斜度,一般設置為0.01。這個小斜坡保證了leaky ReLU不會死掉,不過也就可能進入長期的昏迷。畢竟還活着,長期昏迷后還是有可能蘇醒過來的。

圖11-2 Leaky ReLU
Djork-Arné Clevert等人在其2015年的一篇論文中提出了一種稱為exponential linear unit (ELU)的新的激活函數,表現好過ReLU的所有變體。
\begin{align*}
\mbox{ELU}_{\alpha}(z) = \left\{\begin{matrix}
\alpha(\exp(z) - 1) &\mbox{if} &z < 0 \\
z &\mbox{if} & z \geq 0
\end{matrix}\right.
\end{align*}

圖11-3 ELU激活函數
選擇激活函數時,一般ELU > leaky ReLU (及其變種) > ReLU > tanh > logistic。如果你更關心運行時表現,leaky ReLU要優於ELU。
11.1.3 Batch Normalization
盡管He initialization搭配ELU可以緩解訓練初期的梯度消失(爆炸)問題,但是並不能保證在整個訓練周期都不會出現這一問題。Sergey Ioffe和Christian Szegedy在其2015年的論文中提出了一種被稱作Batch Normalization (BN)的解決方案。比梯度消失(爆炸)更一般的問題是,由於前層參數的變化,造成后層輸入數據分布變化的問題(他們稱為Internal Covariate Shift問題)。
該技術就是在每一層應用激活函數之前,增加一些操作。首先對數據進行簡單的zero-centering和normalizing(其實就是轉換成均值為0,標准差為1的數據),然后使用兩個參數對數據進行scaling和shifting(縮放和平移)操作。換句話說,這兩步就是讓模型學習每層輸入最優的scale(規模)和均值。
批規范化算法(Batch Normalization algorithm):

其中,
- $\mu_B$是經驗均值,在整個mini-batch上進行評估。
- $\sigma_B$是經驗標准差,也是在整個mini-batch上評估的。
- $m_B$是mini-batch的實例數。
- $\hat{X}^{(i)}$是zero-centered和normalized后的輸入數據。
- $\gamma$是該層的scaling參數。
- $\beta$是該層的shifting參數。
- $\epsilon$是為了避免除零的,通常為$10^{-3}$,也被稱為平滑系數。
- $Z^{(i)}$是BN操作的輸出。
在測試階段,不存在mini-batch來計算經驗均值和標准差,簡單地使用整個訓練集的均值和標准差即可。 These are typically efficiently computed during training using a moving average.(這句話沒看懂。moving average是一種快速計算均值和標准差的技術?)所以,每個batch-normalized層需要學習四個參數:$\gamma$(scale),$\beta$(offset),$\mu$(mean)和$\sigma_B$(standard deviation)。
作者宣稱,該技術提升了他們實驗所采用的所有深度學習網絡。梯度消失問題得到了明顯的環境,以至於可以使用飽和激活函數,比如tanh,甚至是logistic激活函數。網絡對權重初始值的敏感度也明顯降低。他們也可以提到學習率來加快訓練速度。此外,Batch Normalization也充當了正則化的角色,減少其他正則化技術的使用(比如后面章節將會介紹的dropout)。
當然,該技術有利也有弊——其增加了模型復雜度,這是一種運行時懲罰:額外的計算導致預測緩慢。所以,如果更在意預測效率的話,最好還是選擇ELU + He initialization。
然后是Batch Normalization的TensorFlow實現,代碼可參考作者的GitHub。
11.1.4 Gradient Clipping
減輕梯度爆炸的一項技術是使反向傳播的梯度不超過某個閾值(這在訓練rnn時比較有用)。 這一技術稱為Gradient Clipping。現在人們更喜歡Batch Normalization,不過了解一下Gradient Clipping還是有幫助的。
11.2 Reusing Pretrained Layers
如果訓練一個大型的DNN,一般都不會從零開始,而是找到一個已經訓練好的,與你的任務相似的模型。復用舊模型的底層,這稱為transfer learning。這不僅會加快訓練速度,而且需要更少的訓練數據。

圖11-4 Reusing pretrained layers
在上圖中,如果新舊模型訓練數據的結構不一致,那就需要增加一步預處理,將數據的格式轉換為就模型需要的。一般情況下,transfer learning在輸入值具有相似的低級別特征時表現良好。
11.2.1 Reusing a TensorFlow Model
講解在原始模型使用TensorFlow時,新的模型如何復用。
11.2.2 Reusing Models from Other Frameworks
講解如果模型是其他框架實現的,在TensorFlow中如何復用。
11.2.3 Freezing the Lower Layers
就是冷凍住低層級的參數,使其在反向傳播時不被更新。
11.2.4 Caching the Frozen Layers
如果使用了11.2.3的技術,由於前面幾層的參數是不變的,可以將最高冷凍層的輸出值緩存起來,不用每次前向傳播都去計算。這可以提高訓練速度。
11.2.5 Tweaking, Dropping, or Replacing the Upper Layers
原始模型的輸出層會被替換掉,因為它一本對新任務來講是沒用的,甚至與新任務輸出值的個數都不匹配。類似的,原始模型的高級隱層對新任務的用處較小。我們需要找到適合復用的層數。
首先復用所有層,訓練模型並評估其表現。然后解凍一個或兩個隱層,訓練之后觀察其表現是否提升。訓練數據越多,就解凍越多的層。
11.2.6 Model Zoos
這就是模型倉庫,可以找到別人訓練好的模型。
TensorFlow的model zoo位於https://github.com/tensorflow/models。包含目前最先進的圖像分類網絡,比如VGG, Inception, and ResNet。
另一個是Caffe’s Model Zoo:https://github.com/BVLC/caffe/wiki/Model-Zoo。
11.2.7 Unsupervised Pretraining
假設你要處理一個復雜的任務,並且沒有足夠帶標簽的訓練數據。並且的是,也找不到針對類似任務訓練好的模型。不要絕望!首先,你當然應該收集更多的帶標簽數據,如果這很困難或者很昂貴,仍然可以使用unsupervised pretraining完成任務(如圖11-5)。這就是說,如果你有大量的無標簽數據,那就可以一層層地訓練,使用無監督特征檢測(feature detector)算法,比如Restricted Boltzmann Machines或者autoencoders。等到所有層都訓練完成,就可以使用監督學習對網絡進行微調。

圖11-5 Unsupervised pretraining
這是一個漫長乏味的過程,但是往往表現良好。事實上,這一技術正是Geoffrey Hinton及其團隊在2016年使用並帶來了神經網絡的復興以及深度學習的成功。
11.2.8 Pretraining on an Auxiliary Task
另外一個選擇是,首先訓練一個輔助的模型,該模型的標簽數據可以輕易生成,然后復用底層來訓練模型實現真正的任務。
比如,你想建立一個人臉識別系統,並且沒有太多的帶標簽數據。不過你可以在網絡隨機搜集一些照片,並訓練一個模型來識別不同的照片中是否是同一個人。第一個模型會學習到識別人臉的優秀特征,這可以在第二個模型中復用,並減少第二個模型所需的訓練數據。
11.3 Faster Optimizers
這一小節的結論是,用AdamOptimizer替換掉GradientDescentOptimizer就行了。我先跳過了,后面有時間再補上這幾個優化算法。
11.4 Avoiding Overfitting Through Regularization
神經網絡往往參數很多,自由度很大,太容易過擬合,所以應該使用一些正則化技術防止過擬合。
11.4.1 Early Stopping
這在第四章介紹過,就是在校驗集表現最好時終止訓練。
11.4.2 $l_i$和$l_2$正則
這個也和第四章的意義,跟傳統機器模型的方式一樣。
11.4.3 Dropout
當前深度神經網絡最受歡迎的正則化技術大概就是dropout了。它首先由G. E. Hinton在2012年提出,並由Nitish Srivastava在其論文中完善。該技術取得了巨大成功。
這是一個很簡單的算法:在每一步訓練,所有神經元(包括輸入神經元但不包括輸出神經元)都有一個可能“dropped out”的概率$p$,意味着本次訓練它可能會被忽視,但在下次訓練有可能會被激活。超參數$p$被稱作dropout rate,一般被設置為50%。訓練結束后,神經元不再被忽視。
起初可能覺得吃驚,這一蠻橫的算法居然表現良好。我們現在假設,一家公司通過拋硬幣決定每個員工是否上班。很明顯這家公司要改編其組織架構。任何工作都不能依賴於單獨一個人,所以專業技術需要傳授給很多人。雇員必須學着與同事合作。如果某員工退出,影響不會太大。雖然不知道這一思想是否適用於公司運營,但其確實適用於神經網絡的訓練。神經元將對輸入值的輕微變化不再敏感,最終會得到一個更加魯棒,更容易一般化的神經網絡。
理解dropout巨大威力的另一個途徑就是理解,每一步訓練其實都在生成一個不同的神經網絡。由於每個神經元都可能出席或者缺席,總共有$2^N$(其中,$N$是可忽視神經元總數)種可能的網絡。如果訓練10000步,本質上就是訓練了10000個神經元。最終的神經元可以看成是這些單獨神經元的一個averaging ensemble。
還有一個技術細節。假設$p$ = 50%,在這種情況下測試階段的神經元數量是訓練階段的兩倍。為了彌補這一問題,我們需要在訓練結束后對連接權重乘以0.5。更一般的,我們需要在訓練結束后對連接權重乘以keep probability (1 – p)。
如果觀察到模型過擬合,可以增大dropout rate。欠擬合就減小dropout rate。
Dropout會顯著地降低收斂速度,但通常會得到一個更好的模型。多花費一些訓練時間也是值得的。
11.4.4 Max-Norm Regularization
另外一種比較受歡迎的神經網絡正則化方法是max-norm正則。對於每一個連接權重$w$,都使其滿足$\left \| w \right \|_2 \leq \gamma $,其中$\gamma$是max-norm超參數。
算法實現時,一般會先計算$\left \| w \right \|_2$,如果需要的話就對其進行修剪($w \leftarrow w\frac{r}{\left \| w \right \|_2}$)。
降低$\gamma$就可以減少過擬合的可能性。max-norm正則也可以緩解梯度消失(爆炸)問題(如果沒使用Batch Normalization)。
TensorFlow並不提供現成的max-norm正則,不過作者自己實現了一個。
11.4.5 Data Augmentation
該正則化技術就是通過已經存在的訓練集來生成新的訓練實例。這一技巧就是生成逼真的訓練數據。理想情況下,人無法區分該實例是否自動生成。簡單的增加white noise並沒有幫助,因為改變必須是可學習的(white noise不能被學習)。例如,根據一個圖片,生成各種不同的圖片,如下圖所示:

圖11-10 使用現有訓練樣本生成新的實例
11.5 實用的指導方針
DNN默認配置:
Initialization:He initialization
Activation function: ELU
Normalization: Batch Normalization
Regularization: Dropout
Optimizer: Adam
Learning rate schedule: None
默認的配置可能需要調整:
- 如果找不到一個合適的學習率(收斂太慢,增大學習率。然后收斂加快了,但是最終的模型不是最優的),那就可以嘗試增加一個learning schedule,比如exponential decay。
- 如果訓練集太小,可以實現data augmentation
- 如果想要一個稀疏的模型,可以增加$l_1$正則。需要更加稀疏的話,嘗試使用FTRL代替Adam優化,並配合$l_1$正則。
- 如果需要一個能夠快速預測的模型,那也許就需要放棄Batch Normalization,改為ELU激活函數。稀疏的模型也能加快預測時間。
