參考鏈接:http://sebastianruder.com/optimizing-gradient-descent/
如果熟悉英文的話,強烈推薦閱讀原文,畢竟翻譯過程中因為個人理解有限,可能會有謬誤,還望讀者能不吝指出。另外,由於原文太長,分了兩部分翻譯,本篇主要是梯度下降優化算法的總結,下篇將會是隨機梯度的並行和分布式,以及優化策略的總結。
梯度下降是優化中最流行的算法之一,也是目前用於優化神經網絡最常用到的方法。同時,每個優秀的深度學習庫都包含了優化梯度下降的多種算法的實現(比如, lasagne 、 caffe 和 keras 的文檔)。然而,這些算法一般被封裝成優化器,如黑盒一般,因此很難得到它們實際能力和缺點的解釋。
本篇博客的目標是為讀者提供不同梯度下降優化算法的直觀解釋,希望讀者可以學以致用。我們會先了解下梯度下降的不同變種。然后會對訓練過程的問題進行簡單總結。接着,我們會介紹最常用的優化算法,展示它們解決這些問題的動機,以及它們對應更新規則變化的原因。我們也就會簡單回顧在並行和分布式的情況下,梯度下降優化的算法和架構。最后,我們也會聊聊有助於優化梯度下降的其他策略。
梯度下降是最小化以模型參數 θ∈Rdθ∈Rd 構建的目標函數 J(θ)J(θ) 的一種方法,它通過按目標函數 ∇θJ(θ)∇θJ(θ) 在參數梯度的相反方向更新參數。學習率 ηη 決定了我們到達(局部)最小所需的步數的大小。換成通俗的話說,我們會沿着目標函數所構建的表面坡度的方向往下走,直到我們到達一個谷底。如果你還不熟悉梯度下降,你可以參考這篇 優化神經網絡的入門介紹 。
不同版本的梯度下降
一共有三種不同版本的梯度下降,它們的不同之處字啊與我們計算目標函數梯度時使用數據的多少。根據數據量的大小,我們會在參數更新的准確度和更新花費的時間之間進行權衡。
批量梯度下降
最普通的梯度下降,即批量梯度下降,使用整個訓練數據根據參數 θθ 計算目標函數的梯度:
θ=θ−η⋅∇θJ(θ)θ=θ−η⋅∇θJ(θ)
因為我們需要計算完整個數據集的梯度才能更新,批量梯度下降非常的耗時,而且面對無法完全放入內容的數據集,處理起來也很棘手。批量梯度更新也無法讓我們 在線 ,即在運行時加入新的樣本進行模型更新。
以代碼的形式,批量梯度下降的形式如下:
for i in range(nb_epochs): params_grad = evaluate_gradient(loss_function, data, params) params = params - learning_rate * params_grad
對於預先設定好的訓練迭代次數,我們首先對於整個數據集根據參數矢量 params
計算損失函數的梯度矢量 weight_grad
。注意最新的深度學習庫提供了自動微分的方法,可以根據參數高效計算梯度。如果你自己做梯度的微分,那么最好做一下梯度檢查。(從 這篇文章 可以獲取一些合理檢查梯度的技巧。)
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
挑戰
然而, 傳統的 mini-batch 梯度下降,並無法保證好的收斂,但卻有一些需要強調的挑戰:
- 選擇一個合適的學習率很困難。學習率太小導致收斂巨慢,而學習率過大又會妨礙收斂,導致損失函數在最小值附件波動,甚至發散出去。
- 學習率的調度 11 嘗試使用如模擬退火等方法在訓練時可以根據預先定義的調度方式,或者當兩次訓練中目標的變化在閾值之下時,可以自動的調整學習率。然而,這些調度方式和閾值需要提前定義,因此無法適用於數據集的特征 10 。
- 另外,同一個學習率應用到所有的參數更新。如果我們的數據非常稀疏,特征具有完全不同的頻率,我們可能不希望以相同的方式對它們進行更新,更希望對少量出現的特征進行較大的更新。
- 另一個關鍵的挑戰在於最小化神經網絡中常見的非凸誤差函數時,要避免陷入大量的局部最小值。Dauphin et al.聲稱實際上難度並非由局部最小值引起,而是由鞍點導致,鞍點就是那些在一個維度是上坡,另一個維度是下坡的點。這些鞍點一般由穩定的相同錯誤值圍繞,這就讓 SGD 很難從鞍點逃逸,因為梯度在各個維度都接近於零。
梯度下降優化算法
接下來,我們將會羅列一些深度學習社區廣泛用於處理前面提到的挑戰的算法。我們將不會討論那些無法實際處理高維數據集的算法,即二階方法,如 牛頓法 。
首先介紹動量梯度下降算法,該算法主要是引進了指數加權平均,如下:
一、動量梯度下降:
公式:
將學習速率a整合到第一個式子中,如下:
二、Nerterov梯度下降:
注意:該項,相當於讓
代替下一時刻的梯度(做一個近似預判),然后結合前面時刻的平均梯度,做出更有效的選擇;
三、Adagrad梯度下降:
四、RMSprop梯度下降:
五、Adadelta梯度下降:
六、Adam梯度下降:
可以參考:https://segmentfault.com/a/1190000012668819(講得不錯!)
二、關於solver.prototxt中相關參數的解釋:
epoch:1個epoch就是將所有的訓練圖像全部通過網絡訓練一次
例如:假如有1280000張圖片,batchsize=256,則1個epoch需要1280000/256=5000次iteration
它的max-iteration=450000,則共有450000/5000=90個epoch
而lr什么時候衰減與stepsize有關,減少多少與gamma有關,即:若stepsize=500, base_lr=0.01, gamma=0.1,則當迭代到第一個500次時,lr第一次衰減,衰減后的lr=lr*gamma=0.01*0.1=0.001,以后重復該過程,所以
stepsize是lr的衰減步長,gamma是lr的衰減系數。
在訓練過程中,每到一定的迭代次數都會測試,迭代次數是由test-interval決定的,如test_interval=1000,則訓練集每迭代1000次測試一遍網絡,而
test_size, test_iter, 和test圖片的數量決定了怎樣test, test-size決定了test時每次迭代輸入圖片的數量,test_iter就是test所有的圖片的迭代次數,如:500張test圖片,test_iter=100,則test_size=5, 而solver文檔里只需要根據test圖片總數量來設置test_iter,以及根據需要設置test_interval即可。
三、關於train.prototxt中相關系數解釋:
name: "LeNet" layer { name: "mnist" type: "Data" //這里的type還有MemoryData(內存讀取)HDF5Date,HDF5output,ImageDta等 top: "data" //bottom代表輸入,top代表輸出 top: "label" include { phase: TRAIN //該層參數只在訓練階段有效 } transform_param { scale: 0.00390625 //數據變換使用的數據縮放因子,用於數據預處理如剪均值,尺寸變換,隨機剪,鏡像等 } data_param { //數據層參數 source: "./examples/mnist/mnist_train_lmdb" #lmdb路徑 batch_size: 64 //批量數目,一次讀取64張圖 backend: LMDB } } layer { //一個新的數據層,但只在測試階段才有效 name: "mnist" type: "Data" top: "data" top: "label" include { phase: TEST //該層參數只在測試階段有效 } transform_param { scale: 0.00390625 } data_param { source: "./examples/mnist/mnist_test_lmdb" batch_size: 100 backend: LMDB } } layer { //定義接下來的卷積層 name: "conv1" type: "Convolution" bottom: "data" //輸入為data top: "conv1" //輸出為conv1 param { lr_mult: 1 //weights的學習率和全局相同 } param { lr_mult: 2 //biases的學習率是全局的2倍 } convolution_param { //卷積計算參數 num_output: 20 //輸出特征圖數量為20,即有20個filters kernel_size: 5 //卷積核的尺寸為5*5,這里的卷積核即為filter stride: 1 //卷積核移動尺寸 weight_filler { //權值使用xavier填充器 type: "xavier" //一種初始化方法 } bias_filler { //bias使用常數填充器,默認為0 type: "constant" } } } layer { //定義接下來的池化層(又稱下采樣層) name: "pool1" type: "Pooling" bottom: "conv1" top: "pool1" pooling_param { pool: MAX //使用最大值下采樣法 kernel_size: 2 //下采樣窗口尺寸為2*2 stride: 2 //窗口移動尺寸為2 } } layer { name: "conv2" type: "Convolution" bottom: "pool1" top: "conv2" param { lr_mult: 1 } param { lr_mult: 2 } convolution_param { num_output: 50 kernel_size: 5 stride: 1 weight_filler { type: "xavier" } bias_filler { type: "constant" } } } layer { name: "pool2" type: "Pooling" bottom: "conv2" top: "pool2" pooling_param { pool: MAX kernel_size: 2 stride: 2 } } layer { //全連接層 name: "ip1" type: "InnerProduct" bottom: "pool2" top: "ip1" param { lr_mult: 1 } param { lr_mult: 2 } inner_product_param { num_output: 500 //該層輸出元素個數為500個 weight_filler { type: "xavier" } bias_filler { type: "constant" } } } layer { //非線性層,使用RELU激活函數 name: "relu1" type: "ReLU" bottom: "ip1" top: "ip1" } layer { name: "ip2" type: "InnerProduct" bottom: "ip1" top: "ip2" param { lr_mult: 1 } param { lr_mult: 2 } inner_product_param { num_output: 10 weight_filler { type: "xavier" } bias_filler { type: "constant" } } } layer { //分類准確率層,只在TEST階段有效,輸出為accuracy,該層用於計算准確率 name: "accuracy" type: "Accuracy" bottom: "ip2" bottom: "label" top: "accuracy" include { phase: TEST } } layer { //損失層,采用softmax分類器計算loss值 name: "loss" type: "SoftmaxWithLoss" bottom: "ip2" bottom: "label" top: "loss" }
momentum:動量(又稱動量衰減系數)
weight_decay:正則化懲罰項的系數
總結:
(1)SGD
優點:簡單方便;
缺點:1、選擇一個合適的學習率很難;2、對於稀疏數據,每個都是同等的更新,但可能存在就是某些數據需要更多更新,某些只要fintune即可;3、容易陷入局部最小值;
(2)SGD with momentum
優點:更新時可以保留之前更新的方向,所以下降時具有慣性更快,且有一定幾率逃出局部最小值;
(3)Nexterov 動量下降
優點:不像傳統的momentum那樣根據慣性來下降,而是根據參數的近似未來位置來更新,更加准確智能;
(4)Adagrad下降
優點:根據參數自適應地更新學習率,對於不斷頻繁更新的參數作較大更新,對於很少更新的參數作作較小更新,所以我們不需要手動更新學習率,一般初始化為0.01;
缺點:由於分母上累積了梯度的平方,且每個加上的值都是整數,隨着不斷訓練累積和會不斷增大,導致學習率不斷減小,學習能力越來越弱;
(5)Adadelta下降
優點:它是對Adagrad的擴展,解決了Adagrad激進單調遞減學習率的缺點,它的梯度遞歸的由前面所有梯度的平方的均值來替代,同時還不需要設置初始學習率了;
(6)RMSprop下降:也是用來解決Adagrad學習率下降過快的問題;
(7)ADAM下降:比其他下降策略更好,具體看上面;
比較:
Karpathy做了一個這幾個方法在MNIST上性能的比較,其結論是:
adagrad相比於sgd和momentum更加穩定,即不需要怎么調參。而精調的sgd和momentum系列方法無論是收斂速度還是precision都比adagrad要好一些。在精調參數下,一般Nesterov優於momentum優於sgd。而adagrad一方面不用怎么調參,另一方面其性能穩定優於其他方法。