深度學習筆記(八)——改善深層神經網絡:超參數調試、正則化以及優化(超參數調試、Batch 正則化和程序框架)

1.調試處理

深度神經網絡需要調試的超參數較多,包括:

  • \alpha:學習因子

  • \beta:動量梯度下降因子

  • \beta _1,\beta _2,\varepsilon:Adam算法參數

  • #layers:神經網絡層數

  • #hidden units:各隱藏層神經元個數

  • learning rate decay:學習因子下降參數

  • mini-batch size:批量訓練樣本包含的樣本個數

超參數之間也有重要性差異。通常來說,學習因子\alpha是最重要的超參數,也是需要重點調試的超參數。動量梯度下降因子\beta、各隱藏層神經元個數#hidden units和mini-batch size的重要性僅次於\alpha。然後就是神經網絡層數#layers和學習因子下降參數learning rate decay。最後,Adam算法的三個參數\beta _1,\beta _2,\varepsilon一般常設置爲0.9,0.999和10^{-8},不需要反覆調試。當然,這裏超參數重要性的排名並不是絕對的,具體情況,具體分析。
如何選擇和調試超參數?傳統的機器學習中,我們對每個參數等距離選取任意個數的點,即網格取點,然後,分別使用不同點對應的參數組合進行訓練,最後根據驗證集上的表現好壞,來選定最佳的參數。例如有兩個待調試的參數,分別在每個參數上選取5個點,這樣構成了5x5=25種參數組合,如下圖所示:

這裏寫圖片描述

這種做法在參數比較少的時候效果較好。但是在深度神經網絡模型中,我們一般不採用這種均勻間隔取點的方法,比較好的做法是使用隨機選擇。也就是說,對於上面這個例子,我們隨機選擇25個點,作爲待調試的超參數,如下圖所示:

這裏寫圖片描述

 隨機化選擇參數的目的是爲了儘可能地得到更多種參數組合。還是上面的例子,如果使用均勻採樣的話,每個參數只有5種情況;而使用隨機採樣的話,每個參數有25種可能的情況,因此更有可能得到最佳的參數組合。

這種做法帶來的另外一個好處就是對重要性不同的參數之間的選擇效果更好。假設hyperparameter1爲\alpha,hyperparameter2爲\varepsilon,顯然二者的重要性是不一樣的。如果使用第一種均勻採樣的方法,\varepsilon的影響很小,相當於只選擇了5個\alpha值。而如果使用第二種隨機採樣的方法,\varepsilon\alpha都有可能選擇25種不同值。這大大增加了\alpha調試的個數,更有可能選擇到最優值。其實,在實際應用中完全不知道哪個參數更加重要的情況下,隨機採樣的方式能有效解決這一問題,但是均勻採樣做不到這點。

在經過隨機採樣之後,我們可能得到某些區域模型的表現較好。然而,爲了得到更精確的最佳參數,我們應該繼續對選定的區域進行由粗到細的採樣(coarse to fine sampling scheme)。也就是放大表現較好的區域,再對此區域做更密集的隨機採樣。例如,對下圖中右下角的方形區域再做25點的隨機採樣,以獲得最佳參數。

這裏寫圖片描述

2.爲超參數選擇合適的範圍

對於某些超參數是可以進行尺度均勻採樣的,但是某些超參數需要選擇不同的合適尺度進行隨機採樣。

超參數#layers和#hidden units都是正整數,是可以進行均勻隨機採樣的,即超參數每次變化的尺度都是一致的(如每次變化爲1)。

但是,對於某些超參數,可能需要非均勻隨機採樣。例如超參數\alpha,待調範圍是[0.0001, 1]。如果使用均勻隨機採樣,那麼有90%的採樣點分佈在[0.1, 1]之間,只有10%分佈在[0.0001, 0.1]之間。這在實際應用中是不太好的,因爲最佳的\alpha值可能主要分佈在[0.0001, 0.1]之間,而[0.1, 1]範圍內​\alpha值效果並不好。因此我們更關注的是區間[0.0001, 0.1],應該在這個區間內細分更多刻度。

通常的做法是將線性軸轉換爲對數軸,將均勻尺度轉化爲非均勻尺度,然後再在log scale下進行均勻採樣。這樣,[0.0001, 0.001],[0.001, 0.01],[0.01, 0.1],[0.1, 1]各個區間內隨機採樣的超參數個數基本一致,也就擴大了之前[0.0001, 0.1]區間內採樣值個數。

這裏寫圖片描述

一般解法是,如果線性區間爲[a, b],令m=log(a),n=log(b),則對應的log區間爲[m,n]。對log區間的[m,n]進行隨機均勻採樣,然後得到的採樣值r,最後反推到線性區間,即10^r10^r就是最終採樣的超參數。相應的Python語句爲:

m = np.log10(a)
n = np.log10(b)
r = np.random.rand()
r = m + (n-m)*r
r = np.power(10,r)

 除了\alpha之外,動量梯度因子\beta也是一樣,在超參數調試的時候也需要進行非均勻採樣。一般\beta的取值範圍在[0.9, 0.999]之間,比較難以直接取值。所以一般爲1-\beta取值,取值範圍[0.001, 0.1]。直接進行log變換即可。

假設\beta從0.9000變化爲0.9005,那麼\frac{1}{1-\beta}基本沒有變化。但假設\beta從0.9990變化爲0.9995,那麼\frac{1}{1-\beta}前後差別1000。\beta越接近1,指數加權平均的個數越多,變化越大。所以對\beta接近1的區間,應該採集得更密集一些。

3.超參數訓練的實踐:Pandas vs. Caviar

經過調試選擇完最佳的超參數並不是一成不變的,一段時間之後(例如一個月),需要根據新的數據和實際情況,再次調試超參數,以獲得實時的最佳模型。

在訓練深度神經網絡時,一種情況是受計算能力所限,一個人只能對一個模型進行訓練,調試不同的超參數,使得這個模型有最佳的表現。我們稱之爲Babysitting one model。另外一種情況是可以對多個模型同時進行訓練,每個模型上調試不同的超參數,根據表現情況,選擇最佳的模型。我們稱之爲Training many models in parallel。

這裏寫圖片描述

因爲第一種情況只使用一個模型,所以類比做Panda approach;第二種情況同時訓練多個模型,類比做Caviar approach。使用哪種模型是由計算資源、計算能力所決定的。一般來說,對於非常複雜或者數據量很大的模型,使用Panda approach更多一些。

4.正則化網絡的激活函數

Batch Normalization可以讓調試超參數更加簡單,神經網絡模型更加“健壯”。即較好模型可接受的超參數範圍更大一些,包容性更強,更容易去訓練一個深度神經網絡。

在訓練神經網絡時,標準化輸入可以提高訓練的速度。方法是對訓練數據集進行歸一化的操作,即將原始數據減去其均值\mu後,再除以其方差\sigma ^2。但是標準化輸入只是對輸入進行了處理,那麼對於神經網絡,又該如何對各隱藏層的輸入進行標準化處理呢?

其實在神經網絡中,第l層隱藏層的輸入就是第l-1層隱藏層的輸出A^{[l-1]}。對A^{[l-1]}進行標準化處理,從原理上來說可以提高W^{[l]}b^{[l]}的訓練速度和準確度。這種對各隱藏層的標準化處理就是Batch Normalization。實際應用中,一般是對Z^{[l-1]}進行標準化處理而不是A^{[l-1]},其實差別不是很大。

Batch Normalization對第l層隱藏層的輸入Z^{[l-1]}做如下標準化處理,忽略上標[l-1]

\mu=\frac1m\sum_iz^{(i)}

\sigma^2=\frac1m\sum_i(z_i-\mu)^2

z^{(i)}_{norm}=\frac{z^{(i)}-\mu}{\sqrt{\sigma^2+\varepsilon}}

其中,m是單個mini-batch包含樣本個數,\varepsilon是爲了防止分母爲零,可取值10^{-8}。這樣,使得該隱藏層的所有輸入z^{(i)}均值爲0,方差爲1。

但是,大部分情況下並不希望所有的z^{(i)}均值都爲0,方差都爲1,也不太合理。通常需要對z^{(i)}進行進一步處理:\tilde z^{(i)}=\gamma\cdot z^{(i)}_{norm}+\beta

上式中,\gamma\beta是learnable parameters,類似於W和b一樣,可以通過梯度下降等算法求得。這裏,\gamma\beta的作用是讓\tilde z^{(i)}的均值和方差爲任意值,只需調整其值就可以了。例如,令:

\gamma=\sqrt{\sigma^2+\varepsilon},\ \ \beta=u

\tilde z^{(i)}=z^{(i)},即identity function。可見,設置\gamma\betaβ爲不同的值,可以得到任意的均值和方差。

通過Batch Normalization,對隱藏層的各個z^{[l](i)}進行標準化處理,得到\tilde z^{[l](i)},替代z^{[l](i)}

值得注意的是,輸入的標準化處理Normalizing inputs和隱藏層的標準化處理Batch Normalization是有區別的。Normalizing inputs使所有輸入的均值爲0,方差爲1。而Batch Normalization可使各隱藏層輸入的均值和方差爲任意值。實際上,從激活函數的角度來說,如果各隱藏層的輸入均值在靠近0的區域即處於激活函數的線性區域,這樣不利於訓練好的非線性神經網絡,得到的模型效果也不會太好。

5.將Batch Norm擬合進神經網絡

對於L層神經網絡,經過Batch Norm的作用,整體流程如下:

這裏寫圖片描述

Batch Norm經常使用在mini-batch上。因爲Batch Norm對各隱藏層Z^{[l]}=W^{[l]}A^{[l-1]}+b^{[l]}有去均值的操作,所以這裏的常數項b^{[l]}可以消去,其數值效果完全可以由\tilde Z^{[l]}中的\beta來實現。因此,在使用Batch Norm時,可以忽略各隱藏層的常數項b^{[l]}。使用梯度下降算法時,分別對W^{[l]}\beta^{[l]}\gamma^{[l]}進行迭代更新。除了傳統的梯度下降算法之外,還可以使用動量梯度下降、RMSprop或者Adam等優化算法。

6.Batch Norm爲什麼奏效

我們可以把輸入特徵做均值爲0,方差爲1的規範化處理,來加快學習速度。而Batch Norm也是對隱藏層各神經元的輸入做類似的規範化處理。總的來說,Batch Norm不僅能夠提高神經網絡訓練速度,而且能讓神經網絡的權重W的更新更加“穩健”,尤其在深層神經網絡中更加明顯。比如神經網絡很後面的W對前面的W包容性更強,即前面的W的變化對後面W造成的影響很小,整體網絡更加健壯。

如果實際應用的樣本與訓練樣本分佈不同,即發生了covariate shift,則一般是要對模型重新進行訓練的。在神經網絡,尤其是深度神經網絡中,covariate shift會導致模型預測效果變差,重新訓練的模型各隱藏層的W^{[l]}b^{[l]}均產生偏移、變化。而Batch Norm的作用恰恰是減小covariate shift的影響,限制了在前層的參數更新會影響數值分佈的程度,減少了輸入值改變的問題,讓模型變得更加健壯,魯棒性更強。Batch Norm減少了各層W^{[l]}b^{[l]}之間的耦合性,讓各層更加獨立,實現自我訓練學習的效果。也就是說,如果輸入發生covariate shift,那麼因爲Batch Norm的作用,對個隱藏層輸出Z^{[l]}進行均值和方差的歸一化處理,W^{[l]}b^{[l]}更加穩定,使得原來的模型也有不錯的表現。

從另一個方面來說,Batch Norm也起到輕微的正則化(regularization)效果。具體表現在:

  • 每個mini-batch都進行均值爲0,方差爲1的歸一化操作

  • 每個mini-batch中,對各個隱藏層的Z^{[l]}添加了隨機噪聲,效果類似於Dropout

  • mini-batch越小,正則化效果越明顯

但是,Batch Norm的正則化效果比較微弱,正則化也不是Batch Norm的主要功能。batch歸一化一次只能處理一個mini-batch上的數據。

7.測試時的Batch Norm

訓練過程中,Batch Norm是對單個mini-batch進行操作的,但在測試過程中,可能需要Batch Norm對單個樣本進行處理。

首先,回顧一下訓練過程中Batch Norm的主要過程:

\mu=\frac1m\sum_iz^{(i)}

\sigma^2=\frac1m\sum_i(z^{(i)}-\mu)^2

z_{norm}^{(i)}=\frac{z^{(i)}-\mu}{\sqrt{\sigma^2+\varepsilon}}

\tilde z^{(i)}=\gamma\cdot z^{(i)}_{norm}+\beta

其中,\mu\sigma ^2是對單個mini-batch中所有m個樣本求得的。如果只有一個樣本,求均值和方差沒有意義,需要對\mu\sigma ^2進行估計。理論上可以將所有訓練集放入最終的神經網絡模型中,然後將每個隱藏層計算得到的\mu^{[l]}\sigma ^{2[l]}直接作爲\mu\sigma ^2來使用。但是,實際應用中一般使用指數加權平均的方法來預測測試過程單個樣本的\mu\sigma ^2

指數加權平均的做法很簡單,對於第l層隱藏層,考慮所有mini-batch在該隱藏層下的\mu^{[l]}\sigma ^{2[l]},然後用指數加權平均的方式來預測得到當前單個樣本的\mu^{[l]}\sigma ^{2[l]}。這樣就實現了對測試過程單個樣本的均值和方差估計。最後,再利用訓練過程得到的\gamma\beta值計算出各層的\tilde z^{(i)}值。

8.softmax迴歸

目前介紹的都是二分類問題,神經網絡輸出層只有一個神經元,表示預測輸出\hat y是正類的概率P(y=1|x)\hat y>0.5則判斷爲正類,\hat y<0.5則判斷爲負類。

對於多分類問題,用C表示種類個數,神經網絡中輸出層就有C個神經元,即n^{[L]}=C。其中,每個神經元的輸出依次對應屬於該類的概率,即P(y=c|x)。爲了處理多分類問題,我們一般使用Softmax迴歸模型。C=2,softmax迴歸等於logistic迴歸。Softmax迴歸模型輸出層的激活函數如下所示:

z^{[L]}=W^{[L]}a^{[L-1]}+b^{[L]}

a^{[L]}_i=\frac{e^{z^{[L]}_i}}{\sum_{i=1}^Ce^{z^{[L]}_i}}

輸出層每個神經元的輸出a^{[L]}_i對應屬於該類的概率,滿足:

\sum_{i=1}^Ca^{[L]}_i=1

所有的a^{[L]}_i,即\hat y,維度爲(C, 1)。

下面給出幾個簡單的線性多分類的例子:

這裏寫圖片描述

9.訓練一個softmax分類器

先來看一下softmax classifier的loss function。舉例來說,假如C=4,某個樣本的預測輸出\hat y和真實輸出y爲:

\hat y=\left[ \begin{matrix} 0.3 \\ 0.2 \\ 0.1 \\ 0.4 \end{matrix} \right]

y=\left[ \begin{matrix} 0 \\ 1 \\ 0 \\ 0 \end{matrix} \right]

\hat y值來看,P(y=4|x)=0.4,概率最大,而真實樣本屬於第2類,因此該預測效果不佳。定義softmax classifier的loss function爲:

L(\hat y,y)=-\sum_{j=1}^4y_j\cdot log\ \hat y_j

然而,由於只有當j=2時,y_{2}=1,其它情況下,y_{i}=0。所以,上式中的L(\hat y,y)可以簡化爲:

L(\hat y,y)=-y_2\cdot log\ \hat y_2=-log\ \hat y_2

所有m個樣本的cost function爲:

J=\frac1m\sum_{i=1}^mL(\hat y,y)

其預測輸出向量A^{[L]}\hat Y的維度爲(4, m)。

softmax classifier的反向傳播過程仍然使用梯度下降算法,其推導過程與二元分類有一點點不一樣。因爲只有輸出層的激活函數不一樣,先推導dZ^{[L]}

da^{[L]}=\frac{dLoss}{da^{[L]}}=-loga^{[l]}=-\frac{1}{a^{[L]}}

\frac{\partial a^{[L]}}{\partial z^{[L]}}=\frac{\partial}{\partial z^{[L]}}\cdot (\frac{e^{z^{[L]}_i}}{\sum_{i=1}^Ce^{z^{[L]}_i}})=a^{[L]}\cdot (1-a^{[L]})

dz^{[L]}=da^{[L]}\cdot \frac{\partial a^{[L]}}{\partial z^{[L]}}=a^{[L]}-1=a^{[L]}-y

對於所有m個訓練樣本:

dZ^{[L]}=A^{[L]}-Y

可見dZ^{[l]}的表達式與二元分類結果是一致的,雖然推導過程不太一樣。然後就可以繼續進行反向傳播過程的梯度下降算法了,推導過程與二元分類神經網絡完全一致。

10.深度學習框架

深度學習框架有很多,例如:

  • Caffe/Caffe2

  • CNTK

  • DL4J

  • Keras

  • Lasagne

  • mxnet

  • PaddlePaddle

  • TensorFlow

  • Theano

  • Torch

一般選擇深度學習框架的基本準則是:

  • Ease of programming(development and deployment)

  • Running speed

  • Truly open(open source with good governance)

11.TensorFlow

舉個例子來說明,例如cost function是參數w的函數:

J=w^2-10w+25

如果使用TensorFlow對cost function進行優化,求出最小值對應的w,程序如下:

import numpy as np
import tensorflow as tf

w = tf.Variable(0,dtype=tf.float32)
#cost = tf.add(tf.add(w**2,tf.multiply(-10,w)),25)
cost = w**2 - 10*w +25
train = tf.train.GradientDescentOptimizer(0.01).minimize(cost)

init = tf.global_variables_initializer()
session = tf.Session()
session.run(init)
print(session.run(w))
session.run(train)
print(session.run(w))
for i in range(1000):
    session.run(train)
print(session.run(w))

 

>>0.0

>>0.1

>>4.99999

TensorFlow框架內可以直接調用梯度下降優化算法,不需要我們自己再寫程序了,大大提高了效率。在運行1000次梯度下降算法後,w的解爲4.99999,已經非常接近w的最優值5了。

針對上面這個例子,如果對w前的係數用變量x來代替,程序如下:

import numpy as np
import tensorflow as tf

cofficients = np.array([[1.],[-10.],[25.]])

w = tf.Variable(0,dtype=tf.float32)
x = tf.placeholder(tf.float32,[3,1])
#cost = tf.add(tf.add(w**2,tf.multiply(-10,w)),25)
#cost = w**2 - 10*w +25
cost = x[0][0]*w**2 + x[1][0]*w + x[2][0]
train = tf.train.GradientDescentOptimizer(0.01).minimize(cost)

init = tf.global_variables_initializer()
session = tf.Session()
session.run(init)
print(session.run(w))
session.run(train, feed_dict=(x:coefficients))
print(session.run(w))
for i in range(1000):
    session.run(train, feed_dict=(x:coefficients))
print(session.run(w))

>>0.0

>>0.1

>>4.99999

結果跟之前是一樣的。除此之外,我們還可以更改x即cofficients的值,而得到不同的優化結果w。

另外,上段程序中的:

session = tf.Session()
session.run(init)
print(session.run(w))

有另外一種寫法:

with tf.Session() as session:
    session.run(init)
    print(session.run(w))

TensorFlow的最大優點就是採用數據流圖(data flow graphs)來進行數值運算。圖中的節點(Nodes)表示數學操作,圖中的線(edges)則表示在節點間相互聯繫的多維數據數組,即張量(tensor)。而且它靈活的架構讓你可以在多種平臺上展開計算,例如臺式計算機中的一個或多個CPU(或GPU),服務器,移動設備等等。
 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章