入門神經網絡優化算法(一):Gradient Descent,Momentum,Nesterov accelerated gradient

梯度下降

基於梯度的優化算法,Gradient based optimization,也往往被稱爲一階優化算法。所以很容易猜到,還有二階優化算法等的高階優化算法,但是在實際應用中,基於梯度的一階優化算法是目前的絕對主流方法(當前是,2~3年以後未必),本文就重點羅列一下基於梯度的優化算法。

最典型以及簡單的是:梯度下降算法。梯度下降法是神經網絡求解優化中最常用的一類算法(實際上是在數值優化方法裏的一種常用方法,常常用以求解連續可微函數的局部極小值)。

  • gradient descent:梯度下降,又叫做Vanilla gradient descent,或者Batch gradient descent,特指用整個數據集來計算變量的梯度,它的形式是這樣
    θ=θηθJ(θ)\theta = \theta - \eta \cdot \nabla_\theta J( \theta)
    其中θ\theta是求解法變量或者叫weight,也叫parameter,JJ是cost function或者叫loss function,η\eta是學習率learning rate。相信很多人初學優化算法一定會看資料[1],其中有說道:Batch gradient descent is guaranteed to converge to the global minimum for convex error surfaces and to a local minimum for non-convex surfaces[1]。前半句global minimum for convex是對的,後半句不完全對,除了local minimum也可能會遇到鞍點(saddle point),以及大片的平坦區域(plateau)。

  • stochastic gradient descent:隨機梯度下降,最早提出來是相反於gd每次要用所有的數據,而每次更新只用一個樣本:
    θ=θηθJ(θ;x(i),y(i))\theta = \theta - \eta \cdot \nabla_\theta J( \theta; x^{(i)}, y^{(i)})
    最大的不同就是隻用一個樣本來計算梯度,這樣做的好處是計算一次梯度代價是O(1),但是往往需要反覆計算,而且一般variance比較大,學習率比較難調,容易震盪甚至不收斂。所以實際上我們平時用的最多的是mini-batch stochastic gradient descent,很容易想到,是一個折中方案——每次取一個小batch來計算梯度(平均梯度),比如常用的batch = 32,128,256等,通過一個batch既一定程度上減少了方差,也不會太多增加單次梯度計算cost。然後神奇的地方是,現在的神經網絡,只要喂進去數據,用一些default的學習率策略,比如multi-step(0.1),或者poly,總能夠收斂,真是香。當然,需要知道,現在很少有人直接用基本的SGD了(還是有些不穩定),而是用Momentum SGD,或者用Nesterov Momentum SGD,我個人覺得這兩個優化器纔是深度學習算法如此低門檻(好用)可用的關鍵,雖然說有一階需要調節的超參數,但是實際上都有一些default經驗值非常好用。

Momentum SGD,Nesterov accelerated gradient

先統一下叫法,我們指的Momentum SGD算法一般是指最簡單的Polyak‘s momentum(Boris Polyak在1964年提出),又名Heavy Ball算法。Nesterov‘s momentum也就是我們一般指的Nesterov accelerated gradient(NAG)。

  • Momentum SGD:動量SGD,最簡單的理解就是在梯度上做一個moving avg,如下形式:
    vt=mvt1ηθJ(θt)θt+1=θt+vt v_t = m v_{t-1} - \eta \nabla_\theta J( \theta_t) \\ \theta_{t+1} = \theta_t + v_t
    其中mm叫做momentum parameter, m[0,1)m \in [0,1),當 m=0m=0的時候,就退化成了原始的Vanilla Gradient Method (VGM)。η0\eta\ge0是學習率,在這裏假設mmη\eta都是常數,因此不寫下標了。注意的是,在不同的文獻或代碼中,有幾種實現方式,都是等價的。比如把加減號換下位置:
    vt=mvt1+ηθJ(θt)θt+1=θtvt v_t = m v_{t-1} + \eta \nabla_\theta J( \theta_t) \\ \theta_{t+1} = \theta_t - v_t
    或者把學習率寫在更新項上(第二行):
    vt=mvt1θJ(θt)θt+1=θt+ηvt v_t = m v_{t-1} - \nabla_\theta J( \theta_t) \\ \theta_{t+1} = \theta_t + \eta v_t
    當然這個是假設lr是一個常數,所以可以提出來,如果不是一個常數,形式上要想提出來就沒那麼簡單了。但是因爲lr本身也很經驗性去調,實際中好像也沒什麼關係。這裏我還要再推一個形式,是比較有意思的(看上面第一種形式):
    θt+1=θt+mvt1ηθJ(θt)θt+1=θtηθJ(θt)+m(θtθt1) \theta_{t+1} = \theta_t + m v_{t-1} - \eta \nabla_\theta J( \theta_t) \\ \theta_{t+1} = \theta_t - \eta \nabla_\theta J( \theta_t) + m (\theta_{t} - \theta_{t-1})
    這個的解釋看這一段很有啓發[3]:

其實momentum的中文就是高中物理學到的動量。之所以被稱作動量,是因爲使用momentum的初衷,就是模擬有質量的粒子在有阻尼的場裏運動軌跡 。空間內的反梯度方向就是場的作用力方向,粒子會從能量高的位置(初始解)向能量低的位置(最優解)移動,momentum parameter決定阻尼係數。因爲粒子有質量,所以具有慣性,而慣性的定性定義是爲物體抵抗“動量”改變的性質。換句話說,粒子具有保持靜止狀態或勻速直線運動狀態的性質,所以粒子的運動的瞬時方向,會是力的作用方向和軌跡切線方向的一個組合。
這個物理解釋可以用於理解MGM的加速效果,粒子從起點到距離終點 [公式] 距離的時間可以解釋爲算法的迭代複雜度(iteration complexity)。如果阻尼小,會使得粒子產生很高的移動速度,但過大的動量會使得粒子震盪,並非保持向終點移動。而當阻尼過大,又會導致粒子移動過於緩慢的話,會花很久時間接近終點,同時震盪會減小。那麼如何選擇最優的設置阻尼,這就是已有的大量文獻裏提供的參數選擇了。知道了這個簡單的物理解釋,其實就可以理解爲什麼momentum會被稱作overshoot了。因爲vanilla gradient method其實對應0質量粒子,所以粒子不具備慣性,會始終按作用力方向運動(加速度無窮大)。

很多帖子裏寫到動量SGD會用類似下面這個圖,說左邊是sgd,而右邊是帶動量的。其實這個圖的本意是說動量的會快一些,但是實際上對sgd的示意圖有問題。可以參考【3】進行理解,我這裏就不贅述了。
在這裏插入圖片描述

  • Nesterov‘s momentum:NAG算法是Yurii Nesterov在1983年提出的對沖量梯度下降算法的改進版本,其速度更快。其變化之處在於計算“超前梯度”更新衝量項
    vt=γvt1ηθJ(θt+γvt1)θt+1=θt+vt v_t = \gamma v_{t-1} - \eta \nabla_\theta J( \theta_t + \gamma v_{t-1} ) \\ \theta_{t+1} = \theta_t + v_t
    一個NAG計算的梯度的更新方向的示意圖如下:
    在這裏插入圖片描述

While Momentum first computes the current gradient (small blue vector) and then takes a big jump in the direction of the updated accumulated gradient (big blue vector), NAG first makes a big jump in the direction of the previous accumulated gradient (brown vector), measures the gradient and then makes a correction (red vector), which results in the complete NAG update (green vector).

以上,總結來看可以看下TensorFlow的描述https://tensorflow.google.cn/api_docs/python/tf/keras/optimizers/SGD?hl=en

v(t+1) = momentum * v(t) - learning_rate * gradient
theta(t+1) = theta(t) + v(t+1)
if `nesterov` is False, gradient is evaluated at theta(t).
if `nesterov` is True, gradient is evaluated at theta(t) + momentum * v(t),
    and the variables always store theta + m v instead of theta

本篇就到這裏,下一篇繼續。

參考資料

[1] https://ruder.io/optimizing-gradient-descent/index.html#gradientdescentvariants
[2] 數值優化(Numerical Optimization)學習系列-目錄
[3] 知乎:常見的關於momentum的誤解(上)
[4] 知乎:一文看懂常用的梯度下降算法
[5] 知乎:Nesterov加速和Momentum動量方法

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