一、簡介
在很多機器學習和深度學習的應用中,我們發現用的最多的最佳化器是Adam,為什麼呢。
下面是 TensorFlow 中的最佳化器:
詳情參見:https://www.tensorflow.org/api_guides/python/train
在keras中也有SGD,RMSprop,Adagrad,Adadelta,Adam等,詳情:https://keras.io/optimizers/
我們可以發現除了常見的梯度下降,還有 Adadelta,Adagrad,RMSProp 等幾種最佳化器,都是什麼呢,又該怎麼選擇呢。
二、最佳化器演算法簡述
首先來看一下梯度下降最常見的三種變形 BGD,SGD,MBGD,
這三種形式的區別就是取決於我們用多少資料來計算目標函數的梯度,
這樣的話自然就涉及到一個trade-off,即參數更新的準確率和已耗用時間。
1. Batch gradient descent
梯度更新規則:
BGD 採用整個訓練集的資料來計算cost function對參數的梯度:
缺點:
由於這種方法是在一次更新中,就對整個資料集計算梯度,所以計算起來非常慢,遇到很大量的資料集也會非常棘手,而且不能投入新資料即時更新模型
for i in range(nb_epochs): params_grad = evaluate_gradient(loss_function, data, params) params = params - learning_rate * params_grad
我們會事先定義一個迭代次數 epoch,首先計算梯度向量params_grad,然後沿著梯度的方向更新參數params,learning rate決定了我們每一步邁多大。
Batch gradient descent對於凸函數可以收斂到全域極小值,對於非凸函數可以收斂到局部極小值。
2. Stochastic gradient descent
梯度更新規則:
和BGD 的一次用所有資料計算梯度相比,SGD 每次更新時對每個樣本進行梯度更新,
對於很大的資料集來說,可能會有相似的樣本,這樣BGD 在計算梯度時會出現冗餘,
而SGD一次只進行一次更新,就沒有冗餘,而且比較快,並且可以新增樣本。
for i in range(nb_epochs): np.random.shuffle(data) for example in data: params_grad = evaluate_gradient(loss_function, example, params) params = params - learning_rate * params_grad
看代碼,可以看到區別,就是整體資料集是個迴圈,其中對每個樣本進行一次參數更新。
缺點: 但是 SGD 因為更新比較頻繁,會造成cost function有嚴重的震蕩。
BGD可以收斂到局部極小值,當然 SGD的震蕩可能會跳到更好的局部極小值處。
當我們稍微減小 learning rate,SGD和 BGD的收斂性是一樣的。
3. Mini-batch gradient descent
梯度更新規則: MBGD每一次利用一小批樣本,即 n 個樣本進行計算,
這樣它可以降低參數更新時的方差,收斂更穩定,
另一方面可以充分地利用深度學習庫中高度最佳化的矩陣操作來進行更有效梯度計算。
和SGD的區別是每一次迴圈不是作用於每個樣本,而是具有 n 個樣本的批次
for i in range(nb_epochs): np.random.shuffle(data) for batch in get_batches(data, batch_size=50): params_grad = evaluate_gradient(loss_function, batch, params) params = params - learning_rate * params_grad
超參數設定值: n 一般取值在 50~256
缺點: 不過 Mini-batch gradient descent不能保證很好的收斂性: learning rate 如果選擇的太小,收斂速度會很慢,如果太大,loss function 就會在極小值處不停地震蕩甚至偏離。
(有一種措施是先設定大一點的學習率,當兩次迭代之間的變化低於某個閾值後,就減小 learning rate,不過這個閾值的設定需要提前寫好,這樣的話就不能夠適應資料集的特點) 此外,這種方法是對所有參數更新時應用同樣的 learning rate,如果我們的資料是稀疏的,我們更希望對出現頻率低的特徵進行大一點的更新。 另外,對於非凸函數,還要避免陷於局部極小值處,或者鞍點處,因為鞍點周圍的error 是一樣的,所有維度梯度都接近於0,SGD 很容易被困在這裡。
鞍點就是:一個光滑函數的鞍點鄰域的曲線,曲面,或超曲面,都位於這點的切線的不同邊。
例如下圖這個二維圖形,像個馬鞍:在x-軸方嚮往上曲,在y-軸方嚮往下曲,鞍點就是(0,0)
為了應對上述這三點挑戰,於是就有了下面這些演算法。
4. Momentum
SGD在 ravines 的情況下容易被困住, ravines 就是曲面的一個方向比另一個方向更陡,這時 SGD會發生震蕩而遲遲不能接近極小值:
梯度更新規則:
Momentum通過加入 γv_t−1,可以加速SGD, 並且抑制震蕩
當我們將一個小球從山上滾下來時,沒有阻力的話,它的動量會越來越大,但是如果遇到了阻力,速度就會變小。
加入的這一項,可以使得梯度方向不變的維度上速度變快,梯度方向有所改變的維度上的更新速度變慢,這樣就可以加快收斂並減小震蕩。
超參數設定值: 一般 γ 取值 0.9 左右。
缺點: 這種情況相當於小球從山上滾下來時是在盲目地沿著坡滾,如果它能具備一些Crowdsourced Security Testing,例如快要上坡時,就知道需要減速了的話,適應性會更好。
5. Nesterov accelerated gradient
梯度更新規則:
用θ−γv_t−1來近似當做參數下一步會變成的值,則在計算梯度時,不是在當前位置,而是未來的位置上
超參數設定值: γ 仍然取值 0.9 左右。
效果比較:
藍色是Momentum的過程,會先計算當前的梯度,然後在更新後的累積梯度後會有一個大的跳躍。
而 NAG會先在前一步的累積梯度上(brown vector)有一個大的跳躍,然後衡量一下梯度做一下修正(red vector),這種預期的更新可以避免我們走的太快。
NAG 可以使RNN 在很多任務上有更好的表現。
目前為止,我們可以做到,在更新梯度時順應loss function 的梯度來調整速度,並且對 SGD進行加速。
我們還希望可以根據參數的重要性而對不同的參數進行不同程度的更新。
6. Adagrad
這個演算法就可以對低頻的參數做較大的更新,對高頻的做較小的更新,也因此,對於稀疏的資料它的表現很好,很好地提高了SGD的魯棒性,例如識別Youtube視頻裡面的貓,訓練GloVe word embeddings,因為它們都是需要在低頻的特徵上有更大的更新。
梯度更新規則:
其中 g 為:t 時刻參數 θ_i 的梯度
如果是普通的SGD, 那麼 θ_i 在每一時刻的梯度更新公式為:
但這裡的learning rate η也隨t和i而變:
其中 G_t是個對角矩陣, (i,i) 元素就是 t 時刻參數 θ_i 的梯度平方和。
Adagrad的優點是減少了學習率的手動調節
超參數設定值: 一般 η 就取 0.01。
缺點: 它的缺點是分母會不斷積累,這樣學習率就會收縮並最終會變得非常小。
7. Adadelta
這個演算法是對Adagrad的改進,和 Adagrad相比,就是分母的 G 換成了過去的梯度平方的衰減平均值:
這個分母相當於梯度的均方根root mean squared (RMS),所以可以用RMS簡寫:
其中 E 的計算公式如下,t 時刻的依賴於前一時刻的平均和當前的梯度:
梯度更新規則:
此外,還將學習率 η 換成了RMS[Δθ],這樣的話,我們甚至都不需要提前設定學習率了:
超參數設定值: γ 一般設定為 0.9。
8. RMSprop
RMSprop 是 Geoff Hinton提出的一種適應性學習率方法。
RMSprop 和Adadelta 都是為瞭解決 Adagrad學習率急劇下降問題的,
梯度更新規則: RMSprop與 Adadelta 的第一種形式相同
超參數設定值: Hinton建議設定 γ 為 0.9, 學習率 η 為 0.001。
9. Adam
這個演算法是另一種計算每個參數的適應性學習率的方法。
除了像 Adadelta 和RMSprop一樣儲存了過去梯度的平方 vt 的指數衰減平均值 ,也像 momentum 一樣保持了過去梯度 mt 的指數衰減平均值:
如果 mt 和 vt被初始化為 0 向量,那它們就會向 0 偏置,所以做了偏差校正,
通過計算偏差校正後的mt和vt來抵消這些偏差:
梯度更新規則:
超參數設定值: 建議 β1 = 0.9,β2 = 0.999,ϵ = 10e−8
實踐表明,Adam 比其他適應性學習方法效果要好。
10.效果比較
下面看一下幾種演算法在鞍點和等高線上的表現:
上面兩種情況都可以看出,Adagrad, Adadelta, RMSprop 幾乎很快就找到了正確的方向並前進,收斂速度也相當快,而其它方法要麼很慢,要麼走了很多彎路才找到。
由圖可知適應性學習率方法即Adagrad, Adadelta, RMSprop, Adam在這種情景下會更合適而且收斂性更好。
三、如何選擇 如果資料是稀疏的,就用自適用方法,即 Adagrad, Adadelta, RMSprop, Adam。 RMSprop, Adadelta, Adam 在很多情況下的效果是相似的。 Adam 就是在 RMSprop 的基礎上加了bias-correction 和 momentum, 隨著梯度變的稀疏,Adam比 RMSprop效果會好。 整體來講,Adam是最好的選擇。 很多論文裡都會用 SGD,沒有momentum等。SGD雖然能達到極小值,但是比其它演算法用的時間長,而且可能會被困在鞍點。 如果需要更快的收斂,或者是訓練更深更複雜的神經網路,需要用一種自適應的演算法。