1. 程式人生 > >BP神經網路的各種優化演算法

BP神經網路的各種優化演算法

loss一直在波動,沒有收斂的趨勢,以為自己訓練有問題,

後來看了SGD的介紹,再瞭解這屬於正常的,具體看下面介紹吧。

=====================================================================================

梯度下降是最流行的優化演算法之一併且目前為止是優化神經網路最常見的演算法。與此同時,每一個先進的深度學習庫都包含各種演算法實現的梯度下降(比如lasagne'scaffe's, 和 keras'的文件)。然而,這些演算法經常作為黑盒優化程式使用,所以難以感受到各種演算法的長處和不足。

此博文旨在為你提供對不同梯度演算法的直觀感受,以期會幫助你更好地使用不同的梯度下降演算法。首先,我們會羅列各種梯度下降演算法的變種並簡單地總結演算法訓練階段的挑戰。然後,我們會通過展示解決問題的動機和依據這些動機來推導更新法則,以介紹最常見的優化演算法。我們也順帶羅列下並行分散式環境下的演算法和體系結構。最後,我們會討論其他有利於梯度下降優化演算法的策略。

梯度下降是一種以通過在目標函式梯度 的反向上更新模型引數,來最小化模型引數的目標函式的方法。學習速率 決定了我們前往(區域性)極小值的步長。換言之,我們沿著目標函式所構造麴面的斜面按向下的方向走動,直到我們到達山谷。如果你對梯度下降不熟悉,你可以點選此處點選開啟連結去了解一篇關於優化神經網路的介紹。

梯度下降演算法變種

存在三種梯度下降的變種,他們不同之處在於我們在計算目標函式梯度時所用資料量的多少。依據資料的規模,我們在更新引數的準確性和執行一次更新所用時間之間進行一種折中。

批量梯度下降

普通的梯度下降,也稱批量梯度下降,利用所有的訓練資料計算目標函式的梯度。

                                                                                             

由於我們每進行一次引數更新需要計算整體訓練資料的梯度,批量梯度下降會變得很慢並且一遇到記憶體吃不下資料就掛了。同時批量梯度下降也無法支援模型的線上更新,例如,新的樣本不停的到來。

程式碼中,批量梯度下降大概長這樣:

  1. for i in range(nb_epochs):  
  2.   params_grad = evaluate_gradient(loss_function, data, params)  
  3.   params = params - learning_rate * params_grad  

對於一個預先定義迭代輪數,我們首先以整體資料計算損失函式的梯度向量weights_grad關於引數向量params。值得注意的是先進的

深度學習庫提供對一些引數進行自動求導可以有效地計算梯度。如果你是自己來推梯度,梯度檢查是一個不錯的注意。(點選點選開啟連結檢視關於如何正確地檢查梯度的建議)

我們隨後以梯度的反方向更新我們的引數,此時學習速率決定著我們每次執行多大的更新。批量梯度下降可以保證在convex error surfaces 條件下取得全域性最小值,在non-convex surfaces條件下取得區域性極小值。

隨機梯度下降

隨機梯度下降(SGD)以一個訓練樣例和標籤進行一次引數更新。

                                                                                  

由於在每次引數更新前對相似的樣例進行梯度重複計算, 批量梯度下降會在大資料集上進行冗餘計算。SGD通過每次計算一個樣例的方式避開這種冗餘。因此,SGD速度會更快並支援線上學習。

SGD頻繁的執行更新所伴隨的高方差(a high variance)會導致目標函式如圖1所示的劇烈波動。

 圖1. SGD波動

批量梯度下降收斂到盆面的極小值,SGD的波動一方面能夠使(損失函式)跳到一個全新並且可鞥呢更優的區域性極小值,另一方面這種波動由於一直overshooting終究會很難收斂到確切的極小值。然而,(實驗)表明當我們慢慢地減小學習速率時SGD表現出和批量梯度下降同樣的收斂行為,幾乎確定地在non-convex and convex optimization中各自收斂到一個區域性或者全域性極小值在。

SGD的程式碼片段簡單在訓練例項上套一個迴圈並評估每一個訓練樣例的梯度。值得注意的是我們在每輪迭代時候會打亂訓練資料,相關解釋見點選開啟連結

  1. for i in range(nb_epochs):  
  2.   np.random.shuffle(data)  
  3.   for example in data:  
  4.     params_grad = evaluate_gradient(loss_function, example, params)  
  5.     params = params - learning_rate * params_grad  

Mini-batch gradient descent

Mini-batch gradient descent 吸收了上述兩個演算法的長處,利用小批量訓練樣例執行一次更新。

                                                                                     

以這種方式,它可以 a) 減小引數更新的方差,導致更平穩的收斂。b) 利用先進深度學習庫中常見的高度優化矩陣操作來高效地計算小批量的梯度。普通小批量的規模在50到256之間,但在不同的應用中會變化。Mini-batch gradient desent 是訓練神經網路的經典選擇,採用mini-bathes時常常也會稱為SGD。注意在後文中SGD改進中,為簡單起見,我們省略引數

程式碼中,我們每輪迭代的mini-bathes規模設定為50。

  1. for i in range(nb_epochs):  
  2.   np.random.shuffle(data)  
  3.   for batch in get_batches(data, batch_size=50):  
  4.     params_grad = evaluate_gradient(loss_function, batch, params)  
  5.     params = params - learning_rate * params_grad  

挑戰

然而,普通的mini-batch gradient descent不能保證較好的收斂性,這一點引出了下述挑戰:

  1. 選擇一個合適的學習速率是很難的。學習速率太小會導致收斂慢,太大會阻礙收斂並導致損失函式在極小值周圍波動甚至背離。
  2. 嘗試通過設定排程在訓練時候調整訓練速率,比如,模擬退火按照預先定義好的排程演算法或者當相鄰的迭代中目標變化小於一個閾值時候減小學習速率。但是這些排程和閾值需要預先設定,無法對資料集特徵進行自適應。
  3. 除此之外,對所有的引數更新都按照同一個學習速率也是一個問題。如果我們的資料很稀疏並且我們的特徵出現的次數不同,我們可能不會希望所有的引數以某種相同的幅度進行更新,而是針對很少出現的特徵進行一次大幅度更新。
  4. 在神經網路中常見的極小化highly non-convex error functions的一個關鍵挑戰是避免步入大量的suboptimal local minima。Dauphin等人認為實踐中的困難來自saddle points而非local minima。所謂saddle points是指那些維度梯度不一致的點。這些saddle points經常被一個相等誤差的平原包圍,導致SGD很難擺脫,因為梯度在所有方向都近似於0。

注意:關於saddle points討論就後面附加部落格3

梯度下降優化演算法

下面我們會概述一些深度學習社群廣泛採用的以解決上述挑戰的演算法。我們不會討論那些實踐中對高維資料集合計算上不可行的演算法,比如二階方法(比如牛頓法)。

Momentum

SGD不那麼容易越過ravines,所謂ravines也就是那些surface curves在某個維度比其他維度梯度大得多的地方,這些地方經常在區域性極小值周圍出現。在這種情況下,SGD會像圖2一般沿著slopes of the ravine振盪中前進,在底部磨磨蹭蹭地朝區域性最優走。

圖2. 不帶Momentum的SGD

圖3. 帶有Momentum的SGD

Momentum是一種幫助SGD在相關方向進行加速並抑制振盪的方法,如圖3所示。它通過向當前更新向量中加入上一時刻的更新向量的部分實現上述功能。

                                                                                 

                                                                                  

注意,有些實現中會對等式中的符號進行變動。momentum term  一般設定為0.9或類似值。

我們向山下丟一個小球就會涉及使用到momentum。小球在向山下滾動時候會積累動量滾動地越來越快(如果存在空氣阻力,會一直加速到終端速度terminal velocity)。相同的事情會發生在引數更新上:momentum term會在更新方向相同的維度加速,會在更新方向不同的維度上減速。最終,我們更快地收斂並減少振盪。

Nesterov accelerated gradient

然而,我們並不滿意於滾動的小球僅僅只是盲目地沿著斜面slope滾動。我們希望有一個更加聰明的小球,一個知道自己能否認識到自己選擇道路的小球,這種小球會在斜面slope再次上傾的時候放慢自己的速度。

Nesterov accelerated gradient (NAG) 是一種能夠給我們momentum term帶來這種先見之明的方法。我們清楚我們會使用momentum term 來更新引數。計算 會讓我們看到更新後引數的近似值(完整的更新還需要考慮梯度),讓我們大致知道引數朝那地方更新。我們現在可以通過計算下一個位置引數的梯度(而不是當前位置的引數) 進行提前準備: 

                                                                                   

                                                                                    

我們再次將momentum term  設定在0.9的附近。不同於Momentum方法先計算當面的梯度(圖4中藍色小向量)後在更新過的累積梯度方向上進行一個大跨越(藍色大向量),NAG先在上一個累積梯度方向進行跳躍(棕色向量),測量下梯度然後進行一個修正(綠色向量)。這種預期式的更新防止我們走的太快並增加反應能力,顯著提高了RNN在一些任務上的效能。

圖4. Nesterov更新

點選點選開啟連結參考對於NAG背後直覺的另外一種解釋,同時Ilya Sutskever在他博士論文中給出更詳細的綜述。

現在我們可以讓我們的更新適應於損失函式所構造的斜面slope的同時加快SGD的速度。我們也希望我們的更新適應於單獨的引數,按照引數自身的重要性來進行大幅度或者小幅度的更新。

Adagrad

Adagrad是一個基於梯度優化的演算法:它讓學習速率自適應於引數,對低頻引數進行大幅度更新而對高頻引數進行小幅度更新。因為這一點,它非常適合於處理稀疏資料。Dean等人發現Adagrad大大地提高了SGD的魯棒性並在谷歌的大規模神經網路訓練中採用了它進行引數更新,其中包含了在Youtube視訊中進行貓臉識別。此外,由於低頻詞(引數)需要更大幅度的更新,Pennington等人在GloVe word embeddings的訓練中也採用了Adagrad。

之前,我們每次更新都涉及所有的引數且每個引數  採用相同的速率。由於Adagrad在時刻 對每個引數  採用了不同的學習速率,我們先展示Adagrad的per-parameter 更新然後對他們進行向量化。簡答起見, 表示損失函式中引數  在時刻  的梯度。

                                                                                                                                     

SDG演算法時刻  對引數  進行更新則表示為:

                                                                                                   

在這個更新規則中,Adagrad利用過去時刻算好了的梯度對不同時刻  不同引數 的學習速率  進行調整:

                                                                                                    

是一個對角矩陣,其中每個對角元素是引數  到時刻  為止所有時刻梯度的平方之和, 是平滑項以避免分母為0。有趣的是,如果不用平方根操作,演算法效能會變差很多。

由於  的對角包含著所有引數過去時刻的平方之和,我們可以通過在  和   執行element-wise matrix-vector multiplication來向量化我們的操作:

                                                                                                     

Adagrad的一個優點就是不用進行人工調整學習速率。許多實現中都是使用預設的0.01進行賦值。

Adagrad的一個主要弱點就是它在分母中累加梯度的平方:由於每次加入的是一個正數,累加的和在訓練階段一直增加。這會導致學習速率一直變小最終變得無限小,在學習速率無限小的地方Adagrad演算法無法取得額外的知識。下面這個演算法就是為了克服這個缺陷而產生的。

Adadelta

Adadelta是Adagrad的一種擴充套件,以緩解Adagrad學習速率單調遞減問題的演算法。Adadelta不是對過去所有時刻的梯度平凡進行累加,而是將累加時刻限制在視窗大小為的  區間。

梯度累加沒有采用簡單的儲存前個時刻的梯度平方,而是遞迴使的定義為過去所有時刻梯度平方的decaying average。時刻  的running average  僅僅依賴於之前average和當前的梯度:

                                    

類似momentum term,我們將  取值在0.9附近。 簡潔起見,我們從引數更新向量 角度重寫普通SGD的引數更新:

                                                                                            

                                                                                             

Adagrad中我們推導的引數更新向量現在就以下述形式出現:

                                                                                              

現在我們簡單地將對角矩陣  替換為過去時刻梯度平方的decaying average 

                                                                                              

由於分母是root mean squared (RMS) error criterion of the gradient, 則上面公式可以替換為:

                                                                                               

[中間有些沒搞懂] 

作者發現(和SGD,Momentum或者Adagrad一樣)上述更新中的單元不匹配(我的個人理解是:只有部分引數進行更新),也就是引數和更新應該有著相同的hypothetical units。為了實現這個目的,他們首先定義了另外一個exponentially decaying average,這一次對更新引數的平方進行操作,而不只是對梯度的平方進行操作:

                                  

引數更新中的root mean squared error則為:

                                  

將以前的更新規則中的學習速率替換為引數更新的RMS,則得到Adadelta更新規則:

                                 

                                 

由於Adadelta更新規則中沒有了學習速率這一項,我們甚至都不用對學習速率進行設定。

RMSprop

RMSprop是一個沒有發表的工作,它是Geoff Hinton在他課上提出的一種自適應學習速率方法(adaptive learning rate method)。

RMSprop和Adadelta是在差不多的時間各自獨立產生的工作,目的都是為了緩解Adagrad的學習速率減少的問題。實際上RMSprop和我們在Adadelta中推到的第一個更新向量是相同的:

                                                                                

RMSprop也將學習速率除以梯度平方的exponentially decaying average. Hinton建議 設定為0.9,學習速率 設定為0.001。 

Adam

Adaptive Moment Estimation (Adam)是另外一種對每個引數進行自適應學習速率計算的方法。除了像Adeadelta和RMSprop一樣儲存去過梯度平方和的exponentially decaying average外,Adam還儲存類似momentum一樣過去梯度的exponentially decaying average。

                           

                           

 和 是分別是梯度的一階矩(均值)和二階矩(偏方差)的估計,這就是方法名稱的由來。由於 和 由全零的向量來初始化,Adam的作者觀察到他們會被偏向0,特別是在initial time steps或decay rates很小的時候(即 和 都接近於1)

他們通過計算bias-corrected 一階矩和二階矩的估計低消掉偏差。

                                                                           

                                                                           

然後他們使用上述項和Adadelta和RMSprop一樣進行引數更新,可以得到Adam的更新規則:

                                                                            

他們建議預設設定 為0.9,  為0.999, 為10e-8。他們經驗性地表明Adam能夠在實踐中很好地起作用並且和一點也不比其他的適應性學習演算法遜色。

演算法的視覺化

下面兩幅動畫讓我們直觀感受一些優化演算法的優化過程。

在圖5中,我們看到他們隨著時間推移在損失表面的輪廓(contours of a loss surface)的移動。注意到Adagrad、Adadelta和RMSprop幾乎立刻轉向正確的方向並快速收斂,但是Momentum和NAG被引導偏離了軌道。這讓我們感覺就像看滾下山的小球。然而,由於NAG擁有通過遠眺所提高的警惕,它能夠修正他的軌跡並轉向極小值。

圖5. 損失表面輪廓(loss surface contours)上SGD演算法表現

圖6展現了各種演算法在saddle point上的表現。所謂saddle point也就是某個維度是positive slope,其他維度為negative lope。前文中我們已經提及了它給SGD所帶來的困難。注意到SGD、Momentum和NAG很難打破對稱,雖然後兩者最後還是逃離了saddle point。然而Adagrad, RMSprop, and Adadelta很迅速地沿著negative slope下滑。 

圖6.saddle point上SGD演算法表現

正如我們所看到的,自適應學習速率的方法,即 Adagrad、 Adadelta、 RMSprop 和Adam,在這些場景下最合適並快速收斂。

採用哪種

現在,問題來了。你現在會採用哪種優化演算法呢?如果你的輸入資料是稀疏的,你更可能通過採用某種自適應學習速率方法來獲得最好的結果。這麼做另外一個好處是你不必去調學習速率,僅用預設值就可以取得最好的結果。

總而言之,RMSprop是Adagrad的一種針對處理Adagrad的學習速率減小的擴充套件。它和Adadelta是一樣的,唯一不同就是Adadelta使用在更新規則的分子中使用引數更新的RMS。Adam是將bias-correction and momentum加入到RMSprop中。RMSprop、Adadelta和Adam是很相似的演算法並且在相似的環境中效能都不錯。Kingma等人發現在優化後期由於梯度越來越稀疏,bias-correction幫助Adam微弱地擊敗RMSprop。綜合看來,Adam可能是最佳選擇。

有趣的是,最近很多論文都採用不帶momentum的普通SGD和一種簡單的模擬退火(annealing schedule)學習速率。SGD常常會達到極小值,但是花掉的時間比其他的演算法多得多。SGD在更加依賴於魯棒的初始化和模擬退火(annealing schedule)並可能被saddle points而不是區域性極小值困住。所以,如果你在意快速收斂或者在訓練一個很深很複雜的神經網路,你應該採用一種自適應學習速率方法。

並行式與分散式SGD

鑑於無處不在的大規模資料解決方案和低價可得的叢集,將SGD進行分佈以進一步加速是一個理所當然的選擇。

SGD自身是sequential的:我們逐步地朝著極值前進。SGD跑起來收斂性好但是在速度非常慢,尤其是在大資料集上。相反,非同步方式的SGD速度很快,但是workers之間次優的通訊會導致收斂較差。另外,我們也可以在一臺機器上將SGD並行,而不用大的計算叢集。下面是一些關於優化並行式和分散式SGD的演算法和體系結構。