no2 神經網絡

反向傳播

求導鏈式法則+反向計算

訓練過程:通過前向傳播得到損失,反向傳播得到梯度(Lw ),最後用梯度更新權重。

一個神經元的運算

一個神經元的運算

那麼Lx=LzzxLy=LzzyLw=Lzzw 。通過這種方式,我們就可以從輸出的節點開始,往輸入節點的方向計算損失關於各個節點權重的導數值,從而用梯度下降法進行權重的迭代,利用最優化減少損失。

  • 和數學歸納法類似,已知了最右邊的損失L,通過求導進行反向傳播就可以計算出另一個節點的右邊的導數,每一個節點都有下一個節點,所以都可以求出其導數。

舉一個比較實際的例子

計算如下的一個式子:

f(x,y)=x+σ(y)σ(x)+(x+y)2

通過添加中間變量,進行的正向傳播。

x = 3 # example values
y = -4

# forward pass
sigy = 1.0 / (1 + math.exp(-y)) # sigmoid in numerator   #(1)
num = x + sigy # numerator                               #(2)
sigx = 1.0 / (1 + math.exp(-x)) # sigmoid in denominator #(3)
xpy = x + y                                              #(4)
xpysqr = xpy**2                                          #(5)
den = sigx + xpysqr # denominator                        #(6)
invden = 1.0 / den                                       #(7)
f = num * invden # done!                                 #(8)

反向傳播計算各個變量權重的導數

# backprop f = num * invden
dnum = invden # gradient on numerator                             #(8)
dinvden = num                                                     #(8)
# backprop invden = 1.0 / den 
dden = (-1.0 / (den**2)) * dinvden                                #(7)
# backprop den = sigx + xpysqr
dsigx = (1) * dden                                                #(6)
dxpysqr = (1) * dden                                              #(6)
# backprop xpysqr = xpy**2
dxpy = (2 * xpy) * dxpysqr                                        #(5)
# backprop xpy = x + y
dx = (1) * dxpy                                                   #(4)
dy = (1) * dxpy                                                   #(4)
# backprop sigx = 1.0 / (1 + math.exp(-x))
dx += ((1 - sigx) * sigx) * dsigx # Notice += !! See notes below  #(3)
# backprop num = x + sigy
dx += (1) * dnum                                                  #(2)
dsigy = (1) * dnum                                                #(2)
# backprop sigy = 1.0 / (1 + math.exp(-y))
dy += ((1 - sigy) * sigy) * dsigy                                 #(1)
# done! phew

向量化反向傳播(層與層之間)

# forward pass
W = np.random.randn(5, 10)
X = np.random.randn(10, 3)
D = W.dot(X)

# now suppose we had the gradient on D from above in the circuit
dD = np.random.randn(*D.shape) # same shape as D
dW = dD.dot(X.T) #.T gives the transpose of the matrix
dX = W.T.dot(dD)
  • 注意分析各個矩陣的形狀,比如說X是(10,3),dD是(5,3),那麼要得到(5,10)的dW,我們就需要dW = dD.dot(X.T)

細節

CIFAR-10的基本神經網絡識別思路

  1. 輸入層是一個3072大小的向量,是將一張圖片3通道的顏色值拉伸成一個向量
  2. 權重W1,隱藏層的輸入是XW1,然後使用RuLU激活函數,得到隱藏層的輸出,o=max(0,XW1)
  3. oW2是輸出層的輸出,s=oW2 ,s是該圖片屬於各個類的分數
  4. 其中權重W1和W2就是需要用梯度下降法學習的網絡參數

Tips.

  1. 不需要太多的數據,可以使用遷移學習,選擇一些別人訓練好的網絡,取他們的前部分,當作類似於特徵提取的功能模塊,然後訓練最後幾層,就可以節省數據
  2. 不要覺得計算量大就無腦增加數據

激活函數的選擇

激活函數在神經元的中間,將輸入轉化爲輸出。out=f(XW+b) ,其中的f就是激活函數。

sigmoid

σ(x)=1/(1+ex)

sigmoid函數圖像

優缺點

  1. 輸出控制在[0,1]之間
  2. 深度學習領域開始時使用頻率最高,而且可以解釋爲神經元的激活率
  3. 輸入很大或者很小的時候,會導致導數σx 很小,再加上其導數的最大值是0.25,這意味着在反向傳播的時候,導數在每一層至少會被壓縮爲原來的1/4,導數越來越小以至於對權重的更新很小,從而引起梯度彌散(gradient vanishing)
  4. 輸出不是0均值的,這裏輸入全爲正,導致在反向傳播的時候權重W全爲正或者全爲負。使用batch可以緩解這個問題
  5. exp冪運算比較耗時

梯度彌散:由於很多原因,比如說層數太多等,導致反向傳播的時候梯度變小,前面權重進行更新的時候變化非常小,以至於不能夠從樣本中進行有效的學習。

tanh

tanh(x)=2σ(2x)1

tanh函數圖像

優缺點

  1. 輸出控制在[-1,1]之間
  2. 輸出是0均值的
  3. 輸入很大或者很小的時候,會導致導數很小,從而引起梯度彌散(gradient vanishing)

ReLU(Rectified Linear Unit)

f(x)=max(0,x)

Relu函數圖像

優缺點

  1. 在大於0的區域不會飽和,也就是說導數不會變得很小
  2. 計算效率高
  3. 收斂速度比sigmoid和tanh快,比如有的快了6倍
  4. 輸出不是0均值的
  5. 在輸入小於0的區域會梯度消失,神經元處於非激活狀態,無法進行反向傳播,權重也無法進行更新,大於0的區域梯度直接向前傳播
  6. 存在一些死掉的神經元,即任何數據都不能激活它(wx+b<0),通常是由於學習率太高,梯度流很大,權重一下子就更新了很多,也可能是初始化權重策略,可以在訓練時統計來檢查,初始化的時候把偏置設置成一個比較小的正數

Leaky ReLU

f(x)=max(αx,x)

其中α 是一個很小的常量,比如說0.01,也可以通過反向傳播去訓練

優缺點:同ReLU,但是改進了5和6,神經元就算在輸入小於0的區域也不會梯度消失,不會有非激活狀態

ELU(Exponential Linear Units)

f(x)={x,α(expx1),if x>0otherwise

優缺點

  1. 不會有Dead ReLU(神經元失活)問題
  2. 輸出的均值接近0,zero-centered
  3. 冪運算計算效率高
  4. 其他同ReLU

Maxout

f(x)=max(wT1x+b1,wT2x+b2)

優缺點:統一了ReLU和Leaky ReLU,在神經元的計算上多了非線性的元素,但是計算參數數量多了一倍。

總結

  1. 優先考慮ReLu,但是要注意學習率,並注意網絡中神經元的失活問題
  2. 可以嘗試使用Leaky ReLU, Maxout, ELU
  3. 也可以嘗試使用tanh,但是期望不要太大
  4. 不要使用sigmoid

神經網絡結構

通常的網絡按層來組織,每層中都有神經元,沒有迴路。這種層的組織結構可以很輕鬆的進行向量矩陣的運算,容易理解和實現,並且效率高。全連接層(fully-connected layer)是很常見的一種結構,表示相鄰兩層之間的神經元兩兩連接。下面是兩個例子。

神經網絡結構
神經網絡結構

命名規則,N-層神經網絡,N包括隱藏層和輸出層,不包括輸入層。

輸出層不像神經網絡的其他層,一般沒有激活函數,因爲最後輸出層的輸出在分類問題中是代表類的分數,在迴歸問題中是實值數。

神經網絡的規模,通常用參數的數量,或者神經元的數量來衡量網絡的規模。例如在圖的例子中,第一個網絡有[3×4]+[4×2]=20 個權重,4+2個偏置,總共26個參數,或者有4+2個神經元(不包括輸入層)。

確定隱藏層層數和規模

網絡規模越大,容量越大,能表示的不同的函數越多。如下是一個二分類的例子,一個隱藏層。

不同隱藏節點對比

從上圖中可以看到,隨着隱藏神經元的增大,可以代表越複雜的函數,但是也有過擬合的風險(擬合太好,以至於把訓練數據的噪聲給擬合進去了),如上圖中的右圖,將異常點擬合進去,可能會導致模型的泛化能力差,也就是在測試集上表現差。

在實際使用中,儘量使用大的網絡,因爲過擬合可以通過正則化等方法修補,但是網絡太小導致不能表達需要的函數是不能修補的。

數據預處理

對於一個原始數據X[N,D],其中N是樣本數量,D是特徵數量。可以做如下的操作:

  1. 0均值化

    X -= np.mean(X, axis=0)

    有兩種處理方式,以CIFAR-10爲例,其中的圖片是[32,32,3]。

    1. 生成均值圖片,大小爲[32,32,3],是所有圖片的平均,然後讓每張圖片都減去這張均值圖片。(AlexNet)
    2. 通道均值,得到3個通道的均值,然後讓每張圖片都減去對應的均值。(VGGNet)
  2. 歸一化(normalized data)。歸一化的方法很多,不一定必須是如下的方法,比如最大最小值歸一化。圖像處理中很少使用歸一化,因爲像素的尺度是一致的,都是[0,255]

    X /= np.std(X, axis=0)

    0均值化和歸一化

  3. 根據協方差,可以使用PCA主成份分析,或者白化,降低特徵之間的相關性,使特徵具有相同的方差。在機器學習中比較常見,在圖像處理中比較少
    PCA & Whitening

    • 注意0均值化中,訓練集、驗證集、測試集減去的都是訓練集的均值

權重初始化

初始化爲0顯然不是一個很好的選擇,因爲這樣那麼對於每個神經元,輸出和梯度都是一樣的,那麼他們做的操作都是一樣的。

  1. 生成小的隨機數來初始化權重,比如說N(0,0.01)

    W = 0.01*np.random.randn(D, H)

    這種方法對於小網絡適用,但是對於層數比較深的網絡不行。每次乘上W,權重太小(0.01),會導致輸入越來越小,梯度也會越來越小,從而導致梯度彌散;權重過大(1),會導致輸入越來越大,如果使用tanh或sigmoid,過大的輸入會讓導數接近0,從而梯度彌散,無法訓練權重。

  2. Xavier,確保在xw前後數據的方差一致,那麼權重如下,其中n是輸入的維度數s=niwixi 。在tanh中有效,但是在ReLU中輸入到0更快,需要加上因子2

    w = np.random.randn(n) / sqrt(n)
    w = np.random.randn(n) * sqrt(2.0/n) # ReLU
    • 偏置一般初始化爲0,使用ReLU,權重初始化使用ReLU的初始化。

Batch Normalization

一般設置在全連接層或者卷積層的後面和非線性層(激活函數)的前面。

Batch Normalization論文地址
論文的中文整理博客

算法流程

在測試的時候依然使用如下式子:

x^=xE[x]Var[x]+ϵ

這裏的均值和方差已經不是針對某一個Batch了,而是針對整個數據集而言。因此,在訓練過程中除了正常的前向傳播和反向求導之外,我們還要記錄每一個Batch的均值和方差,以便訓練完成之後按照下式計算整體的均值和方差:
E[x]EB[μB]

Var[x]mm1EB[σ2B]

反向傳播的導數:

lx^ilσ2BlμBlxilγlβ======lyiγi=1mlx^i(xiμB)12(σ2B+ϵ)3/2(i=1mlx^i1σ2B+ϵ)+lσ2Bmi=12(xiμB)mlx^i1σ2B+ϵ+lσ2B2(xiμB)m+lμB1mi=1mlyix^ii=1mlyi

作者在文章中說應該把BN放在激活函數之前,這是因爲Wx+b具有更加一致和非稀疏的分佈。但是也有人做實驗表明放在激活函數後面效果更好。這是實驗鏈接,裏面有很多有意思的對比實驗。

  1. 提高了整個網絡的梯度流
  2. 可以設置更高的學習率
  3. 減少了對初始值的依賴
  4. 有一定正則化的作用,減少了對Dropout的需要

損失函數

損失是爲了量化預測值和實際值之間的差距,差距越大損失越大。數據損失L是所有樣本損失的平均,L=1NiLi ,縮寫f是網絡的輸出,我們的預測值。對於不同的問題有集中不同的輸出。

分類問題

只有一個正確答案的分類問題,常用的有svm和softmax損失函數。

Li=jyimax(0,fjfyi+1)

Li=logefyijefj

大量類別的,比如說英文字典,或者ImageNet包含了22000個類別。可以使用分層的Softmaxpdf here。先創建一棵類別樹,每一個節點用於softmax,樹的結構對分類效果影響很大,需要具體問題具體分析。

屬性分類,一個樣本有好幾個分類屬性,也有可能一個也沒有。有兩種計算方法,其一是給每個屬性單獨的設置一個二元分類器(類似於svm)。

Li=jmax(0,1yijfj)

其二是給每個屬性單獨的設置一個邏輯斯特迴歸分類器(類似於softmax)。
P(y=1x;w,b)=11+e(wTx+b)=σ(wTx+b)

loss是對概率的對數最大似然估計如下。
Li=jyijlog(σ(fj))+(1yij)log(1σ(fj))

其中σ 是sigmoid函數。對其求導結果:Li/fj=yijσ(fj)

迴歸問題

迴歸問題是預測實值,例如通過房子的面積來預測房子的價格。常見的通過L1或者L2範式來衡量預測值和實際值之間的差距。

Li=fyi22

Li=fyi1=jfj(yi)j

Note:L2損失比Softmax損失更難優化,因爲需要對樣本的每個值都準確的預測一個正確值,包括異常值。在遇到一個迴歸問題的時候,首先考慮是否可以轉化爲分類問題,比如說給某個產品星級評分1-5星,用5個分類器可能比迴歸更好。

正則化方法

用於控制網絡的容量,不讓模型過於複雜,從而防止過擬合,增加模型的泛化能力。

L2正則化

最常用的正則化形式。在計算損失loss的時候,加上12λW2 作爲最後的損失,在反向傳播計算權重w導數的時候,根據求導法則加上對正則項的導數λW 。乘上0.5是爲了求導的時候格式好看。L2正則項是爲了能夠充分利用各個特徵項。

L1正則化

給損失加上λ|w| 。也可以同時加上L1和L2正則項。L1正則化項得到的權重會更加稀疏,起到一個類似於特徵選擇的效果,選擇重要的特徵進行預測,而濾去噪聲。如果沒有做過特徵選擇,那麼用L1會有更好的效果。

Max norm constraints

最大範數約束主要思路是安照正常進行update,只不過每次更新之後檢查其範數是否大約了設定值||w⃗ ||2<x 的經典取值是3或者4。

Dropout

一種很有效、簡單的正則化技術,Dropout: A Simple Way to Prevent Neural Networks from Overfitting,基本思想是訓練的時候選擇一部分的神經元工作。
Standard neural Net
After applying dropout
神經元以P或者0的概率決定是否被使用,測試的時候全部的神經元都是可用的。

一個3層神經網絡的例子

""" Vanilla Dropout: Not recommended implementation (see notes below) """

p = 0.5 # probability of keeping a unit active. higher = less dropout

def train_step(X):
  """ X contains the data """

  # forward pass for example 3-layer neural network
  H1 = np.maximum(0, np.dot(W1, X) + b1)
  U1 = np.random.rand(*H1.shape) < p # first dropout mask
  H1 *= U1 # drop!
  H2 = np.maximum(0, np.dot(W2, H1) + b2)
  U2 = np.random.rand(*H2.shape) < p # second dropout mask
  H2 *= U2 # drop!
  out = np.dot(W3, H2) + b3

  # backward pass: compute gradients... (not shown)
  # perform parameter update... (not shown)

def predict(X):
  # ensembled forward pass
  H1 = np.maximum(0, np.dot(W1, X) + b1) * p # NOTE: scale the activations
  H2 = np.maximum(0, np.dot(W2, H1) + b2) * p # NOTE: scale the activations
  out = np.dot(W3, H2) + b3

上面我們沒有對輸入層進行dropout(也可以做),輸出層加入了p這一乘積項,這是因爲訓練的時候某個神經單元被使用的概率是p,如果他的輸出是x那麼他訓練時輸出值的期望只是px,所以在預測階段要乘以p,但是並不推薦這樣做。因爲在預測階段進行操作無異於增加了預測時間。我們用inverted dropout來解決這個問題。在預測時不再乘以p,而是在預測時除以p。

""" 
Inverted Dropout: Recommended implementation example.
We drop and scale at train time and don't do anything at test time.
"""

p = 0.5 # probability of keeping a unit active. higher = less dropout

def train_step(X):
  # forward pass for example 3-layer neural network
  H1 = np.maximum(0, np.dot(W1, X) + b1)
  U1 = (np.random.rand(*H1.shape) < p) / p # first dropout mask. Notice /p!
  H1 *= U1 # drop!
  H2 = np.maximum(0, np.dot(W2, H1) + b2)
  U2 = (np.random.rand(*H2.shape) < p) / p # second dropout mask. Notice /p!
  H2 *= U2 # drop!
  out = np.dot(W3, H2) + b3

  # backward pass: compute gradients... (not shown)
  # perform parameter update... (not shown)

def predict(X):
  # ensembled forward pass
  H1 = np.maximum(0, np.dot(W1, X) + b1) # no scaling necessary
  H2 = np.maximum(0, np.dot(W2, H1) + b2)
  out = np.dot(W3, H2) + b3

Bias regularization

bias不與輸入變量直接相乘,不能控制數據對最終目標的影響,所以表示一般不用regularization,但是它的數量相對於w很少,約束下也不會有太大的影響。

Per-layer regularization

用的很少,對不同的層使用不同的正則化方法。

實際使用

  1. 最常見的是交叉驗證單獨使用全局的L2正則化
  2. 將L2正則化配合dropout也比較常見,其中p的值一般默認是0.5,可以在驗證集調參

網絡學習

前面介紹了網絡的連接結構、數據預處理以及損失函數,這一節介紹學習參數的過程和超參數調參。

梯度檢驗

通過比較網絡中公式計算的梯度和數值法得到的梯度是否相似,來判斷網絡的梯度部分是否正確。

數值法計算梯度

df(x)dx=f(x+h)f(x)h

通過泰勒展開可以知道下面的公式誤差更小。
df(x)dx=f(x+h)f(xh)2h

相對誤差

對於不同的問題,梯度的尺度不一樣,所以用相對誤差來衡量兩者的誤差。

fafnmax(fa,fn)

原來的公式分母是兩者之一,但是爲了保證ReLU分母不爲0,所以就取兩者的最大值。
  1. relative error > 1e-2 一般來說梯度計算出了問題
  2. 1e-2 > relative error > 1e-4 可能有問題了
  3. 1e-4 > relative error不光滑的激勵函數來說時可以接受的,但是如果使用平滑的激勵函數如 tanh nonlinearities and softmax,這個結果還是太高了。
  4. 1e-7 通過

需要注意的是網絡越深誤差越大,如果網絡有10層,那麼1e-2也可以接受。

  • 使用double雙精度,降低精度會使誤差變大
  • 注意浮點數的活動範圍, “What Every Computer Scientist Should Know About Floating-Point Arithmetic”。這篇文獻介紹的浮點型,可以糾正一些錯誤。數字過小會導致很多錯誤,比如說梯度1e-10,那麼計算數值誤差和解析誤差的時候就會出問題。
  • kinks 目標函數的不光滑,Kinks是指目標函數不能完全可微的情況可以由之前我們提到的ReLU 、SVM loss,Maxout 等。如果我們要檢驗的是在x=−1e6時的ReLU的梯度。x<0時analytic gradient一定爲0。然而如果我們區的h稍微大一點就會使f(x+h)跨過0點,數值法得到的梯度就不會爲0了,這種情況經常發生。神經網絡和SVM分類會因爲ReLU出現更多的kink。我們可以通過跟蹤max(x,y)中較大的一個判斷是否有kink出現如果在向前計算的時候x或者y最大,但是在計算 f(x+h) 和 f(x−h)時至少有一個的最大值變化了,說明存在kink了。
  • 使用少量的樣本數據可以避免kink,同時提高梯度檢驗的效率
  • 步長h越小越小,但是不能太小,否則會有數值精度問題,一般設置爲1e-4或者1e-6。維基百科中介紹了h和數值梯度誤差之間的關係
  • 我們的梯度檢查中,檢查的是一部分的點,並不是這些點正確就代表全局正確,我們必須選擇儘量典型的點作爲檢查的對象。另外隨機初始化可能不是參數空間中最“特徵”的點,實際上可能會引入一些病態的情況,在這種情況下,梯度似乎得到了正確的實現,但並非如此。例如,一個帶有非常小的權重初始化的SVM將所有數據的分數幾乎都爲零。梯度的不正確實現仍然可以產生這種模式,並不能推廣到比其他分數更大的更有特徵的操作模式。
  • 正則項的梯度太大蓋過了數據的梯度從而導致梯度檢驗出錯。可以在梯度檢驗的時候關掉正則項,或者單獨檢查正則項的梯度,確認對數據的梯度影響。
  • 關掉dropout和數據增強,也可以設置隨機數種子
  • 實際中會有百萬級的參數梯度,一般檢查部分少量的維度,需要注意的是維度中每個獨立的參數都要檢驗

學習之前的完整性檢驗

  1. 用小的參數初始化參數,關掉正則化,單獨檢查損失函數的損失。比如CIFAR-10中使用Softmax分類器,預計初始損失爲2.303,因爲10個類,平均一個類的概率爲0.1,ln(0.1)=2.302 ,比如Weston Watkins SVM,預計初始損失爲9,因爲所有分數接近0,邊界爲1。如果得到的損失和預計的不一致,那麼初始化可能有錯誤
  2. 增加正則化係數,損失因爲會增加
  3. 過擬合少量數據(比如20個樣本)。關掉正則化,取少量樣本,訓練至損失爲0,說明網絡可以學習。當然就算過擬合了也可能有問題,比如說樣本特徵由於某些錯誤隨機了,仍然會過擬合,但是在泛化能力基本沒有。

Babysitting Learning process

有很多圖可以實時查看網絡的訓練狀態。下面的圖的x軸單位是epochs,表示數據集迭代的次數。因爲iterations(反向傳播次數)取決於batch size,所以epochs更有意義。

Loss function

一個形象的圖
實際的圖
學習率太小看着像是線性的,高的學習率剛開始是指數型的,太高的學習率最後的結果會不好。

  • 上下抖動和batch size有關,越大抖動越小。
  • 有人喜歡將其在log空間中繪圖,曲線可能會就更直一些
  • 可以將多個交叉驗證的模型一起繪製,可以直觀的看出不同點。

Train/Val accuracy

通過驗證集訓練集的準確率,可以看模型是否過擬合。

val/test accuracy-epoch
模型過擬合可以增加正則化、增加數據。如果驗證集準確率和訓練集準確率差不多,那麼表示模型規模不夠,通過增加參數數量來增大模型規模。

Ratio of weights:updates 權重更新率

觀察權重更新的快慢可以知道學習速度learning rate 設定的大小是否合適,下面是權重更新率計算的過程,一般來說在值小於1e-3的時候說明學習效率設定的太小,大於的時候說明設置的太大。

# assume parameter vector W and its gradient vector dW
param_scale = np.linalg.norm(W.ravel())
update = -learning_rate*dW # simple SGD update
update_scale = np.linalg.norm(update.ravel())
W += update # the actual update
print update_scale / param_scale # want ~1e-3

Activation/Gradient distributions per layers 每層的激活函數、梯度分佈

初始化出錯會降低甚至停止學習過程,可以通過網絡中每一層的激活輸出和梯度的直方圖來查看。比如說看到所有激活函數輸出都爲0,或者對於tanh激活函數,輸出不是-1就是1等。
不合理的分佈
不合理的分佈

First-layer Visualizations

如果是在處理圖片可以將第一層可視化:

下圖第二個是一個非常合理的結果:特徵多樣,比較乾淨、平滑。但是第一個圖很粗糙,顯示不出底層特徵,可能是因爲網絡不收斂或者學習速率設置不好或者是因爲懲罰因子設置的太小。


參數更新

本節介紹使用梯度來對參數進行更新的方法。

SGD,momentum,Nesterov momentum參數更新方法

Vanilla update:最簡單的方法,往負梯度方向更新參數。

# Vanilla update
x += - learning_rate * dx

learning_rate學習率是一個超參數。

Momentum update

Δxt=ρΔxt1ηgt

借鑑了物理中的慣性,把損失看成是凹凸不平的面,放一個小球,動量法就類似於小球在面上滾動。
# Momentum update
v = mu * v - learning_rate * dx # integrate velocity
x += v # integrate position

其中v初始值是0,mu是一個超參數,一般設爲0.9,也可以通過交叉驗證從 [0.5, 0.9, 0.95, 0.99]中選取,從上面的迭代過程中我們可以看到,相對於傳統的沿梯度方向更新的方法,這裏的更新是在之前的基礎上的更新,如果最後dx變爲0,經過多次乘以mu之後v也會變得非常小,也就是最後的停車,它保留了自然界中的慣性的成分,因此不容易在局部最優解除停止,而且中間有加速度所以會加速運算的過程。

Nesterov Momentum:動量法的改進版本,在凸函數收斂上有很好的理論保證,實際使用也比標準的動量法稍微好一點。之前我們採用v=muvlearningratedx 的方法計算增量,其中的dx還是當前的x,但是我們已經知道了,下一刻的慣性將會帶我們去的位置,所以現在我們要用加了mu*v之後的x,來更新位置,下面的圖很形象:
Momentum update
nesterov momentum update

x_ahead = x + mu * v
# evaluate dx_ahead (the gradient at x_ahead instead of at x)
v = mu * v - learning_rate * dx_ahead
x += v

用類似於前面SGD和動量的方法更新:

v_prev = v # back this up
v = mu * v - learning_rate * dx # velocity update stays the same
x += -mu * v_prev + (1 + mu) * v # position update changes form

Annealing the learning rate學習率衰減

在訓練網絡的時候,將學習速率進行退火衰減處理,一般會有些幫助。學習率衰減太快,會浪費太多時間,衰減太慢曲線波動大,而且達不到最優點。一般有3種衰減方法:

  1. 按步衰減(Step decay):每多少epoch減小學習率,典型的值是5epoch減小一半,或者20epoch減小0.1,主要取決於問題和模型的類型。以固定學習率訓練,如果看到驗證集誤差停止提升,就可以減小學習率,比如說減小一半
  2. 指數衰減(Exponential decay):α=α0ektα0,k 是超參數,t是迭代次數(或者epoch)
  3. 1/t衰減:α=α0/(1+kt) ,參數同2

1比較多一點,因爲超參數可以解釋。如果有足夠的資源,學習率可以小一點訓練更多時間。

Second order methods

Second order methods是基於牛頓法的二次方法,它的迭代公式如下:

xx[Hf(x)]1f(x)

其中, Hf(x)是Hessian matrix黑塞矩陣. 他是二階偏導數構成的方陣∇f(x)是梯度向量, 引入海森矩陣的逆陣可以使得在坡度陡時候減小學習步伐,坡度緩的時候加快學習步伐 ,值得注意的是這種方法並不涉及學習速率的超參數,這是相對於一階方法 first-order methods的優勢。

可是,hessian矩陣的計算成本太高,深度學習中的網絡有比較大,如果有100萬個參數,海森矩陣的size爲 [1,000,000 x 1,000,000], 需要3725G的RAM.
雖然現在有近似於hessian矩陣的方法例如L-BFGS,但是它需要在整個訓練集中進行訓練, 這也使得L-BFGS or similar second-order methods等方法在大規模學習的今天不常見的原因。如何是l-bfgs能像sgd一樣在mini-batches上比較好的應用也是現在一個比較熱門的研究領域。

自適應調整學習率的方法

前面的方法有的是通過別的超參數來調整學習率,以下幾種是自適應調整學習率的方法

Adagrad:借鑑L2正則化,只是不是調節W,而是梯度。

Δxt=ηtτ=1(gτ)2gt

注意到cache與梯度矩陣同形,他是一個針對每個參數的梯度迭代累加的矩陣,他作爲分母可以使在梯度大的時候學習效率速率降低,梯度小的時候學習速率增高,要注意的是開根號很重要的,沒了他效果會大打折扣。
# Assume the gradient dx and parameter vector x
cache += dx**2
x += - learning_rate * dx / (np.sqrt(cache) + eps)

其中eps一般取值1e-4 到1e-8,以避免分母出現0。但是這種方法用在深度學習中往往會學習的波動較大,並且使過早的停止學習。

RMSprop:Adagrad的改進,通過移動平均來減小波動。

cache = decay_rate * cache + (1 - decay_rate) * dx**2
x += - learning_rate * dx / (np.sqrt(cache) + eps)

改變就是分母中的cache,其中decay_rate是一個超參數,典型取值是[0.9, 0.99, 0.999]。cache的改變使這種方法有adagrad根據參數梯度自調整的優點,也克服adagrad單調減小的缺點。

Adam:有點像RMSProp+momentum

簡單實現

m = beta1*m + (1-beta1)*dx
v = beta2*v + (1-beta2)*(dx**2)
x += - learning_rate * m / (np.sqrt(v) + eps)

論文中推薦eps = 1e-8, beta1 = 0.9, beta2 = 0.999. 完整版的程序還包括了一個偏差修正值,以彌補開始時m,v初始時爲零的現象。

實際使用中,推薦Adam,他會比RMSProp效果好些. 也可以嘗試SGD+Nesterov Momentum。另外如果能夠允許全局的更新時可以試試L-BFGS。

更新版本,一般是用這個

# t is your iteration counter going from 1 to infinity
m = beta1*m + (1-beta1)*dx
mt = m / (1-beta1**t)
v = beta2*v + (1-beta2)*(dx**2)
vt = v / (1-beta2**t)
x += - learning_rate * mt / (np.sqrt(vt) + eps)

各種方法比較


超參數優化

常見的超參數:

  1. 初始學習速率
  2. 學習速率衰減策略(例如衰減係數)
  3. 正則項比例(L2正則,dropout)

還有其他很多相對不敏感的超參數,例如參數自適應學習方法、動量及其策略的設置。下面介紹一些超參數搜索的注意點:

  1. 大規模的網絡需要很長時間訓練,因此超參數搜索需要數天或者星期時間。在設計程序的時候需要注意兩個模塊,第一是能夠保存超參數和性能,在訓練階段,能夠追蹤每epoch驗證集性能的變化,保存在模型的檢查點中;其二是在集羣中能夠調度模塊1,能夠檢查模塊1的檢查點和繪製訓練數據等
  2. 只需要在一個驗證集進行交叉驗證,不需要“折”。(因爲數據集大,所以驗證集數據足夠完成評估的任務?)
  3. 超參數範圍。使用log尺度搜索超參數。一個典型的例子就是學習率。learning_rate = 10 ** uniform(-6, 1),因爲學習率如果是0.9-0.99,選中0.9-0.91和0.98-0.99的概率是一樣的,但是我們想在0.98-0.99概率更大一點,就用到了對數尺度。有些參數(比如dropout)就不需要這個(dropout = uniform(0,1))
  4. 隨機搜索比網格搜索效率更高
  5. 注意超參數範圍邊界的值是不是最優值
  6. 現在一個大的範圍搜索,然後縮小範圍。可以先訓練一個epoch看看效果,因爲有的參數設置會直接讓模型停止學習,然後縮小範圍訓練5epochs,最後在更精確的範圍搜素更多的epochs
  7. 利用Bayesian方法進行超參數優化。很多人在探究,現在已經有了一些工具箱比如Spearmint, SMAC, 及Hyperopt.但是在 ConvNets 上表現還是不如給定間隔的隨機選擇

模型集成

在測試階段,綜合一些神經網絡的結果,來提高性能。模型越多樣,提升效果越好。

  1. 不同初始值的同一個模型。用交叉驗證確定最好的超參數以後,設置不同的初始值得到不同的模型
  2. 在交叉驗證確定最優超參數的時候,挑選前幾個(10)模型來構成集成。這種方法提高了模型多樣性,但是可能會帶入一些參數次優的模型。實際中這種方法容易使用,因爲不需要額外的再訓練
  3. 一個模型的不同檢查點。如果訓練代價太大,那麼就可以用這種。雖然缺少了模型多樣性,但是用起來還算好,而且成本低
  4. 平均訓練參數。另外一個比較容易得到的模型就是copy下網絡的參數,然後用指數下降的方式求訓練過程中的平均,最終得到了最後幾次迭代爲主的模型,這樣的模型經常會有些比較好的表現。一種直觀的解釋是在一個碗狀的目標函數,我們得到結果經常在最下方的附近進行跳躍,而用平均值就會增加了更接近碗底的機會

模型集成的缺點是在測試集上太耗時。

完成Softmax和一個簡單的神經網絡

http://cs231n.github.io/neural-networks-case-study/

參考資料

  1. 視頻課程地址
  2. 官方資料網站
  3. 課後作業參考
  4. 神經網絡激活函數
  5. 訓練深度神經網絡儘量使用zero-centered數據
  6. Vectorized、PCA和Whitening
  7. 講義總結:梯度檢驗 參數更新 超參數優化 模型融合博客
  8. 參數更新方法博客
  9. Batch Normalization論文地址
  10. 論文的中文整理博客
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章