前言
本文翻譯自 An overview of gradient descent optimization algorithms
概要
梯度優化算法,作為各大開源庫(如Tensorflow,Keras,PyTorch等)中重要的黑盒子,在網絡訓練中至關重要,擁有很強的魔力(實用性),但官網一般很少介紹各種梯度優化算法變種的原理。作者在這篇文章里,介紹了不同優化算法的基本原理,旨在讓讀者知道每個優化算法的利弊和適用的場景,也涉及了一些在並行化和分布式下的結構形式。
前言
對於優化神經網絡而言,梯度下降是目前最主流、最常見的算法,目前幾乎所有的開源庫都包含豐富的梯度優化算法,如Adam、Adagrad,SGD,RMSprop等。作者在文章依次介紹了當前常見的算法參數更新原理,並給出了自己的一些見解。
在給定神經網絡時,梯度下降被用來最小化目標函數J(θ),通過不斷往負梯度方向更新網絡參數。學習率η用來確定每次更新多大的梯度步長。換句話說,我們總是沿着目標函數的斜坡往下走,直到到達一個山谷(局部最優解或者最優解)。
梯度下降
Batch gradient descent
又稱Vanilla gradient descent,通過計算整個訓練樣本的梯度來更新參數θ:
因為我們需要計算所有的訓練數據的梯度,進行一次參數更新,因此BGD非常慢,無法處理內存放不下數據的情況,也無法進行在線更新。
for i in range(nb_epochs):
params_grad = evaluate_gradient(loss_function, data, params)
Params = params - learning_rate * params_grad
目前各大框架都支持梯度的自動求導,如果要自己實現求導,最好做一下梯度檢查。
隨機梯度下降(Stochastic gradient descent)
隨機梯度下降,每次選取一個樣本進行參數更新:
BGD存在着梯度冗余計算的問題——在每個參數更新之前,重復計算了相同的樣本梯度。SGD通常速度更快一些,可用於在線更新。但是因為SGD每次僅用一個樣本進行頻繁的參數更新,可能導致目標函數值震盪比較厲害,如下圖:
Mini-Batch gradient descent
MBGD結合了上述兩個基本算法的優點,每次借助batch_size個樣本進行參數更新:
- 降低了參數跟新的波動方差,保證穩定收斂
- 在計算batch樣本的梯度更新參數時,可以借助矩陣運算,非常高效
- batch_size一般在50~256,根據不同的任務取值不同
- batch是很經典的思想,在后文中提到的SGD,均指支持batch的SGD
for i in range(nb_epochs):
np.random.shuffle(data)
for batch in get_batches(data, batch_sizes=50):
params_grad = evaluate_gradient(loss_function, batch, params)
Params = params - learning_rate * params_grad
存在的挑戰
mini-batch梯度下降算法並不能保證能收斂到一個很好解上,主要面臨以下幾個問題:
- 很難選擇一個合適的學習率η。學習率太小則收斂特別慢,學習率太大,會導致在最優解附近震盪,甚至偏離最優解得到局部最優解
- 自適應學習率調度,能夠在訓練過程不斷調整學習率的大小。根據預定義的降級參數,可以訓練的不同階段,遞減的降低學習率的大小(衰減因子),但是在何時,以何種尺度衰減學習率,同樣是一個難題。
- 此外,所有的參數共享同一學習率η。如果訓練數據是稀疏的,特征具有不同的頻次,這時我們並不想以同樣的步長更新參數,更希望對很少出現的特征對應的參數,進行更大步長的更新。
- 另一個重要問題是,在神經網絡里常常是嚴重非凸的誤差函數,如果避免落入局部最優解?Dauphin等人認為局部最優解並不是最棘手問題——鞍點才是,在鞍點處,梯度接近為0,在用SGD時,很難保證能逃離鞍點區域。
優化算法
作者介紹了目前常見的優化算法,這里不討論在高維數據中無法技術實現的算法,如擬牛頓法。
動量法(Momentum)
SGD在目標函數的“峽谷”處常出現震盪難以快速收斂的問題,如下圖所示,在每一次更新時,損失函數會在峽谷處來回擺動。動量法的思想在於通過阻尼這種往復“擺動”,保持SGD在相對不變方便的動量,以加速收斂速度——抵消峽谷兩側的動量,維護向谷底運動的動量。
γ通常取0.9。實質上,當我們使用動量法時,就像從山上推下一個球,球會逐漸累積往山谷滾的動量,越滾越快。在參數更新中,動量會對指向相同方向的維度對應的參數進行持續更新,約束梯度方向總是改變的維度參數,盡可能引導損失函數向“谷底”收斂。
Nesterov accelerated gradient(NAG)
但是,讓“球”無腦地沿着斜坡向下滾並不總能得到讓人滿意的解。我們更希望有這樣的一個小球,它能夠對下一步怎么滾有個基本的“主見“,在滾過斜坡遇到上坡時,能夠減速,防止越過最優解。
NAG則在動量法的基礎上引入了”預測“的功能。在動量法中,我們用γv_{t-1}更新參數θ。通過計算θ-γv_{t-1}處的梯度,可以對下一個位置的走向做一個初步的預估:
依舊設γ在0.9左右。當動量法第一次計算當前梯度時(短藍線),並在累積梯度方向上進行了一大步參數更新(長藍線);NAG是首先在之前的梯度方向上走了一大步(棕色線),然后在此位置上評估一下梯度,最后做出一個相對正確的參數更新(綠線)。這種“先知”更新方法可以避免走過頭而逃離了最優解——這在RNN相關的各種任務中收益甚好。
以上的參數更新策略同樣可以適用於SGD。
Adagrad
Adagrad可以自適應的調節學習率:對於很少更新的參數采用較大的學習率,對於頻繁更新的參數采取更小的學習率——非常適合處理稀疏的數據。谷歌的Dean等人發現,在使用它訓練大規模神經網絡時,Adagrad很大程度上提升了SGD的魯棒性。同時,Penningto等人用它來訓練GloVe詞嵌入模型,因為低頻詞相對高頻詞需要更大的學習步長。
在之前的優化算法中,我們對所有的參數θ采取了相同的學習η。Adagrad在不同訓練的step t中,對不同的參數θi應用不同的學習率。我們先看每個參數是怎么更新的,在進行向量表示。為了簡要起見,令g(t,i)為在step t下參數θi的梯度:
SGD在每一步t對參數θi的更新:
Adagrad修正了上述的更新量,考慮了歷史梯度的信息:
其中Gt是一個對角矩陣,在(i, i)處的元素表示到step t時歷史θi的梯度平方和。分母下的常量是平滑因子(一般取1e-8)。值得注意的是,沒有平方根運算時,算法性能會很差。
Adagrad的好處在於,它避開了人工調參學習率的問題,絕大部分開源庫實現只需要在開始的時候設置η=0.01即可。
主要缺點在於它累積了歷史梯度作為分母:因為每一個新梯度平方后都是非負值,在訓練過程中,分母會越來越大,導致學習率整體會減小直至最后無限小——意味着算法不再能夠學習到新的知識。后續算法會解決這個問題的。
Adadelta
Adadelta是Adagrad的一種拓展形式——Adadelta僅考慮了一個歷史時間窗口w下的累積梯度(在實現上並非存儲歷史梯度,而是借助衰減因子),避免了Adagrad中學習率總是單調遞減的問題。
在t步時的平均梯度,取決於歷史累積梯度和當前梯度(衰減因子作為trade-off):
γ值類似之前動量法中的因子,通常取0.9。簡單期間,我們重寫一下SGD的參數更新公式:
在Adagrad優化算法中,上兩式變為:
我們把Gt替換為歷史梯度的衰減平方:
對於分母,剛好是梯度的均方根誤差,則上式變為:
作者指出:the units is this update(as well as in SGD, Momentum, or Adagrad) do not match, i.e. the update should have the same hypothetical units as the paramter. 因此,則定義了另一個指數衰減方式——參數平方更新,而非梯度的平方。
因此,參數更新的的均方根誤差為:
但是RMS[delt(θ)t]是未知的,我們借助先前step的參數更新的估計值逼近。則在之前的更新規則中用RMS[delt(θ)t-1]代替學習率:
在 Adadelta優化算法中,並不需要我們設置學習率的初始值,η會自動根據更新規則進行估計。
RMSprop
RMSprop是Hinton在他的Coursera課程上提出的(未公布)的自適應學習率的梯度優化方法。它和Adadelta關注和解決的問題是一樣的——學習率的單調遞減問題。
Hinton 建議γ設置為0.9,初始學習率建議設置為0.001
Adam
Adaptive Moment Estimation(Adam),是另一種針對每個參數自適應調整學習率η的優化算法。除了會衰減累積歷史的梯度平方和(類似Adadelta, RMSprop),Adam還會保存歷史梯度的衰減累積和,類似動量的概念:
Mt和vt分別是梯度的一階、二階估計,並在訓練開始時被初始為0,Adam的作者觀察到這兩值偏置接近0,尤其在初始化前后且兩個beta的取值接近1時(衰減率很小)。
計算兩個和梯度相關變量的無偏估計值:
進行參數更新:
作者提出beta1的值建議0.9,beta2的值建議0.999,平滑因子設置為1e-8。經驗上來講,在實際使用中Adam相對於其他自適應學習率優化算法,效果要更好一點
AdaMax
在Adam中vt的更新規則類似於在歷史的梯度上應用了L2正則:
我們可以基於此擴展成Lp正則:
但是p值取得過大可能導致數值不穩定——這也是為什么實際使用中L1和L2才比較常見。但是,當p=無窮大時也具有穩定行為。基於此,作者提出了AdaMax,為了避免與Adam混淆,我們使用ut表示無窮正則約束:
我們將ut代替之前Adam更新公式中vt的平方根:
需要指出的是,ut依賴於max操作,因此並不像Adam中的vt和mt那樣偏置接近於0,這也是為什么我們不要計算ut的bias correction的原因。建議η=0.002,beta1=0.9,beta2=0.999
Nadam
正如上述所見,Adam可以看做是RMSprop和動量法的結合,NAG是動量法的高階版本。而Nadam(Nesterov-accelerated Adaptive Moment Estimation)則可以看做是Adam和NAG的組合版本。為了把NAG整合進Adam里,我們需要修正一下動量mt的計算方式。
首先,我們先回顧下動量法的更新公式:
其中,J是目標損失函數,γ是動量衰減因子,η是學習率,上式可以整理為:
從式子中可以看出,動量法涉及兩個部分——歷史動量方向一小步和當前梯度方向一小步。NAG考慮了預測信息,能夠在當前梯度方向邁出更加精確的一步:
Dozat提出了一種方式修正NAG:相對於應用動量值兩次——①更新梯度gt②更新參數θt+1,我們現在應用一個前視(look-ahead)動量直接更新當前參數:
這里,相對於之前利用mt-1的向量,我們使用了當前向量mt做眺望。為了能夠把NAG動量加到Adam中,我們替換了先前的動量向量,首先,回憶一下Adam的更新規則:
將mt的表達式帶入最后一個公式里:
括號里的第一項是先前步驟的無偏估計,可以替換為:
考慮到look-ahead的動量,將mt-1替換為mt即可得到Nadam的更新規則:
可視化
所有的優化算法都是同樣的初始點出發,可以看出,Adagrad,Adadelta,RMSprop可以很迅速的沿着正確方向收斂,而動量法和NAG開始時會偏離正確收斂方向,但是NAG在后續的step中由於考慮到了預見信息,修正了方向。
在鞍點的表現:SGD,動量法和NAG很難突破鞍點的困境——盡管最后后兩者逃離了鞍點。但Adagrad,RMSprop和Adadelta能夠很好的快速找到最優負梯度防線,Adadelta拔得頭籌。
如何選擇
那么如何選擇合適的優化算法呢?
如果數據是稀疏的,你也許可以使用自適應學習率的算法,可幫助你獲取更好的性能,而且不需要手動調節學習率參數。
總的來說,RMSprop是Adagrad的擴展——在處理梯度單調衰減問題上。RMSprop,Adadelta,Adam在考慮歷史梯度方面,非常相似。目前,Adam是整體最好的選擇。
SGD雖然傾向於能找到最小值,但是花費的step要更大一些,對權重初始化比較敏感,不能輕易逃出鞍點,有陷入局部最優解的風險。如果你在訓練一個很深很復雜的神經網絡,又希望能快速收斂,建議選擇上述自適應學習率的優化算法。
並行和分布式下的SGD
TODO
優化SGD的其他技巧
我們討論一些除了優化算法之外的能提升模型性能的tircks。
Shuffling and Curriculum Learning
一般情況下,我們在訓練模型時,常常會打亂樣本的輸入順序——否則,模型可能會捕捉這種順序信息,最后得到有偏置的模型。
從另一方面來講,有時候我們目的在於逐漸學習越來越難得問題,這時我們可以先喂給模型相對容易區分學習的樣本,然后逐漸提升樣本區分難度——這稱為Curriculum Learning
Batch Normalization
為了促進學習過程,我們一般會對模型參數進行零均值單位方差的初始化,在訓練過程中,由於參數往不同方向更新,網絡層常會損失這種分布,會導致模型收斂較慢,尤其是網絡很深的時候。
Batch Normalization會對每個batch的樣本重建這種零均值單位方差的數據分布,同時隨着反向傳播進行適應改變。可以將BN作為網絡單獨的一層,這樣我們可以用更大的學習率而不用太過關心參數初始化問題。BN可以認為是一種正則手段,一般不和dropout一起使用。
Early Stopping
Hinton言:Early stopping is beautiful free lunch. 如果在訓練過程中,你的validation error不再下降時,就應該考慮終止訓練。
Gradient Noise
Neelakantan等人在每個梯度更新中添加了高斯噪音:
並對方差進行了退火處理:
他們指出通過添加高斯噪聲能夠提高模型在初始化不好時的魯棒性,幫助訓練復雜的網絡,認為高斯噪聲能夠幫助模型逃離局部最優解。
小結
作者在這篇文章中,介紹了各種梯度下降優化算法的變種——動量法,NAG,Adagrad,Adadelta,RMSprop,Adam,Nadam,Adamax等。