本章內容
8.1 梯度下降的三種形式
8.2 一階與二階方法
8.3 最佳化的挑戰
8.4 動量法
8.5 自適應學習率
8.6 自適應動差估計
8.7 學習率衰減
8.8 正規化加速訓練
8.9 參考資料
訓練神經網路,本質上是在一個維度極高、形狀複雜的損失曲面上尋找低點。模型架構決定了曲面的樣子,最佳化演算法則決定我們如何在這片地形上移動:每一步往哪個方向走、走多遠。同一個模型,換一個最佳化演算法或學習率設定,收斂速度可能相差好幾倍,最後停下的位置也不盡相同。影響最佳化成敗的因素主要有三個:目標函數的形狀、學習率,以及梯度的大小與方向。
這一章從最基本的梯度下降開始,說明它在深度學習中會遇到哪些困難,再依序介紹為了解決這些困難而發展出的動量法、自適應學習率方法與學習率排程,最後談輸入標準化與批次正規化如何讓損失曲面本身變得更容易最佳化。
8.1 梯度下降的三種形式Gradient Descent Variants
梯度下降(gradient descent)的更新規則很簡單:沿著目標函數梯度的反方向走一小步。
θ ← θ − η ∇ θ J ( θ )
\theta \leftarrow \theta - \eta \, \nabla_{\theta} J(\theta)
θ ← θ − η ∇ θ J ( θ ) 其中 θ \theta θ 代表所有模型參數,J ( θ ) J(\theta) J ( θ ) 是目標函數(成本函數),η \eta η 是學習率(learning rate),控制每一步的步長。本章後面的演算法依慣例改用 α \alpha α 表示學習率,兩者意義相同。
三種形式的差別在於「計算一次梯度時用了多少資料」。以一個訓練週期(epoch,也就是完整看過一次訓練集)為單位比較,設訓練集有 m m m 筆資料:
形式
每次更新使用的資料
每個 epoch 更新次數
特性
批次梯度下降(batch GD)
整個訓練集
1 1 1
梯度精確,但每一步很慢
隨機梯度下降(stochastic GD, SGD)
1 筆資料
m m m
更新頻繁但雜訊大,無法利用向量化
小批次梯度下降(mini-batch GD)
B B B 筆資料
m / B m/B m / B
兼顧兩者,實務上的預設選擇
小批次的大小
小批次梯度下降是深度學習的標準做法。批次大小 B B B 介於兩個極端之間:比 1 大,所以能利用 GPU 的向量化運算平行處理;比 m m m 小,所以不必看完全部資料才更新一次參數。資料量很小時(例如兩千筆以內),直接使用批次梯度下降即可;資料量大時,常見的小批次大小是 64、128、256、512,習慣上取 2 的次方,以配合硬體的記憶體配置。最重要的限制是:一個小批次的資料與對應的中間運算結果,必須放得進 CPU/GPU 的記憶體(見第 9 章 )。
損失曲線的樣子
批次梯度下降每一步都使用精確的梯度,只要學習率夠小,成本函數會隨迭代次數單調遞減 ;如果某次迭代的成本反而上升,通常代表學習率太大或程式有錯。小批次梯度下降則不同:每個小批次只是全體資料的一個樣本,算出的梯度帶有雜訊,成本曲線會上下震盪,但整體趨勢向下。這種震盪是正常現象,不代表訓練出了問題。隨機梯度下降的雜訊最大,它不會真正停在最小值上,而是在附近徘徊,這也是後面需要學習率衰減的原因之一。
補充Note
批次大小與學習率的作用是連動的。小批次梯度的雜訊大小大致與 η / B \eta / B η / B 成正比,因此增加批次大小的效果,類似於降低學習率 。Smith 等人(2018)在 Don't Decay the Learning Rate, Increase the Batch Size 中證實,訓練過程中逐步加大批次,可以得到與學習率衰減相近的結果,同時減少參數更新的次數。
8.2 一階與二階方法First- and Second-Order Methods
梯度下降只用到一階導數,也就是只知道「哪個方向往下」,卻不知道曲面在那個方向彎得多厲害,因此需要一個人為設定的學習率來決定步長。二階方法則進一步利用二階導數,也就是黑塞矩陣(Hessian matrix)H = ∇ θ 2 J ( θ ) H = \nabla^2_{\theta} J(\theta) H = ∇ θ 2 J ( θ ) ,來描述曲面的曲率。
牛頓法
在目前的參數 θ 0 \theta_0 θ 0 附近,把 J J J 以二階泰勒展開近似:
J ( θ ) ≈ J ( θ 0 ) + ∇ J ( θ 0 ) ⊤ ( θ − θ 0 ) + 1 2 ( θ − θ 0 ) ⊤ H ( θ − θ 0 )
J(\theta) \approx J(\theta_0) + \nabla J(\theta_0)^{\top}(\theta - \theta_0) + \tfrac{1}{2}(\theta - \theta_0)^{\top} H \,(\theta - \theta_0)
J ( θ ) ≈ J ( θ 0 ) + ∇ J ( θ 0 ) ⊤ ( θ − θ 0 ) + 2 1 ( θ − θ 0 ) ⊤ H ( θ − θ 0 ) 對這個二次函數求最小值,就得到牛頓法(Newton's method)的更新規則:
θ ← θ 0 − H − 1 ∇ J ( θ 0 )
\theta \leftarrow \theta_0 - H^{-1} \nabla J(\theta_0)
θ ← θ 0 − H − 1 ∇ J ( θ 0 ) 步長由曲率自動決定:曲率大的方向走小步,曲率小的方向走大步,不需要學習率 。若 J J J 本身就是二次函數,牛頓法一步就能到達最小值。
然而在深度學習中,牛頓法幾乎無法直接使用:
黑塞矩陣太大 :模型有 p p p 個參數時,H H H 是 p × p p \times p p × p 的矩陣,需要 O ( p 2 ) O(p^2) O ( p 2 ) 的記憶體,求反矩陣更需要 O ( p 3 ) O(p^3) O ( p 3 ) 的運算。一個只有一百萬個參數的小型網路,H H H 就有 10 12 10^{12} 1 0 12 個元素。
需要完整批次 :用小批次估計的黑塞矩陣雜訊很大,取反矩陣後誤差會被放大,因此二階方法通常只適用於批次梯度下降。
會被鞍點吸引 :牛頓法尋找的是梯度為零的點,不區分最小值與鞍點,在非凸問題上需要額外修正。
擬牛頓法:BFGS 與 L-BFGS
擬牛頓法(quasi-Newton methods)不直接計算黑塞矩陣,而是利用連續幾步的參數變化與梯度變化,逐步建立 H − 1 H^{-1} H − 1 的近似。BFGS(以 Broyden、Fletcher、Goldfarb、Shanno 四位作者命名)是其中最著名的演算法,它省去了計算與求反矩陣的成本,但仍需儲存一個 p × p p \times p p × p 的近似矩陣。L-BFGS(limited-memory BFGS)更進一步,只保留最近 k k k 步(通常 5 到 20 步)的向量,用它們隱式地表示 H − 1 H^{-1} H − 1 的近似,記憶體需求降到 O ( k p ) O(kp) O ( k p ) 。
8.3 最佳化的挑戰Challenges in Optimization
深度網路的損失曲面並不是一個平滑的碗。以下幾個問題會讓梯度下降變慢,甚至停滯。要注意,這些問題並非隨機梯度下降所獨有 ,任何以梯度為基礎的方法都會遇到,只是程度不同;本章後面介紹的演算法,大多就是針對它們而設計的。
病態條件:某個方向特別敏感
如果損失函數在某個方向非常陡峭、在另一個方向卻很平緩,等高線就會是一個細長的橢圓。這種情況稱為病態條件(ill-conditioning),可以用黑塞矩陣最大與最小特徵值的比值,也就是條件數(condition number)κ = λ max / λ min \kappa = \lambda_{\max} / \lambda_{\min} κ = λ m a x / λ m i n 來衡量。
在這樣的曲面上,梯度主要指向陡峭的方向,而不是最小值所在的方向。結果是參數在陡峭方向上來回震盪,走出鋸齒狀(zig-zag)的路徑,在平緩方向上卻前進得很慢。學習率也進退兩難:為了不在陡峭方向發散,學習率必須小於約 2 / λ max 2/\lambda_{\max} 2/ λ m a x ;但這麼小的學習率,在平緩方向上幾乎走不動。參數維度越高,各方向的曲率差異往往越大,這個問題也就越普遍。
局部最小值與鞍點
在局部最小值(local minimum)與鞍點(saddle point),梯度都等於零,梯度下降在這兩種位置都會停下或變得非常緩慢。
在二維、三維的直覺圖像中,我們常擔心模型卡在不好的局部最小值。但在高維空間,情況大不相同。一個梯度為零的點要成為局部最小值,必須在所有 p p p 個方向上都往上彎(黑塞矩陣的特徵值全為正);只要有些方向往上、有些方向往下,它就是鞍點。當 p p p 高達數百萬時,所有方向同時往上彎的可能性非常低,因此高維空間中的臨界點絕大多數是鞍點 。研究也顯示,大型網路中真正的局部最小值,損失通常已經接近全域最小值。局部最小值是低維度問題中較常見的困擾,高維度時鞍點才是主要的障礙。
高原
高原(plateau)是指一大片梯度接近零的平坦區域,常出現在鞍點附近。梯度很小,每一步的更新也很小,參數要花很長的時間才能離開,訓練曲線看起來就像停滯了一樣。動量法與自適應學習率方法都能在一定程度上加快脫離高原;小批次梯度本身的雜訊,也有助於離開鞍點。
8.4 動量法Momentum
動量法(momentum)的想法來自物理:一顆滾下山坡的球,不會因為地面的小起伏就立刻改變方向,而是帶著之前累積的速度繼續前進。對應到最佳化,就是不只看當下的梯度,而是看「最近一段時間梯度的平均方向」。
指數加權平均
動量法使用的平均方式是指數加權平均(exponentially weighted average)。令 g t = ∇ θ J ( θ t − 1 ) g_t = \nabla_{\theta} J(\theta_{t-1}) g t = ∇ θ J ( θ t − 1 ) 為第 t t t 步的梯度(在小批次梯度下降中,就是該小批次算出的梯度):
v t = ρ v t − 1 + ( 1 − ρ ) g t θ t = θ t − 1 − α v t
\begin{aligned}
v_t &= \rho\, v_{t-1} + (1-\rho)\, g_t \\
\theta_t &= \theta_{t-1} - \alpha\, v_t
\end{aligned}
v t θ t = ρ v t − 1 + ( 1 − ρ ) g t = θ t − 1 − α v t v v v 稱為速度(velocity),是梯度的移動平均,初始值 v 0 = 0 v_0 = 0 v 0 = 0 ;ρ \rho ρ 的作用類似摩擦力,常用值為 0.9 或 0.99。
在病態條件的曲面上,陡峭方向的梯度正負交替,平均之後互相抵消,震盪因此減弱;平緩方向的梯度始終指向同一邊,平均之後得以保留,前進速度因此加快。
補充Note
有些框架(例如 PyTorch)的寫法是 v t = ρ v t − 1 + g t v_t = \rho\, v_{t-1} + g_t v t = ρ v t − 1 + g t ,省略了 ( 1 − ρ ) (1-\rho) ( 1 − ρ ) 。兩種寫法只差一個常數倍數 1 / ( 1 − ρ ) 1/(1-\rho) 1/ ( 1 − ρ ) ,可以透過學習率互相換算;但在省略 ( 1 − ρ ) (1-\rho) ( 1 − ρ ) 的寫法中,改變 ρ \rho ρ 也會同時改變有效步長,因此調整 ρ \rho ρ 之後需要重新調整 α \alpha α 。
平均了多少步
把遞迴式展開:
v t = ( 1 − ρ ) ∑ k = 0 t − 1 ρ k g t − k
v_t = (1-\rho) \sum_{k=0}^{t-1} \rho^{k}\, g_{t-k}
v t = ( 1 − ρ ) k = 0 ∑ t − 1 ρ k g t − k 越久以前的梯度,權重以 ρ k \rho^k ρ k 指數遞減。令 β = 1 − ρ \beta = 1 - \rho β = 1 − ρ ,利用
( 1 − β ) 1 / β ≈ e − 1 ≈ 0.37
(1-\beta)^{1/\beta} \approx e^{-1} \approx 0.37
( 1 − β ) 1/ β ≈ e − 1 ≈ 0.37 可知經過 1 / ( 1 − ρ ) 1/(1-\rho) 1/ ( 1 − ρ ) 步之後,權重會降到原本的約三分之一。因此可以把 v t v_t v t 粗略看成最近 1 / ( 1 − ρ ) 1/(1-\rho) 1/ ( 1 − ρ ) 步梯度的平均 :ρ = 0.9 \rho = 0.9 ρ = 0.9 約平均 10 步,ρ = 0.99 \rho = 0.99 ρ = 0.99 約平均 100 步。ρ \rho ρ 越大,平均越平滑,但對梯度方向改變的反應也越慢。
偏差修正
因為 v 0 = 0 v_0 = 0 v 0 = 0 ,訓練初期的 v t v_t v t 會被低估。例如 ρ = 0.9 \rho = 0.9 ρ = 0.9 時,v 1 = 0.1 g 1 v_1 = 0.1\, g_1 v 1 = 0.1 g 1 ,只有實際梯度的十分之一。偏差修正(bias correction)把它除以一個隨時間變化的係數:
v ^ t = v t 1 − ρ t
\hat{v}_t = \frac{v_t}{1-\rho^{t}}
v ^ t = 1 − ρ t v t t = 1 t = 1 t = 1 時分母是 1 − ρ 1-\rho 1 − ρ ,正好把 v 1 v_1 v 1 還原成 g 1 g_1 g 1 ;隨著 t t t 增加,ρ t → 0 \rho^t \to 0 ρ t → 0 ,分母趨近 1,修正的效果自然消失,因此偏差修正只影響最初的幾步。單純的動量法實務上常省略這一步,因為最初幾步走得慢一點影響不大;但在 Adam 中,它是不可或缺的一環。
Nesterov 動量
Nesterov 加速梯度(Nesterov accelerated gradient, NAG)對動量法做了一個小修改:既然這一步無論如何都會先沿著速度方向滑出 ρ v \rho v ρ v ,不如先往前看 ,在預計抵達的位置計算梯度,再用它修正方向。這樣在快要衝過頭時,就能提早減速。
Nesterov 動量通常寫成「速度已包含學習率」的形式,這裡的 v v v 已乘上學習率並帶有負號,與前面指數加權平均的定義不同:
v t + 1 = ρ v t − α ∇ J ( θ t + ρ v t ) θ t + 1 = θ t + v t + 1
\begin{aligned}
v_{t+1} &= \rho\, v_t - \alpha\, \nabla J(\theta_t + \rho\, v_t) \\
\theta_{t+1} &= \theta_t + v_{t+1}
\end{aligned}
v t + 1 θ t + 1 = ρ v t − α ∇ J ( θ t + ρ v t ) = θ t + v t + 1 與一般動量法唯一的差別,是梯度在往前看的位置 θ t + ρ v t \theta_t + \rho\, v_t θ t + ρ v t 計算,而不是在 θ t \theta_t θ t 。
這個形式在實作上不太方便,因為程式通常只在「目前儲存的參數」上計算梯度。因此常做變數代換,令 θ ~ t = θ t + ρ v t \tilde{\theta}_t = \theta_t + \rho\, v_t θ ~ t = θ t + ρ v t ,直接儲存往前看的參數:
v t + 1 = ρ v t − α ∇ J ( θ ~ t ) θ ~ t + 1 = θ ~ t + v t + 1 + ρ ( v t + 1 − v t )
\begin{aligned}
v_{t+1} &= \rho\, v_t - \alpha\, \nabla J(\tilde{\theta}_t) \\
\tilde{\theta}_{t+1} &= \tilde{\theta}_t + v_{t+1} + \rho\,(v_{t+1} - v_t)
\end{aligned}
v t + 1 θ ~ t + 1 = ρ v t − α ∇ J ( θ ~ t ) = θ ~ t + v t + 1 + ρ ( v t + 1 − v t ) 第二式可以由 θ ~ t + 1 = θ t + 1 + ρ v t + 1 = θ t + ( 1 + ρ ) v t + 1 \tilde{\theta}_{t+1} = \theta_{t+1} + \rho\, v_{t+1} = \theta_t + (1+\rho)\, v_{t+1} θ ~ t + 1 = θ t + 1 + ρ v t + 1 = θ t + ( 1 + ρ ) v t + 1 ,再代入 θ t = θ ~ t − ρ v t \theta_t = \tilde{\theta}_t - \rho\, v_t θ t = θ ~ t − ρ v t 得到。如此一來,每一步只需在儲存的參數 θ ~ \tilde{\theta} θ ~ 上計算梯度,實作方式與一般動量法相同。
8.5 自適應學習率Adaptive Learning Rates
動量法改善的是「方向」,自適應學習率方法改善的則是「步長」:它們為每一個參數分別 調整有效學習率,依據是該參數過去梯度的大小。以下的平方、開根號與除法都是逐元素(element-wise)運算。
AdaGrad
AdaGrad 累加每個參數歷來梯度的平方,再把學習率除以這個累加值的平方根:
r t = r t − 1 + g t ⊙ g t θ t = θ t − 1 − α r t + ϵ ⊙ g t
\begin{aligned}
r_t &= r_{t-1} + g_t \odot g_t \\
\theta_t &= \theta_{t-1} - \frac{\alpha}{\sqrt{r_t} + \epsilon} \odot g_t
\end{aligned}
r t θ t = r t − 1 + g t ⊙ g t = θ t − 1 − r t + ϵ α ⊙ g t ϵ \epsilon ϵ 是一個很小的數(例如 10 − 8 10^{-8} 1 0 − 8 ),只是為了避免除以零。梯度一直很大的參數,有效學習率會被壓小;梯度一直很小或很少被更新的參數(例如稀疏特徵對應的權重),則保有相對較大的學習率。
問題在於 r t r_t r t 只增不減。訓練時間一長,所有參數的有效學習率都會持續縮小,最後幾乎停止學習。對凸函數來說,這種逐步縮小的步長有理論上的收斂保證,因此 AdaGrad 在凸問題上表現不錯;但在深度網路這類非凸問題上,學習率往往在抵達好的區域之前就已經縮得太小。
RMSProp
RMSProp(root mean square propagation)是為了讓 AdaGrad 適用於非凸問題而提出的修正:把「累加」改成指數加權平均,讓久遠的梯度逐漸被遺忘,有效學習率就不會無止盡地縮小。
s t = ρ s t − 1 + ( 1 − ρ ) g t ⊙ g t θ t = θ t − 1 − α g t s t + ϵ
\begin{aligned}
s_t &= \rho\, s_{t-1} + (1-\rho)\, g_t \odot g_t \\
\theta_t &= \theta_{t-1} - \alpha\, \frac{g_t}{\sqrt{s_t} + \epsilon}
\end{aligned}
s t θ t = ρ s t − 1 + ( 1 − ρ ) g t ⊙ g t = θ t − 1 − α s t + ϵ g t s t s_t s t 是梯度平方的移動平均,s t \sqrt{s_t} s t 就是近期梯度的均方根(RMS),代表該參數方向上「梯度通常有多大」。ρ \rho ρ 常設為 0.9。注意 RMSProp 只對梯度做縮放,並沒有對梯度本身取平均,這部分是動量法的工作。
它的直覺是:把每個方向的梯度除以該方向自己的典型大小,讓各方向的步長變得相近。
在陡峭 的方向(梯度大、常來回震盪),s t \sqrt{s_t} s t 大,有效步長變小 ,震盪受到抑制。
在平緩 的方向(梯度小),s t \sqrt{s_t} s t 小,有效步長相對變大 ,前進得更快。
回到病態條件的細長橢圓:RMSProp 相當於把橢圓「壓圓」,讓參數更直接地朝最小值前進,因此也能使用較大的學習率。
常見誤區Common pitfall
RMSProp 加速學習的原因,並不是「用更大的數值去放大更新」,而是相對縮放 :梯度大的方向被除以大的數,梯度小的方向被除以小的數。結果是陡峭方向的步伐變小、平緩方向的步伐變大,鋸齒狀的震盪因此減少。
8.6 自適應動差估計Adam
Adam(adaptive moment estimation)結合了動量法與 RMSProp:以梯度的指數加權平均(一階動差)決定方向,以梯度平方的指數加權平均(二階動差)縮放步長,並對兩者都做偏差修正。
m t = β 1 m t − 1 + ( 1 − β 1 ) g t s t = β 2 s t − 1 + ( 1 − β 2 ) g t ⊙ g t m ^ t = m t 1 − β 1 t , s ^ t = s t 1 − β 2 t θ t = θ t − 1 − α m ^ t s ^ t + ϵ
\begin{aligned}
m_t &= \beta_1\, m_{t-1} + (1-\beta_1)\, g_t \\
s_t &= \beta_2\, s_{t-1} + (1-\beta_2)\, g_t \odot g_t \\
\hat{m}_t &= \frac{m_t}{1-\beta_1^{t}}, \qquad \hat{s}_t = \frac{s_t}{1-\beta_2^{t}} \\
\theta_t &= \theta_{t-1} - \alpha\, \frac{\hat{m}_t}{\sqrt{\hat{s}_t} + \epsilon}
\end{aligned}
m t s t m ^ t θ t = β 1 m t − 1 + ( 1 − β 1 ) g t = β 2 s t − 1 + ( 1 − β 2 ) g t ⊙ g t = 1 − β 1 t m t , s ^ t = 1 − β 2 t s t = θ t − 1 − α s ^ t + ϵ m ^ t 原始論文以 v t v_t v t 表示二階動差,這裡改用 s t s_t s t ,以免與動量法的速度混淆。
超參數
常用值
是否需要調整
學習率 α \alpha α
例如 10 − 3 10^{-3} 1 0 − 3
需要,仍是最重要的超參數
β 1 \beta_1 β 1 (一階動差)
0.9 0.9 0.9
很少調整
β 2 \beta_2 β 2 (二階動差)
0.999 0.999 0.999
很少調整
ϵ \epsilon ϵ
10 − 8 10^{-8} 1 0 − 8
幾乎不調整
偏差修正在 Adam 中特別重要。β 2 = 0.999 \beta_2 = 0.999 β 2 = 0.999 時,s t s_t s t 平均的範圍約 1000 步,若不修正,初期的 s t s_t s t 會遠小於實際值,分母過小,造成最初幾步的步長過大。
重點Key point
Adam 對超參數相對不敏感,多數問題「用預設值就能訓練得動」,因此是一個穩健的預設選擇,特別適合快速建立初始模型。但它並非在所有情況下都是最好的:在影像分類等電腦視覺任務上,仔細調校的 SGD 加動量,最終的泛化表現常與 Adam 相當,甚至更好。
Adam 也有幾個常見的變形:Nadam 把其中的動量換成 Nesterov 動量;AdamW 則修正了 Adam 與 L2 正則化搭配時的問題,把權重衰減從自適應縮放中獨立出來(見第 9 章 )。
8.7 學習率衰減Learning Rate Decay
使用小批次梯度下降時,梯度帶有雜訊,即使參數已經接近最小值,固定的學習率仍會讓它在附近來回晃動,無法真正收斂。學習率衰減(learning rate decay)的想法是:訓練初期用較大的學習率快速前進,後期逐漸縮小步長,讓參數穩定地落在最小值附近較小的範圍內。
以下 α 0 \alpha_0 α 0 為初始學習率,n n n 為目前的 epoch 數:
方法
公式
說明
反比例衰減
α = α 0 1 + r ⋅ n \alpha = \dfrac{\alpha_0}{1 + r \cdot n} α = 1 + r ⋅ n α 0
r r r 為衰減率(decay rate),是新的超參數
平方根衰減
α = k n α 0 \alpha = \dfrac{k}{\sqrt{n}}\,\alpha_0 α = n k α 0
k k k 為常數,n n n 從 1 起算
指數衰減
α = c n α 0 \alpha = c^{\,n}\,\alpha_0 α = c n α 0
c < 1 c < 1 c < 1 ,例如 0.95 0.95 0.95
階梯式衰減
每隔固定 epoch 乘上一個係數
例如每 30 個 epoch 除以 10
餘弦退火
餘弦退火(cosine annealing)讓學習率在 T T T 個 epoch 內沿著半個餘弦週期,從 α 0 \alpha_0 α 0 平滑地降到下限 α min \alpha_{\min} α m i n :
α n = α min + 1 2 ( α 0 − α min ) ( 1 + cos π n T )
\alpha_n = \alpha_{\min} + \tfrac{1}{2}\,(\alpha_0 - \alpha_{\min})\left(1 + \cos\frac{\pi n}{T}\right)
α n = α m i n + 2 1 ( α 0 − α m i n ) ( 1 + cos T πn ) 它沒有階梯式衰減那種突然的跳動,需要決定的也只有 T T T 與 α min \alpha_{\min} α m i n ,是目前很常用的排程。Loshchilov 與 Hutter(2017)提出的 SGDR 會在降到下限後重新拉高學習率(warm restart),週期性地重複這個過程。
手動衰減
運算資源有限、一次只訓練少數幾個模型時,也可以觀察訓練曲線,在開發集的損失停滯不前時,手動把學習率降低(例如除以 10)。許多框架提供的 reduce-on-plateau 排程,就是把這個流程自動化。
常見誤區Common pitfall
Adam 等自適應方法雖然會依梯度大小縮放每個參數的步長,但這並不能取代學習率衰減:當梯度雜訊持續存在時,m ^ t / s ^ t \hat{m}_t / \sqrt{\hat{s}_t} m ^ t / s ^ t 不會自動趨近於零,參數仍會在最小值附近晃動。實務上,Adam 也常搭配學習率排程(例如暖身加餘弦衰減)使用。
8.8 正規化加速訓練Normalization
這一節的正規化(normalization)是指調整數值的平均與尺度,目的是讓最佳化更快;它與第 9 章 處理過擬合的正則化(regularization)是不同的概念。
輸入標準化
若各輸入特徵的尺度差異很大,例如一個特徵介於 0 到 1、另一個介於 0 到 1000,損失曲面就會在某些方向特別陡、某些方向特別平,等高線呈細長的橢圓,梯度下降只能用小學習率走出鋸齒狀的路徑。把每個特徵標準化為平均 0、變異數 1:
x ← x − μ σ
x \leftarrow \frac{x - \mu}{\sigma}
x ← σ x − μ 可以讓等高線更接近圓形,從各個方向到最小值的距離與曲率變得相近,梯度方向更直接地指向最小值,因此能使用較大的學習率,收斂也更快。這個做法對批次與小批次梯度下降同樣有效。要注意,μ \mu μ 與 σ \sigma σ 必須由訓練集計算,再以同一組數值 套用到開發集與測試集。
批次正規化
批次正規化(batch normalization, BN)把同樣的想法延伸到隱藏層:既然標準化輸入能加速學習,那麼把每一層的輸入(也就是前一層的輸出)也標準化,應該同樣有幫助。
對某一層的一個小批次 { z ( 1 ) , … , z ( B ) } \{z^{(1)}, \dots, z^{(B)}\} { z ( 1 ) , … , z ( B ) } (z z z 是激勵函數之前的線性輸出),逐個神經元計算:
μ B = 1 B ∑ i = 1 B z ( i ) , σ B 2 = 1 B ∑ i = 1 B ( z ( i ) − μ B ) 2 z ^ ( i ) = z ( i ) − μ B σ B 2 + ϵ z ~ ( i ) = γ z ^ ( i ) + β
\begin{aligned}
\mu_B &= \frac{1}{B} \sum_{i=1}^{B} z^{(i)}, \qquad \sigma_B^2 = \frac{1}{B} \sum_{i=1}^{B} \left(z^{(i)} - \mu_B\right)^2 \\
\hat{z}^{(i)} &= \frac{z^{(i)} - \mu_B}{\sqrt{\sigma_B^2 + \epsilon}} \\
\tilde{z}^{(i)} &= \gamma\, \hat{z}^{(i)} + \beta
\end{aligned}
μ B z ^ ( i ) z ~ ( i ) = B 1 i = 1 ∑ B z ( i ) , σ B 2 = B 1 i = 1 ∑ B ( z ( i ) − μ B ) 2 = σ B 2 + ϵ z ( i ) − μ B = γ z ^ ( i ) + β 再把 z ~ ( i ) \tilde{z}^{(i)} z ~ ( i ) 送進激勵函數。γ \gamma γ 與 β \beta β 是可學習的參數,和權重一樣以梯度下降更新(這裡的 β \beta β 與 Adam 的 β 1 , β 2 \beta_1, \beta_2 β 1 , β 2 無關)。它們讓網路自行決定每個神經元輸出的平均與尺度,而不是強迫所有層都維持平均 0、變異數 1;若學到的 γ \gamma γ 與 β \beta β 恰好等於原本的標準差與平均,正規化的效果就被完全還原。此外,減去平均會消去常數項,因此使用 BN 的層可以省略偏差項 b b b ,由 β \beta β 取代它的角色。
為什麼有效
讓損失曲面更好走 :與輸入標準化一樣,各層輸入的尺度維持穩定,可以使用較大的學習率,對權重初始化也較不敏感。
降低內部共變量偏移 :Ioffe 與 Szegedy(2015)提出 BN 時的解釋是,訓練過程中前面各層的參數不斷改變,使後面各層的輸入分布跟著變動,稱為內部共變量偏移(internal covariate shift)。BN 固定了每層輸入的平均與變異數,讓後面的層較不受前面層變化的影響,各層得以更獨立地學習。
輕微的正則化效果 :μ B \mu_B μ B 與 σ B 2 \sigma_B^2 σ B 2 由每個小批次估計,會隨小批次而變動,等於在每一層的輸出加入了一些雜訊,效果類似 dropout。但這個效果很小,不應把 BN 當作正則化的主要手段;批次越大,雜訊越小,正則化效果也越弱。
補充Note
「降低內部共變量偏移」這個解釋至今仍有爭議。Santurkar 等人(2018)的實驗顯示,BN 不一定真的減少了層間分布的變動,他們認為 BN 更主要的作用是讓損失曲面更平滑、梯度更穩定。BN 有效是公認的事實,但它「為什麼」有效,仍是一個開放的研究問題。
測試時的做法
測試時常常一次只預測一筆資料,無法計算小批次的平均與變異數;即使可以,也不希望單筆預測的結果受到同批其他資料影響。因此訓練期間會用指數加權平均,持續追蹤每一層的 μ \mu μ 與 σ 2 \sigma^2 σ 2 :
μ run ← ρ μ run + ( 1 − ρ ) μ B σ run 2 ← ρ σ run 2 + ( 1 − ρ ) σ B 2
\begin{aligned}
\mu_{\text{run}} &\leftarrow \rho\, \mu_{\text{run}} + (1-\rho)\, \mu_B \\
\sigma^2_{\text{run}} &\leftarrow \rho\, \sigma^2_{\text{run}} + (1-\rho)\, \sigma_B^2
\end{aligned}
μ run σ run 2 ← ρ μ run + ( 1 − ρ ) μ B ← ρ σ run 2 + ( 1 − ρ ) σ B 2 測試時改用這組固定的 μ run \mu_{\text{run}} μ run 與 σ run 2 \sigma^2_{\text{run}} σ run 2 進行正規化,再乘上學到的 γ \gamma γ 、加上 β \beta β 。各深度學習框架都會自動處理這件事,前提是正確切換「訓練模式」與「推論模式」,這是實務上常見的錯誤來源。當小批次很小時(例如受限於記憶體只能用 2 到 4 筆),批次統計量的估計很不穩定,這時可以改用不依賴批次的層正規化(layer normalization)或群組正規化(group normalization)。
8.9 參考資料References
Goodfellow, Bengio & Courville, Deep Learning , MIT Press, 2016,第 8 章 Optimization for Training Deep Models。
Sebastian Ruder, "An overview of gradient descent optimization algorithms", arXiv:1609.04747, 2016。
Kingma & Ba, "Adam: A Method for Stochastic Optimization", ICLR 2015。
Sutskever, Martens, Dahl & Hinton, "On the importance of initialization and momentum in deep learning", ICML 2013。
Dauphin et al., "Identifying and attacking the saddle point problem in high-dimensional non-convex optimization", NIPS 2014。
Ioffe & Szegedy, "Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift", ICML 2015;Santurkar et al., "How Does Batch Normalization Help Optimization?", NeurIPS 2018。
Smith, Kindermans, Ying & Le, "Don't Decay the Learning Rate, Increase the Batch Size", ICLR 2018;Loshchilov & Hutter, "SGDR: Stochastic Gradient Descent with Warm Restarts", ICLR 2017。
Nocedal & Wright, Numerical Optimization , 2nd ed., Springer, 2006(BFGS 與 L-BFGS)。
deeplearning.ai, Improving Deep Neural Networks: Hyperparameter Tuning, Regularization and Optimization (Coursera 深度學習專項課程第二門課);Stanford CS231n。