1. 引言
最優化問題是計算數學中最爲重要的研究方向之一。而在深度學習領域,優化算法的選擇也是一個模型的重中之重。即使在數據集和模型架構完全相同的情況下,採用不同的優化算法,也很可能導致截然不同的訓練效果。
梯度下降是目前神經網絡中使用最爲廣泛的優化算法之一。爲了彌補樸素梯度下降的種種缺陷,研究者們發明了一系列變種算法,優化算法經歷了 SGD -> SGDM -> NAG -> AdaGrad -> AdaDelta -> Adam -> NAdam
這樣的發展歷程。從最初的 SGD (隨機梯度下降) 逐步演進到 NAdam。然而,許多學術界最爲前沿的文章中,都並沒有一味使用 Adam/NAdam 等公認“好用”的自適應算法,很多甚至還選擇了最爲初級的 SGD 或者 SGD with Momentum 等。
本文旨在梳理深度學習優化算法的發展歷程,並在一個更加概括的框架之下,對優化算法做出分析和對比。
2. Gradient Descent
梯度下降是指,在給定待優化的模型參數 和目標函數 後,算法通過沿梯度 的相反方向更新 來最小化 。學習率 決定了每一時刻的更新步長。對於每個epoch ,我們可以用下述步驟描述梯度下降的流程:
(1) 計算目標函數關於參數的梯度
(2) 根據歷史梯度計算一階和二階動量
(3) 計算當前時刻的下降梯度(步長) ,並根據下降梯度更新模型參數
其中, 爲平滑項,防止分母爲零,通常取 。
注意:本文提到的步長,就是下降梯度。
3. Gradient Descent 和其算法變種
根據以上框架,我們來分析和比較梯度下降的各變種算法。
4. Vanilla SGD
樸素 SGD (Stochastic Gradient Descent) 最爲簡單,沒有動量的概念,即
這時,更新步驟就是最簡單的
缺點:
- 收斂速度慢,而且可能會在溝壑的兩邊持續震盪
- 容易停留在一個局部最優點
- 如何合理的選擇學習率也是 SGD 的一大難點
5. SGD with Momentum
爲了抑制SGD的震盪,SGD-M認爲梯度下降過程可以加入慣性(動量) Momentum[3],加速 SGD 在正確方向的下降並抑制震盪。就好像下坡的時候,如果發現是陡坡,那就利用慣性跑的快一些。其在SGD的基礎上,引入了一階動量,然後進行更新:
即在原步長(SGD中是) 之上,增加了與上一時刻動量相關的一項 ,目的是結合上一時刻步長(下降梯度), 其中 是上一時刻的動量, 是動量因子。也就是說, 時刻的下降方向,不僅由當前點的梯度方向決定,而且由此前累積的下降方向決定, 通常取 0.9 左右。這就意味着下降方向主要是此前累積的下降方向,並略微偏向當前時刻的下降方向。這使得參數中那些梯度方向變化不大的維度可以加速更新,並減少梯度方向變化較大的維度上的更新幅度。由此產生了加速收斂和減小震盪的效果。
圖 1(a): SGD | 圖 1(b): SGD with momentum |
從圖 1 中可以看出,引入動量有效的加速了梯度下降收斂過程。
6. Nesterov Accelerated Gradient
SGD 還有一個問題是困在局部最優的溝壑裏面震盪。想象一下你走到一個盆地,四周都是略高的小山,你覺得沒有下坡的方向,那就只能待在這裏了。可是如果你爬上高地,就會發現外面的世界還很廣闊。因此,我們不能停留在當前位置去觀察未來的方向,而要向前一步、多看一步、看遠一些。
NAG全稱Nesterov Accelerated Gradient,則是在SGD、SGD-M的基礎上的進一步改進,算法能夠在目標函數有增高趨勢之前,減緩更新速率。我們知道在時刻 的主要下降方向是由累積動量決定的,自己的梯度方向說了也不算,那與其看當前梯度方向,不如先看看如果跟着累積動量走了一步後,那個時候再怎麼走。因此,NAG在步驟1,不計算當前位置的梯度方向,而是計算如果按照累積動量走了一步,那個時候的下降方向:
參考圖2理解:最初,SGD-M先計算當前時刻的梯度(短藍向量)和累積動量 (長藍向量)進行參數更新。改進的方法NAG,先利用累積動量計算出下一時刻的 的近似位置 (棕向量),並根據該未來位置計算梯度(紅向量)公式(1),然後使用和 SGD-M 中相同的方式計算當前時刻的動量項(下降梯度),進而得到完整的參數更新(綠向量),公式(2)即將該未來位置的梯度與累積動量計算當前時刻的動量項(下降梯度):
KaTeX parse error: No such environment: equation at position 8:
\begin{̲e̲q̲u̲a̲t̲i̲o̲n̲}̲
\begin{split}
…
更新參數:
這種計算梯度的方式可以使算法更好的「預測未來」,提前調整更新速率。
注意:
累積動量指的是上一時刻的動量乘上動量因子:
當前時刻的動量項指的是:
(上面的圖只是爲了助於理解,其中累積動量
7. Adagrad
SGD、SGD-M 和 NAG 均是以相同的學習率去更新 的各個分量 。而深度學習模型中往往涉及大量的參數,不同參數的更新頻率往往有所區別。對於更新不頻繁的參數(典型例子:更新 word embedding 中的低頻詞),我們希望單次步長更大,多學習一些知識;對於更新頻繁的參數,我們則希望步長較小,使得學習到的參數更穩定,不至於被單個樣本影響太多。
Adagrad在 時刻對每一個參數 使用了不同的學習率,我們首先介紹 Adagrad 對每一個參數的更新,然後我們對其向量化。爲了簡潔,令 爲在 時刻目標函數關於參數 的梯度:
在 時刻,對每個參數 的更新過程變爲:
對於上述的更新規則,在 時刻,基於對 計算過的歷史梯度,Adagrad修正了對每一個參數 的學習率:
其中, 是對角矩陣,其元素 爲參數 第 維從初始時刻到 時刻的梯度平方和。即通過引入二階動量,來調整每一個參數的學習率,等效爲
由於 的對角線上包含了關於所有參數 的歷史梯度的平方和,現在,我們可以通過 和 之間的元素向量乘法⊙向量化上述的操作:
Adagrad算法的一個主要優點是無需手動調整學習率。在大多數的應用場景中,通常採用常數0.01。通過引入二階動量,對於此前頻繁更新過的參數,其二階動量的對應分量較大,學習率就較小。這一方法在稀疏數據的場景下表現很好。但也存在一些問題:因爲 是單調遞增的,會使得學習率單調遞減至0,可能會使得訓練過程提前結束,即便後續還有數據也無法學到必要的知識。
8. AdaDelta
Adadelta
是 Adagrad
的一種擴展算法,以處理Adagrad學習速率單調遞減的問題。考慮在計算二階動量時,不是計算所有的歷史梯度平方,而只關注最近某一時間窗口內的下降梯度,Adadelta將計算計算曆史梯度的窗口大小限制爲一個固定值
在 Adadelta
中,無需存儲先前的 個平方梯度,而是將梯度的平方遞歸地表示成所有歷史梯度平方的均值。在 時刻的均值 只取決於先前的均值和當前的梯度(分量 類似於動量項):
我們將 設置成與動量項相似的值,即0.9左右。爲了簡單起見,我們利用參數更新向量 重新表示SGD的更新過程:
我們先前得到的 Adagrad
參數更新向量變爲:
現在,我們簡單將對角矩陣 替換成歷史梯度的均值 :
由於分母僅僅是梯度的均方根(root mean squared,RMS)誤差,我們可以簡寫爲:
作者指出上述更新公式中的每個部分(與SGD,SDG-M 或者Adagrad)並不一致,即更新規則中必須與參數具有相同的假設單位。爲了實現這個要求,作者首次定義了另一個指數衰減均值,這次不是梯度平方,而是參數的平方的更新:
因此,參數更新的均方根誤差爲:
由於 是未知的,我們利用參數的均方根誤差來近似更新。利用 替換先前的更新規則中的學習率 ,最終得到 Adadelta
的更新規則:
使用 Adadelta
算法,我們甚至都無需設置默認的學習率,因爲更新規則中已經移除了學習率。
9. RMSprop
RMSprop
是一個未被髮表的自適應學習率的算法,該算法由Geoff Hinton在其Coursera課堂的課程6e中提出。
RMSprop和Adadelta在相同的時間裏被獨立的提出,都起源於對Adagrad的極速遞減的學習率問題的求解。實際上,RMSprop
是先前我們得到的 Adadelta
的第一個更新向量的特例:
同樣,RMSprop
將學習率分解成一個平方梯度的指數衰減的平均。Hinton建議將 設置爲0.9,對於學習率 ,一個好的固定值爲0.001。
10. Adam
Adam 可以認爲是 RMSprop 和 Momentum 的結合。和 RMSprop 對二階動量使用指數移動平均類似,Adam 中對一階動量也是用指數移動平均計算。
其中,初值
注意到,在迭代初始階段, 和 有一個向初值的偏移(過多的偏向了 0)。因此,可以對一階和二階動量做偏置校正 (bias correction),
再進行更新,
可以保證迭代較爲平穩。
11. NAdam
這裏還沒有太看懂,先把上面的優化算法完全理解了再說!
NAdam 在 Adam 之上融合了 NAG 的思想。
首先回顧 NAG 的公式,
NAG 的核心在於,計算梯度時使用了「未來位置」。NAdam 中提出了一種公式變形的思路,大意可以這樣理解:只要能在梯度計算中考慮到「未來因素」,即能達到 Nesterov 的效果;既然如此,那麼在計算梯度時,可以仍然使用原始公式 ,但在前一次迭代計算 時,就使用了未來時刻的動量,即 ,那麼理論上所達到的效果是類似的。
這時,公式修改爲,
理論上,下一刻的動量爲 ,在假定連續兩次的梯度變化不大的情況下,即 ,有 。此時,即可用 近似表示未來動量加入到 的迭代式中。
類似的,在 Adam 可以加入 的變形,將 展開有
引入
再進行更新,
即可在 Adam 中引入 Nesterov 加速效果。
12. 選擇使用哪種優化算法
那麼,我們應該選擇使用哪種優化算法呢?如果輸入數據是稀疏的,選擇任一自適應學習率算法可能會得到最好的結果。選用這類算法的另一個好處是無需調整學習率,選用默認值就可能達到最好的結果。
總的來說,RMSprop是Adagrad的擴展形式,用於處理在Adagrad中急速遞減的學習率。RMSprop與Adadelta相同,所不同的是Adadelta在更新規則中使用參數的均方根進行更新。最後,Adam是將偏差校正和動量加入到RMSprop中。在這樣的情況下,RMSprop、Adadelta和Adam是很相似的算法並且在相似的環境中性能都不錯。Kingma等人[9]指出在優化後期由於梯度變得越來越稀疏,偏差校正能夠幫助Adam微弱地勝過RMSprop。綜合看來,Adam可能是最佳的選擇。
有趣的是,最近許多論文中採用不帶動量的SGD和一種簡單的學習率的退火策略。已表明,通常SGD能夠找到最小值點,但是比其他優化的SGD花費更多的時間,與其他算法相比,SGD更加依賴魯棒的初始化和退火策略,同時,SGD可能會陷入鞍點,而不是局部極小值點。因此,如果你關心的是快速收斂和訓練一個深層的或者複雜的神經網絡,你應該選擇一個自適應學習率的方法。
13. 可視化分析
圖 3: SGD optimization on loss surface contours | 圖 4: SGD optimization on saddle point |
圖 3 和圖 4 兩張動圖直觀的展現了不同算法的性能。(Image credit: Alec Radford)
圖 3 中,我們可以看到不同算法在損失面等高線圖中的學習過程,它們均同同一點出發,但沿着不同路徑達到最小值點。其中 Adagrad、Adadelta、RMSprop 從最開始就找到了正確的方向並快速收斂;SGD 找到了正確方向但收斂速度很慢;SGD-M 和 NAG 最初都偏離了航道,但也能最終糾正到正確方向,SGD-M 偏離的慣性比 NAG 更大。
圖 4 展現了不同算法在鞍點處的表現。這裏,SGD、SGD-M、NAG 都受到了鞍點的嚴重影響,儘管後兩者最終還是逃離了鞍點;而 Adagrad、RMSprop、Adadelta 都很快找到了正確的方向。
關於兩圖的討論,也可參考[2]和[8]。
可以看到,幾種自適應算法在這些場景下都展現了更好的性能。
14. Reference
- https://blog.csdn.net/google19890102/article/details/69942970
- 翻譯的一篇博客
- 一個框架看懂優化算法之異同 SGD/AdaGrad/Adam
- 從 SGD 到 Adam —— 深度學習優化算法概覽(一)
- http://cs231n.github.io/neural-networks-3/
- An overview of gradient descent optimization algorithms [paper]
- 上面博客看不懂就參考這篇博客:An overview of gradient descent optimization algorithms [blog]
- https://www.cnblogs.com/xinchrome/p/4964930.html
- http://www.cs.toronto.edu/~tijmen/csc321/slides/lecture_slides_lec6.pdf
- https://zhuanlan.zhihu.com/p/27449596
- https://juejin.im/entry/5983115f6fb9a03c50227fd4
- https://www.cnblogs.com/denny402/p/5074212.html
- https://zhuanlan.zhihu.com/p/27449596