第9章 優化方法和歸一化
9章 優化方法和歸一化
“Nearly all of deep learning is powered by one very important algorithm: Stochastic Gradient Descent (SGD)” – Goodfellow et al.[1]
我們之前討論了評價函數,它根據權重W和偏置b參數,根據輸入返回數據點 的預測輸出。我們也討論了兩種常見的損失函數,它能度量一個給定的分類器在分類數據時有多好或多壞。
給定這些模塊,我們轉到機器學習、神經網絡、深度學習最重要的方面——優化(optimization)。優化算法是強大的神經網絡和從數據中學習模式的引擎。縱觀討論,獲得高精確度的分類器依賴於找到一組正確的權重W和偏置b。
但是我們如何找到權重W和偏置b來獲得高精確度分類器呢?一種方式是隨機初始化它們,然後評估,再次重復這些步驟,直到在某個點上我們獲得了一組參數獲得較好的分類,這種方式也可以,但是考慮到現代深度學習網絡的參數數目可能有數百萬,這種方式將花費很久的時間才能獲得一組合理參數。
代替完全隨機,我們需要一種優化算法允許我們叠代的獲得權重W和偏置b。本章,我們討論用於訓練神經網絡和深度學習模型最常見的算法——梯度下降(gradient descent)。梯度下降有很多變種,但都具有共同思想:反復評估參數、計算損失、在降低損失的方向上移動一小步。
1 梯度下降
梯度下降有兩個主要的類型:
(1) 標準的“vanilla”實現(批梯度下降);
(2) 更經常使用的、優化的“隨機”(stochastic)版本(SGD)。
本節我們首先回顧基本梯度實現作為基礎理解,然後轉到隨機梯度版本理解。我們還將回顧幾種附加功能,即動量(momentum)和Nesterov加速。
1.1 損失景觀和優化曲面
梯度下降方法是一個工作在損失景觀(或稱為優化曲面)上的叠代優化算法。經典梯度下降可視化例子是沿著x軸權重變化,y軸對應權重的損失曲線,見圖1左側所示。
圖1 左:二維可視化例子;右:多維的可視化例子
由圖1左圖可以看到,基於參數值,損失取不同的峰值和低谷。每個峰值是一個局部最大值表示非常大的損失區域,類似的低谷表示具有比較小的損失區域。具有最小值的局部最小值為全局最小值。理想下,我們希望找到全局最小,確保我們的參數可以取到最理想的值。
那麽,既然我們想達到全局最小,為什麽不一下子就到達這個地點呢?問題是這個損失景觀對我們來說是不可見的,我們實際上不知道它是什麽樣子的。如果我們有一個優化算法,我們可以從一個地方開始,但是我們不知道損失景觀是什麽樣子的,我們希望到達損失最小點而不是到達一個局部最大值的地方。
作者個人非常不喜歡這種可視化損失景觀的方式,因為它太簡單了而且它使得讀者認為梯度下降(極其變種)最終就是找到局部最小或全局最小。作者會在後續章節中進行討論。作者認為更好地描述這個問題的可視化例子是如圖1右側所示的碗的例子,碗的表面就是損失景觀,即損失函數曲線。而損失景觀和實際碗的差別是:實際的碗是三維的,而損失景觀可能存在於很多維度,可能十維、百維、甚至上千維。
沿著這個碗表面的一個位置對應一個給定參數W和b的特定的損失值。我們的目標就是嘗試不同的W和b,評估損失,之後向著更優值(理想上具有最低損失)的方向移動一小步。
1.2 梯度下降中的“梯度”
圖2 梯度下降示例
為了直觀上理解梯度下降,如圖2所示,我們有一個機器人,現在將他隨機放置在損失景觀的一個位置上,所有的機器人要做的就是找到一條到達損失最低的路線到達最低損失處。這個機器人只能依靠W和b計算一個損失值L,但是它計算出的僅僅是在損失景觀上的相對位置,它不知道往哪個方向移動,做法就是計算梯度下降,即跟隨梯度的斜率方向,使用下述公式計算所有維度下的梯度:
如果時多維,則梯度將是一個偏導數的向量。這裏的問題就是它是一個近似計算,而且速度比較慢。實際上,可以使用分析梯度計算,分析梯度精確且快速,但是對於高緯計算很難實現。
我們這裏對梯度下降主要需要記住的就是:嘗試通過一個朝著最小損失的方向邁出一步的叠代的過程,優化我們的參數以獲得低損失和高分類精度。
1.3 當做凸優化問題
如圖1右側所示的損失景觀的例子,使我們得出一個現代神經網絡重要的結論——將損失景觀當做一個凸優化問題對待,即使它不是。如果函數F是凸的,那麽所有的局部最小值也是全局最小值。
但是問題是,在應用神經網絡和深度學習的幾乎所有問題都不是整潔的凸函數。相反,在這個損失景觀碗裏,我們會發現像螺旋狀的山峰,山谷,這更像是峽谷、陡峭的懸崖,甚至是虧損急劇下降,但又急劇上升的槽。
考慮到數據集的非凸特性,為什麽還要使用梯度下降呢?原因是梯度下降工作確實足夠好。當我們訓練一個深度學習網絡時,我們可以對找到一個局部/全局最低要求設定很高的期望,但是這種期望很少與現實相符。相反,我們最終發現了一個低損失區域——這個區域可能甚至不是局部最低,但實際上,這已經足夠了。
1.4 偏置技巧(bias trick)
在應用梯度下降之前,我們討論一個偏置技巧問題,即這是一種將權重矩陣W和偏置向量b結合到單個參數的方法。我們之前定義的評分函數為:
在解釋和執行方面,保持兩個獨立參數很討厭。這裏為了將W和b組合到一起,將輸入X增加一個值為1的額外維度(列)。我們可以在每一個 的開始或結束位置插入該額外維度,這都沒有影響。這樣,我們可以用單一矩陣相乘表示評分函數:
這樣,我們就可以忽略偏置b,使得它插入到權值矩陣W中。
以我們之前的Animals數據集為例,工作在32*32*3的圖像共3072像素。每個 可表示為[3072*1]的向量。添加一個維度變為[3073*1]向量,類似的,添加了偏置b的權值矩陣W將是[3*3073]而不是[3*3072]了。以這種方式,我們將偏置b作為在權值矩陣W中的一個學習參數而不是一個單獨的變量了。以圖3為例:
圖3 左:正常分開表示參數 右:組合參數表示
采用這種偏置技巧,我們僅需要學習單個的權重矩陣參數即可。在將來的本書介紹中,在談到權重W的I維度時,都假定偏置b是隱含在偏置矩陣中了。
1.5 梯度下降的偽隨機碼
下面是標準的、批處理梯度下降算法的類python偽隨機代碼:
這個偽隨機代碼是所有梯度下降的變種算法所依據的。第一行中的循環持續優化,知道退出條件滿足,通常是下面三種的一種:
(1) 特定的批次數目(epoch)滿足。(意味著我們的學習算法“看到”每一個訓練點N次)
(2) 我們的損失足夠低或訓練精確度足夠高;
(3) 損失在M此epoch後不再提高了。
第二行中調用名為evaluate_gradient()的函數,該函數需要三個參數:
(1) loss:一個在在當前參數W和輸入數據上計算損失的函數;
(2) data:訓練數據,這裏的每個訓練樣例都表示一副圖像(或特征向量);
(3) W:我們要優化的實際的權重矩陣。我們的目標是應用梯度下降找到一個最小化損失的W。
這個evaluate_gradient()函數返回一個K維的向量,這裏K是圖像/特征向量的維度。這個Wgradient是實際的梯度。
我們在第三行應用梯度下降。將Wgtradient乘以一個參數alpha(α),這是學習率,它控制每一步的大小。
實際上,你將花費大量時間來找到一個最佳的α值,這是目前為止你的模型中最終要的參數值。如果α太大,則將可能會錯過最低值,而在損失景觀上來回震蕩,而不能降低到“碗”的底部;而如果α太小,則會花費太久的叠代步驟到達底部。因此,找到最優的α值,將會花費大量的時間。
1.6 用python應用梯度下降
下面我們使用python來簡單應用梯度下降,打開文件gradient_descent.py,鍵入代碼,運行即可。這裏見https://github.com/shengqishi8787/chapter7-KNN.git下的chapter8目錄,然後python gradient_descent.py即可顯示這裏的示例結果。
這裏我們簡單使用激活函數>0.5時為1,<0.5時為0,其它激活函數將在Startle Bundle的第10章和Practitioner Bundle的第7章討論。
其中參數,我們設定了周期epoch和學習率α,其中學習率通常設定為0.1、0.01、0.0001,這個參數是一個超參數需要在模型中調整。
你也可以看到0和1的權重初始化,但正如我們將在本書後面發現的,良好的初始化對於在合理的時間內訓練神經網絡是至關重要的,所以隨機的初始化和簡單的啟發式在絕大多數情況下都是成功的。
1.7 簡單梯度下降運行結果分析
當第一次運行時,可看到結果分類1為100%,而分類0為0%,這是因為在每一次周期epoch內,都只更新一次權重。對於簡單的梯度下降,我們可以通過多次epoch或設置不同的學習率來更好地訓練網絡模型。但是,就像在下一節介紹的,一種梯度下降的變種形式隨機梯度下降(Stochastic Gradient Descent)在訓練數據的每一批次都更新一次權重,暗含在每一周期epoch內都更新多次權重。這種方法可以獲得更快、更穩定的收斂。
2 隨機梯度下降(SGD)
前一節介紹了簡單的基礎梯度下降方法,但是它在大數據集上計算緩慢。取而代之的是,應用隨機梯度下降(Stochastic Gradient Descent (SGD)),這是對標準梯度下降做的簡單修改,即在訓練數據集的小批次(batch)上而不是在整個數據集上計算權重且更新權重W。
當開始訓練深度神經網絡時,SGD是可論證的最重要的算法。
2.1 Mini-batch SGD
在標準梯度下降算法中,在大數據集上的運行很慢。我們的改進是批量更新。我們由標準梯度算法的改進得來SGD的偽代碼:
在標準梯度下降和SGD之間的唯一差別是添加了next_training_batch()函數,代替在整個訓練集上計算梯度,我們采樣batch大小來計算。在SGD計算中,我們加入一個超參數:batch size。通常,我們的batch size大於1,一般是32、64、128或256。一般來說,這個參數不是我們太擔心的超參數。
2.2 實現mini-batch SGD
該實現文件見git中的sgd.py。
3 SGD擴展
在實際中常見到兩種SGD的擴展,一種是動量(momentum),它用於加速SGD,它通過關註梯度指向相同方向的維度來加速;另一種是Nesterov動量,它是第一種標準動量的改進。
3.1 Momentum
想象一下你小時候從山上跑下山的情況,在跑下山過程中,你越跑越快,即動量越來越大,這將使你更快的到達山下。應用於SGD的動量具有相同作用,我們的目標是在標準權重基礎上加上一個動量項來更新,即允許模型在更少的epoch上獲得更低的損失(即更高的準確率。)因此,動量項應該增加梯度指向同一方向的維度的更新強度,然後減少梯度轉換方向的維度的更新強度。
我們之前的更新權重僅僅是通過學習率縮放梯度:
我們現在引入一個動量項V,由γ縮放:
一種常見的動量項γ取0.9,另一種方法是γ設置為0.5,隨時間增加到0.9,對於動量項γ設置成小於0.5的值是很少見的。
3.2 Nesterov momentum
再次考慮往山下跑的情況,你建立動量一路快速跑下山,但是如果山下正好是一堵墻,這時候你應當避免全速沖撞。這種思想也可以應用到SGD中,如果動量太大,我們可能會錯過最低點,因此我們需要一種聰明的動量方式,它能夠知道什麽時候加速,什麽時候減速,這就是Nesterov momentum。
Nesterov momentum可以被概念化為對動量的修正更新,這使我們在更新後大致了解我們的參數將在哪裏,回顧Hinton的Overview of mini-batch gradient descent slides,見圖4,我們更好地可視化Nesterov momentum。
圖4 Nesterov momentum概念示例
基於標準動量,我們計算梯度(小的藍向量)且在梯度方向上(大的藍向量)進行一次跳躍。在Nesterov加速下,我們在之前的梯度方向上(褐色向量)做出一次大的跳躍,測量梯度,然後做出糾正(紅色向量),綠色向量是最終糾正後由Nesterov加速後更新的向量。
3.3 作者建議
動量是增加模型收斂的重要參數,但是與學習率和正則化懲罰(regularization penalty)相比,我們不需要太關註動量的選擇。作者準則是無論什麽時候使用SGD時,都加上動量項,你可以設置它的值或隨著時間增加到0.9。
對於Nesterov加速,作者傾向於在小數據集上使用,不建議在大數據集如ImageNet上使用。在ImageNet的數據集上的所有主要的發表物(如AlexNet, VGGNet, ResNet, Inception等)都使用了具有動量的SGD,但沒有一個使用Nesterov加速。
註意:這只是作者的觀點。
4 正則化(regularization)
“Many strategies used in machine learning are explicitly designed to reduce the test error, possibly at the expense of increased training error. These strategies are collectively known as regularization.” – Goodfellow et al.[1]
機器學習中使用的許多策略都被明確地設計來減少測試誤差,可能以犧牲增加的訓練誤差為代價,這些策略被統稱為正則化。
雖然我們的損失函數允許我們確定我們的一組參數在給定的分類任務中有多好(或差),但損失函數本身並沒有考慮權重矩陣看起來如何。看起來意味著什麽?註意我們工作在實數值空間,在數據集上有無限的參數集合能夠獲得合理的分類精確度。那麽,我們如何選擇一組參數來保證我們的模型泛化足夠好呢?或者,至少減輕過擬合的影響,答案就是正則化。僅次於學習率,正則化是要學習的模型中最終要的調整參數。
存在各種類型的正則化技術,例如L1正則、L2正則和彈性網絡(Elastic Net),用於更新損失函數本身,增加額外參數以約束模型的容量。
還有幾種明確的加入到網絡架構中的正則化類型:dropout(丟棄)是這種正則化的典型例子。我們也有明確的應用到訓練過程的正則化形式,包括數據增加(data augmentation)和早停(early stopping)。
在本節我們主要關註通過修改損失和更新函數獲得的參數化正則方法。在Startle Bundle的第11章,我們學習dropout且在第17章更深入的討論過擬合(overfitting),以及如何使用早停。在Practitionner Bundle中,你將看到數據增加作為正則化的例子。
4.1 正則化是什麽?我們為什麽需要它?
正則化能夠幫助我們控制模型容量,確保模型在還沒有訓練過的數據點上做出更正確的預測,即具有更好地泛化能力。如果我們不實施正則化,則分類器將很容易變得很復雜從而在訓練數據上過擬合,那麽模型將失去在測試數據上泛化的能力。
但是,太過的正則化也是一件壞事情,這樣將有欠擬合(underfitting)的風險,欠擬合使得模型在訓練數據集上執行很差,從而不能模擬輸入數據和輸出類別標簽的對應關系。如圖5所示的示意圖:
圖5 擬合能力示意圖
橘色線為欠擬合,不能獲得數據之間的關系;藍色線為過擬合,模型中存在太多參數而不平滑。正則化的目標就是很好地獲得這些擬合訓練數據的“綠色”曲線,而避免過擬合訓練數據(“藍色線”)或不能模擬潛在的數據關系(“橘色線”)。我們將在第17章討論怎樣監視訓練和發現過擬合與欠擬合。但是在這時,只需要知道正則化是機器學習的至關重要的方面、我們將使用正則化來控制模型泛化性能即可。下一節中將討論如何理解正則化和它是怎樣影響損失函數和更新權重的。
4.2 更新損失和更新權重包含正則化
讓我們以交叉熵損失函數開始:
整個訓練集上的損失可表示為:
現在,我們說我們獲得了一個權重矩陣W使得訓練集中的每一個數據點都分類正確,也就意味著對於所有的,我們的損失。
了不起!我們獲得了100%的正確率,但是這個權重是唯一的嗎?或者換句話說,存在能夠提高模型的泛化和減少過擬合的能力的權重W更好的選擇嗎?
如果有這樣的一個W,我們如何知道?我們如何將這種懲罰加入到我們的損失函數中?答案就是定義一個正則化懲罰(regularization penalty),這是一個定義在權重矩陣上的函數。正則化懲罰常寫作R(W)的函數形式,下式是最常見的正則化懲罰:L2正則化(又稱為權重衰減weight delay):
依據python代碼,我們可以寫作下述形式:
這裏做的就是在整個矩陣上循環求平方和。在L2正則化懲罰中不鼓勵大的權重值,傾向於小的權重值。通過懲罰大的權重值,我們可以提高泛化能力,即降低過擬合。這樣想象一下,權重值越大,它對預測結果影響越大。具有很大的權重值幾乎獨自就可決定分類器的預測結果,這確定是產生了過擬合。
為了減少對分類器結果的各種影響,我們采用正則化方法,從而尋求考慮到所有維度而不是少數大值的W值。在實際中,可能會發現正則化輕微的影響了訓練精確度,但是它會增加測試精確度。
此時,我們的損失函數有了一個基本形式,僅需要加上正則化項:
第一項為在整個訓練集上的平均損失;
第二項就是我們的正則化懲罰項,λ變量為超參數用於控制正則化的大小或強度。在實際中,學習率α和正則化項λ都是需要花費大量時間進行調整的超參數。
包含L2正則化項的擴展交叉熵形式為:
我們也可擴展多類別SVM損失形式為:
現在,我們看下標準的權重更新規則:
這種方法基於梯度乘以學習率α來更新權重。考慮正則化項,權重更新規則變為:
4.3 正則化技術的類型
一般來說,你可以見到三種直接應用到損失函數中的常見正則化類型。第一種就是L2正則化(也叫權重衰減):
第二種是L1正則化,它考慮權重絕對值:
第三種彈性網絡(elastic net)正則化,同時考慮L1和L2正則化:
還有其他類型的正則化方法,如直接修改網絡的體系結構以及網絡的實際訓練方式,我們將在後續章節中討論三種方法。
在你應該使用哪種正則化方法(或完全不包括任何正則化方法)方面,你應該將此選擇視為一個超參數,你需要對其進行優化並進行實驗,以確定是否應該應用正則化方法,如果應用,則應使用哪種正則化方法,λ的正確值是多少?
4.4 正則化應用到圖像分類
為了實踐正則化,我們用python代碼應用到Animals數據集上。
代碼為regularization.py。在導入代碼中,新導入from sklearn.linear_model import SGDClassifier,其中SGDClassifier封裝了本章中所有概念,包括損失函數、epoch數目、學習率、正則化項,因此它能讓我們完美的演示這些概念。
其中,在測試代碼中,我將elasticnet也加入到測試中,現實中,這個例子太小而不能很好的表現正則化對於性能的影響,在後面訓練CNNs時就可看到。然而,在此期間,只要我們能夠正確調整超參數,就可以簡單地認識到正則化可以提高我們的測試精度並減少過擬合。
5 總結:
學習到這裏,我們有一個良好、基礎的機器學習,但我們還沒有研究神經網絡或從頭開始訓練一個定制的神經網絡。在下一章中,我們將討論神經網絡、反傳播算法以及如何在自定義數據集上訓練自己的神經網絡。
6 附錄
[1] Ian Goodfellow, Yoshua Bengio, and Aaron Courville. Deep Learning. http://www.deeplearningbook.org. MIT Press, 2016 (cited on pages 22, 24, 27, 42, 54, 56, 82, 95, 98, 113, 117, 169, 194, 252).
第9章 優化方法和歸一化