前言
梯度下降算法現(xiàn)在變的越來越流行,但是對于使用者來說,它的優(yōu)化過程變的越來越黑盒。本文我們介紹下不通梯度下降算法的習(xí)性,使得我們能夠更好的使用它們。本人每次復(fù)習(xí)這篇論文,或多或少都有一些收獲,基礎(chǔ)學(xué)習(xí)扎實了,后面的使用才會得心應(yīng)手。
簡介
梯度下降算法,不管在機器學(xué)習(xí),還是在神經(jīng)網(wǎng)絡(luò)中,都是很常見的優(yōu)化算法。所以不通的深度學(xué)習(xí)框架實現(xiàn)了各種不通的梯度下降算法,但是各種算法的優(yōu)勢和劣勢對于普通的使用者來說是一個黑盒。
本文就是基于此來詳細(xì)解釋各種梯度算法的不同點,以及它們的優(yōu)勢和劣勢。
梯度算法簡單來說就是:
優(yōu)化目標(biāo)函數(shù)$J(\theta)$, 使得目標(biāo)函數(shù)達(dá)到最小值時,求解得到參數(shù)$\theta \in R^d$
它的方式是首先求解梯度 $\nabla_{\theta} J(\theta)$, 然后每次往負(fù)梯度方向迭代一步,更新參數(shù)$\theta$.
各種梯度下降
目前常用的有3種梯度下降,主要是根據(jù)計算梯度時使用的數(shù)據(jù)量。
batch gradient descent(BGD, 批量梯度下降)
每次計算梯度的時候都是使用全部的數(shù)據(jù):
http://latex.codecogs.com/svg.latex?\begin{bmatrix}1&x&x2\1&y&y2\1&z&z^2\\end{bmatrix}
http://latex.codecogs.com/svg.latex?a
這里可以看到每一步的參數(shù) $\theta$ 更新都需要計算全部的數(shù)據(jù),
所以BGD在數(shù)據(jù)量特別大的時候是很難處理的,同時也不能做到online更新model。
BGD在凸優(yōu)化里面能夠保證達(dá)到全局的最優(yōu)點,如果在非凸優(yōu)化里面能夠達(dá)到局部最優(yōu)點。
Stochastic gradient descent(SGD, 隨機梯度下降)
SGD每次接收到一個訓(xùn)練數(shù)據(jù)$(x^{i}, y^{i})$時,都需要計算梯度,
然后更新模型參數(shù),粒度非常小。
$\theta = \theta - \eta . \nabla_{\theta}J(\theta;x{i};y{i})$
SGD在大數(shù)據(jù)應(yīng)用上效果非常好,一方面它避免像BGD一樣冗余的計算,因為每一次計算它都會去更新參數(shù),所以收斂速度會快很多,而且它是實時更新參數(shù),所以可以用作online 訓(xùn)練,但是SGD在訓(xùn)練的時候,loss的波動可能很大。
BGD最終會收斂到最優(yōu)點,相對來說SGD是有波動的,一方面,它有機會跳出當(dāng)前的局部最優(yōu)點;另一方面,它可能會造成過擬合。但是只要保持學(xué)習(xí)率緩慢下降的情況下,不管在凸優(yōu)化還是非凸優(yōu)化上面,SGD和BDG的效果可以等價。
Mini-batch gradient descent(MBGD)
MBGD是 BGD 和SGD的一個折中,它是每一個小批次計算一次梯度,更新一次參數(shù)
$$\theta = \theta - \eta . \nabla_{\theta}J(\theta;x{(i:i+n)};y{(i:i+n)})$$
它的好處
- 減少參數(shù)的更新次數(shù),使得模型更佳穩(wěn)定。
2)可以使用矩陣計算的形式,來優(yōu)化計算速度。
常見的Mini-batch 大小一般是 50-256,但是和應(yīng)用有關(guān),比如圖像應(yīng)用的batch小點,nlp任務(wù)的batch可以調(diào)整大些。
挑戰(zhàn)
雖然MBGD還可以,但是它不是凸優(yōu)化,有些問題需要處理
- 合適的學(xué)習(xí)率很難選擇,太小了迭代慢,太大了很容易波動,甚至發(fā)散
- 預(yù)設(shè)學(xué)習(xí)率,比如:模擬退火,學(xué)習(xí)率衰減等,但是這個對數(shù)據(jù)集需要非常了解
- 對于每一步迭代,所有的參數(shù)更新是共享同一學(xué)習(xí)率的,但是如果我們數(shù)據(jù)稀疏,或者特征頻次很不同,我們不需要更新每一個參數(shù),對較少出現(xiàn)的特征更新幅度較大。
- 在神經(jīng)網(wǎng)絡(luò)中優(yōu)化高度非凸的代價函數(shù)的一個挑戰(zhàn)是,避免局部的次最優(yōu)。這個難題不是局部最優(yōu)點帶來的,而是碰到非局部極值點的駐點,在某些維度它的梯度是上升的,在某些維度,它的梯度是下降的。這些鞍點的從每個方向出發(fā),它附近的error值都是一樣的,這就使得SGD非常難以從這樣的地方迭代出來。
梯度下降優(yōu)化算法
下面我們介紹多種優(yōu)化梯度的算法,這里不介紹不適合大數(shù)據(jù)集和二次的算法。
Momentum(動量,慣性)
SGD在做梯度下降的時候,梯度陡的維度下降的幅度比梯度歡的維度更大,這會導(dǎo)致一個問題,在一個局部最優(yōu)點的附近,更新的方式是有點類似走“之”字行的緩慢前進(jìn)。如圖a所示,垂直方向的梯度很陡,水平的很緩,這樣在更新的時候,往垂直方向的更新粒度大,但是水平方向很緩慢。
[圖片上傳失敗...(image-4dfee6-1511578384244)]
為了解決這個問題,引入Momentum,也就是引入動量來抑制震蕩,如圖b
$v_t = \gamma v_{t-1} + \eta \nabla_{\theta}J(\theta)$
$\theta=\theta-v_t$
通常$\gamma$設(shè)為0.9
它是通過保持上一步的梯度來實現(xiàn)抑制震蕩,效果如b所示。
物理上來說,當(dāng)我們從山上扔一個球,隨著球的下落,動量是一直在增加的,
速度也是越來越快的,直到極限速度,$\gamma$代表阻力因子。同樣的場景
適合解釋參數(shù)的更新,如果梯度的方向相同,動量$v_t$是增加的,相反,
動量是降低的,這樣就可以起到加速和抑制震蕩的效果。
Nesterov accelerated gradient(Nesterov 加速梯度, NAG)
然而,如果一個球任由它自己盲目的沿著斜坡下落,結(jié)果是很不理想的,因為球在坡緩地地方下落的慢,在坡陡的地方下落的快,這個和我們迭代參數(shù)的理想情況是相反的。我們期望的是在坡陡(梯度大)的地方迭代更慢。
NAG是解決這個問題的一種方式,使用動量對我們的參數(shù)本身做一些修正,我們先預(yù)估參數(shù)應(yīng)該變成什么樣子?
按照慣性前進(jìn),我們會有有個參數(shù)update 后的預(yù)估位置,
它可能是:$\theta-\gamma v_{t-1}$,
然后在這個位置我們計算它的梯度,$\eta \nabla_{\theta}J(\theta-\gamma v_{t-1})$,
這樣可以起到加速的作用。
整體的公式:
$$v_t = \gamma v_{t-1} + \eta \nabla_{\theta}J(\theta-\gamma v_{t-1})$$
$$\theta=\theta-v_t$$
[圖片上傳失敗...(image-44a45-1511578384244)]
解釋下這個圖:
原來梯度的方向:
如果按當(dāng)前的參數(shù)位置$\theta$來計算梯度,它是小的藍(lán)色的向量
然后保持動量$\gamma v_{t-1}$是長的藍(lán)色向量
NAG:
先保持動量走一步:$\gamma v_{t-1}$為棕色的長向量,和長的藍(lán)色向量平行
在該位置的梯度為紅色的長向量:$\eta \nabla_{\theta}J(\theta-\gamma v_{t-1})$
修正后的位置為左邊綠色向量指定的位置。
這種方式就可以提前修正梯度方向,避免單次走的太快以致偏差太大,起到加速的作用
既然我們可以做到自適應(yīng)的根據(jù)梯度大小來調(diào)整迭代的大小來加速計算。
我們也應(yīng)該做到根據(jù)參數(shù)的重要性來獨立的更新每一個參數(shù)。
Adagrad
Adagrad是一種梯度下降的算法:它是通過學(xué)習(xí)率來作用到參數(shù)更新,非頻繁更新的參數(shù)更新力度大些,相反更新力度小點。
所以這個非常實用于sparse數(shù)據(jù),
Dean et al 發(fā)現(xiàn)Adagrad在訓(xùn)練大數(shù)據(jù)集的時候可以極大的提升SGD的魯棒性,
用Adagrad去訓(xùn)練Glove word Embedding 對于新詞或者出現(xiàn)頻率低的詞效果很好
前面我們對所有的參數(shù)$\theta$共享一個學(xué)習(xí)率,Adagrad對于每個$\theta_i$都會訓(xùn)練出一個對應(yīng)的學(xué)習(xí)率。
下面是計算過程:
首先計算參數(shù)$\theta_i$在step t的梯度:
$g_{t,i}=\nabla_{\theta_t}J(\theta_{t,i})$
普通的SGD對于每一個參數(shù),更新公式為:
$\theta_{t+1,i}=\theta_{t,i}-\eta.g_{t,i}$
在這里Adagrad 對學(xué)習(xí)率$\eta$進(jìn)行修正,這要視基于它過去已經(jīng)計算過的梯度:
$\theta_{t+1,i}=\theta_{t,i}-\frac{\eta}{\sqrt{G_{t,ii} + \epsilon}}.g_{t,i}$
$G_t \in R^{d x d}$ 是一個對角矩陣,只用對角的$i,i$有數(shù)據(jù)
$G_{t,ii}=\sum_{0,t} g_{t,i}^2 $
所以這個值會一直越來越大
$\epsilon$是個平滑極小項,避免出現(xiàn)除0的情況,一般設(shè)為1e-8.
很奇怪的是,如果不加平方計算,效果很差。
使用Adagrad的一個好處是,不需要很特意的去調(diào)整學(xué)習(xí)率,
只要設(shè)置一個初始的$\eta$比如0.01,然后就可以了,模型自己會調(diào)整。
它的確定很明顯,需要計算累積的梯度平方和,這個值會越來越大,
這回是的步長一段時間后變得很小,這樣再訓(xùn)練下去就沒有什么意義了。
下面的方法來fix這個缺陷。
Adadelta
Adadelta是Adagrad的擴(kuò)展,它主要是決絕Adagrad的過度下降學(xué)習(xí)率。它們的不同是Adagrad累積所有歷史的梯度平方,但是Adadelta只是考慮過去一段是時間的梯度平方和。
為了避免每次計算都需要存儲歷史$w$個梯度,做了一種變種的方式,
有點類似衰減 decay:
$E[g^2]t = \gamma E[g^2]{t-1} + (1-\gamma)g_t^2$
同樣$\gamma$可以設(shè)置為0.9,我們將整體公式列出來:
SGD 更新參數(shù):
$\Delta \theta_t=-\eta.g_{t,i} $
$\theta_{t+1}=\theta_t + \Delta \theta_t $
Adagrad更新參數(shù)的element-wise 方式:
$\Delta \theta_t=-\frac{\eta}{\sqrt{G_{t} + \epsilon}}\bigodot g_{t}$
Adadelta就是將$G_t$改為 $E[g^2]_t $,得到
$\Delta \theta_t=-\frac{\eta}{\sqrt{E[g^2]t + \epsilon}}\bigodot g{t}$
其實 $E[g^2]_t $就是RMS(root mean squared)
$\Delta \theta_t=-\frac{\eta}{RMS(g)t}\bigodot g{t}$$
這個作者提到,像SGD,Momentum, 或者 Adagrad)的更新單元其實不適合Adadelta,
其實即使梯度再大,只要學(xué)習(xí)率很小,那么參數(shù)更新的幅度就很小,所以學(xué)習(xí)率的調(diào)整應(yīng)該和參數(shù)的過去一段時間的調(diào)整幅度有關(guān),而不是只是和梯度有關(guān),所以我們引入另外一個累積公式為:
$E[\Delta \theta ^2]t = \gamma E[\Delta \theta ^2]{t-1} + (1-\gamma)\Delta \theta ^2$$
$$RMS[\Delta]_t=\sqrt{E[\Delta\theta^2]_t + \epsilon}$
這里$RMS[\Delta]t$依賴$\Delta \theta_t$是未知的,我們用$RMS[\Delta]{t-1}$近似代替
然后用 $\frac{RMS[\Delta \theta]{t-1}}{RMS[g]{t}}$ 來替代步長$\eta$
物理上可以這么理解:
$\Delta \theta_t = \eta g_t$
$\eta = \frac{\Delta \theta_t}{g_t}$ 然后用$RMS[\Delta]{t-1}$近似$\Delta \theta_t$,用$RMS[g]{t}近似 $g_t$
最終得到:
$\Delta \theta_t=-\frac{RMS[\Delta \theta]{t-1}}{RMS[g]{t}} g_{t}$
$\theta_{t+1}=\theta_t + \Delta \theta_t $
所以用這種算法,我們連初始值都不需要了。
RMSprop
RMSprop是一種非公布的算法,是Hinton提出的
RMSprop 和 Adadelta是獨立提出的相同算法,他們都是用來解決Adagrad累積梯度平方和太快的問題:
$E[g^2]t= 0.9 E[g^2]{t-1} + 0.1 g_t^2 $
$\theta_{t+1}=\theta_{t} - \frac{\eta}{\sqrt{E[g^2]_t + \epsilon}}g_t$
建議 $\eta$設(shè)置為 0.001
Adam
Adaptive Moment Estimation 是兩外一種自適應(yīng)學(xué)習(xí)率算法,類似RMSprop 和 Adadelta,Adam還保存過去一段時間的梯度:
$m_t=\beta_1m_{t-1}+(1-\beta)g_t$
$v_t=\beta_2 v_{t-1}+(1-\beta_2)g_t^2$
$v_t$和上面的RMSprop類似,$m_t$是額外需要記憶的東西,
他們分別可以看成是第一動量(平均),第二動量(方差),
因為$v_t$和$m_t$都是由0初始化的,Adam的作者觀察:
在0附近特別是開始的幾步,偏置非常大,特別是衰減率非常小的時候,
$\beta_1和\beta_2是接近1的數(shù)字$。
為了修正這個偏置,作者稍微對這兩個動量進(jìn)行修正,得到:
$m_t^1 = \frac{m_t}{1-\beta_1^t}$
$v_t^1 = \frac{v_t}{1-\beta_2^t}$
這里的$\beta_1^t$是$\beta_1的t次方$
然后利用這個$m_t^1$去更新參數(shù):
$\theta_{t+1}=\theta_t-\frac{\eta}{\sqrt{v_t^1} + \epsilon}m_t^1 $
作者建議:$\beta_1=0.9$, $\beta_2=0.999$, $\epsilon=10^{-8}$
原始論文:https://arxiv.org/pdf/1412.6980.pdf
[圖片上傳失敗...(image-acdba3-1511578384244)]
AdaMax
上面提到的$v_t$計算$g_t^2$的時候用的其實是l2范數(shù)計算,可以寫成形式:
$v_t=\beta_2 v_{t-1}+(1-\beta_2)|g_t|^2$
我們可以定義更加廣泛的形式:
$v_t=\beta_2^p v_{t-1} + (1-\beta_2p)|g_t|p$
這里使用的的是p范數(shù)。
這里我們引入一種無窮范數(shù)的形式,AdaMax:
$u_t=\beta_2^{\infty}v_{t-1} + (1-\beta_2\infty)|g_t|\infty$
$=max(\beta_2.v_{t-1}, |g_t|)$
我們用$u_t$來代替Adam的$\sqrt(v_t^1 + \epsilon)$,得到:
$\theta_{t+1}=\theta_t-\frac{\eta}{u_t}m_t^1 $
$u_t$計算的時候取最大值,所有在接近于0的時候不會像Adam一樣有這么大的偏置。
Nadam
前面提到,Adam 是RMSprop和動量的結(jié)合算法,RMSprop保證學(xué)習(xí)率會根據(jù)最近幾步的梯度平方來衰減,這里我們再坐下升級,對動量使用NAG(Nesterov accelerated gradient),
首先我們回顧下動量的更新方式:
Momentum:
$g_t=\nabla_{\theta_t}J(\theta_t)$
$m_t=\gamma m_{t-1}+\eta g_t$
$\theta_{t+1}=\theta_t-m_t$
$J$是我們的目標(biāo)函數(shù),$\gamma$是動量的衰減因子,$\eta$是步長,結(jié)合起來:
$\theta_{t+1}=\theta_t-\gamma m_{t-1}-\eta g_t$
NAG可以令我們走的更加精確,我們將NAG的特性加入上式得到:
NAG:
$g_t=\nabla_{\theta_t}J(\theta_t-\gamma m_{t-1})$
$m_t=\gamma m_{t-1} + \eta g_t$
$\theta_{t+1}=\theta_t-m_t$
NAG可以做些修改,因為NAG中會迭代動量兩次,
一次是計算$g_t$的時候,一次是更新參數(shù)$\theta$的時候.修改后也是兩次,
只是動量更新兩次,一次時上面的第二個式子,預(yù)估當(dāng)前的動量,
然后沿著這個步子,再更新一次的動量,整體的式子為:
$g_t=\nabla_{\theta_t}J(\theta_t)$
$m_t=\gamma m_{t-1} + \eta g_t$
$\theta_{t+1}=\theta_t-(\gamma m_t + \eta g_t)$
這樣其實是起到加速的作用,這個我們也可以作用到Adam上面,
首先回顧下Adam算法:
$m_t=\beta_1 m_{t-1}+(1-\beta_1)g_t$
$m_t1=\frac{m_t}{1-\beta_1t}$
$\theta_{t+1}=\theta_t - \frac{\eta}{\sqrt{v_t^1} + \epsilon }m_t^1 $
把這些式子合并成一個式子:
$\theta_{t+1}=\theta_t -\frac{\eta}{\sqrt{v_t^1} + \epsilon } (\frac{\beta_1 m_{t-1}}{1-\beta_1^t} + \frac{(1-\beta_1)g_t}{1-\beta_1^t})$
我們注意到 $\frac{m_{t-1}}{1-\beta_1t}$就是$m_{t-1}1$,我們簡化為
$\theta_{t+1}=\theta_t -\frac{\eta}{\sqrt{v_t^1} + \epsilon } (\beta_1 m_{t-1}^1 + \frac{(1-\beta_1)g_t}{1-\beta_1^t})$
這里我們像上面加速一樣,將上一步的$m_{t-1}^1$動量,
直接用現(xiàn)在的預(yù)估來代替,起到加速的作用,很簡單的替換,得到:
$\theta_{t+1}=\theta_t -\frac{\eta}{\sqrt{v_t^1} + \epsilon } (\beta_1 m_{t}^1 + \frac{(1-\beta_1)g_t}{1-\beta_1^t})$
算法可視化
[圖片上傳失敗...(image-315d2a-1511578384244)]
a. 這是一個loss 曲面,每種算法的起始點是一樣的,每個算法的優(yōu)化路線是不一樣的,可以看到,Adagrad, Adadelta, 和 RMSprop 很快的就開始下降,momentum和 NAG就走了很多的彎路。
b. 展示的是算法如何逃離鞍點的情況,這種鞍點啃可能是,一個維度的梯度是正的,其他維度的梯度是負(fù)的。這種情形對于SGD來說是很難逃離的 ,圖中就可以看出,SGD,Momentum,NAG都很難突破,Adagrad, RMSprop, 和 Adadelta 很快找到負(fù)梯度方向了。
該怎么選擇
如果數(shù)據(jù)分布很稀疏,最好選擇自適應(yīng)學(xué)習(xí)率的算法
RMSprop,Adadelta,Adam都是很相似的自適應(yīng)學(xué)習(xí)率的算法,總體來說Adam效果是最好的。
很有趣的是,最近很多論文都是直接用SGD,沒有使用動量和學(xué)習(xí)率調(diào)節(jié),像上面說的那樣,SGD可以找到一個最小值,但是它可能很慢,對初始值的要求更高,也可能在鞍點就停止了。
反正如果在更深或者更復(fù)雜的網(wǎng)絡(luò),最好使用自適應(yīng)學(xué)習(xí)率的算法。
并行和分布式SGD
SGD的替代是逐步的,所以原始的SGD是很難做到并行的,下面的算法是關(guān)于SGD的并行和分布式化的。
Hogwild!
Hogwild!: A Lock-Free Approach to Parallelizing Stochastic Gradient Descent.它是一種多CPU的并行處理方式,共享一塊內(nèi)存,這些內(nèi)存是不加鎖的,這個適合于數(shù)據(jù)參數(shù)比較稀疏,每個部分只更新內(nèi)存的一個獨立部分,
Downpour SGD
Downpour SGD是一種異步SGD,Google在Large Scale Distributed Deep NetworksDistBelief的時候提出來的,他們把數(shù)據(jù)分為幾個部分,然后一個模型跑多份,然后每個模型將他們的參數(shù)發(fā)送給 parameter server。這個server可能是多機器的,每個機器復(fù)雜存儲和更新一部分參數(shù)。但是他們每一個模型之間是沒有通信和數(shù)據(jù)交換的,這樣會帶來鼓勵和阻礙達(dá)到最優(yōu)。
延時容忍的SGD
Delay-Tolerant Algorithms for Asynchronous Distributed Online Learning論文中擴(kuò)展了AdaGrad,使它成為一種可容忍延時和并行的算法,據(jù)說不錯。
TensorFlow
TensorFlow是google的開源機器學(xué)習(xí)框架,它的分布式結(jié)構(gòu),主要是將graph分成子圖的模式,然后通過rpc的機制通信,傳遞參數(shù)更新。
SGD的其他優(yōu)化
訓(xùn)練數(shù)據(jù)亂序
使得模型不刻意去學(xué)習(xí)數(shù)據(jù)輸入的順序,特別是序列模型的時候
batch normalization
對每個min-batch 進(jìn)行歸一化,可以使我們更加自如的控制學(xué)習(xí)率,使得對初始輸入不是很敏感,它還可以其他還可以起到正則化的作用。
Early stop
loss 下降如果基本不動,就早點停止吧
梯度噪音
對梯度加個噪音(比如正態(tài)分布的噪音):
$g_{t,i}=g_{t,i} + N(0, \sigma_t ^2)$
$\sigma_t2=\frac{\eta}{(1+t)\gamma}$
加上這個噪音,會使得網(wǎng)絡(luò)更加的穩(wěn)定,還有可能更有機會逃離局部最優(yōu)。