天天看點

【從零開始學習深度學習】39. 梯度下降優化之動量法介紹及其Pytorch實作1. 梯度下降中的問題2. 動量法介紹及原理3. 從零實作動量法4. 基于Pytorch簡潔實作動量法總結

動量法的提出主要是為了優化在多變量目标函數中不同自變量梯度下降過程中更新速度快慢不均的問題,并且使目标函數向最優解更快移動。

目錄

  • 1. 梯度下降中的問題
  • 2. 動量法介紹及原理
    • 2.1 動量法的數學解釋---指數權重移動平均
    • 2.2 由指數權重移動平均了解動量法
  • 3. 從零實作動量法
  • 4. 基于Pytorch簡潔實作動量法
  • 總結

1. 梯度下降中的問題

假設輸入為二維向量 x = [ x 1 , x 2 ] ⊤ \boldsymbol{x} = [x_1, x_2]^\top x=[x1​,x2​]⊤,輸出為标量的目标函數 f ( x ) = 0.1 x 1 2 + 2 x 2 2 f(\boldsymbol{x})=0.1x_1^2+2x_2^2 f(x)=0.1x12​+2x22​。下面實作基于這個目标函數的梯度下降,并示範使用學習率為 0.4 0.4 0.4時自變量的疊代軌迹。

%matplotlib inline
import sys 
import d2lzh_pytorch as d2l
import torch

eta = 0.4 # 學習率

def f_2d(x1, x2):
    return 0.1 * x1 ** 2 + 2 * x2 ** 2

def gd_2d(x1, x2, s1, s2):
    # 自變量更新x-eta*dx
    return (x1 - eta * 0.2 * x1, x2 - eta * 4 * x2, 0, 0)

d2l.show_trace_2d(f_2d, d2l.train_2d(gd_2d))
           

輸出:

epoch 20, x1 -0.943467, x2 -0.000073
           
【從零開始學習深度學習】39. 梯度下降優化之動量法介紹及其Pytorch實作1. 梯度下降中的問題2. 動量法介紹及原理3. 從零實作動量法4. 基于Pytorch簡潔實作動量法總結

可以看到,同一位置上,目标函數在豎直方向( x 2 x_2 x2​軸方向)比在水準方向( x 1 x_1 x1​軸方向)的斜率的絕對值更大。是以,給定學習率,梯度下降疊代自變量時會使自變量在豎直方向比在水準方向移動幅度更大。那麼,我們需要一個較小的學習率進而避免自變量在豎直方向上越過目标函數最優解。然而,這會造成自變量在水準方向上朝最優解移動變慢。

下面我們試着将學習率調得稍大一點,此時自變量在豎直方向不斷越過最優解并逐漸發散。

eta = 0.6
d2l.show_trace_2d(f_2d, d2l.train_2d(gd_2d))
           

輸出:

epoch 20, x1 -0.387814, x2 -1673.365109
           
【從零開始學習深度學習】39. 梯度下降優化之動量法介紹及其Pytorch實作1. 梯度下降中的問題2. 動量法介紹及原理3. 從零實作動量法4. 基于Pytorch簡潔實作動量法總結

2. 動量法介紹及原理

動量法的提出是為了解決梯度下降的上述問題。設時間步 t t t的自變量為 x t \boldsymbol{x}_t xt​,學習率為 η t \eta_t ηt​。在時間步 0 0 0,動量法建立速度變量 v 0 \boldsymbol{v}_0 v0​,并将其元素初始化成0。在時間步 t > 0 t>0 t>0,動量法對每次疊代的步驟做如下修改:

v t ← γ v t − 1 + η t g t , x t ← x t − 1 − v t , \begin{aligned} \boldsymbol{v}_t &\leftarrow \gamma \boldsymbol{v}_{t-1} + \eta_t \boldsymbol{g}_t, \\ \boldsymbol{x}_t &\leftarrow \boldsymbol{x}_{t-1} - \boldsymbol{v}_t, \end{aligned} vt​xt​​←γvt−1​+ηt​gt​,←xt−1​−vt​,​

其中,動量超參數 γ \gamma γ滿足 0 ≤ γ < 1 0 \leq \gamma < 1 0≤γ<1。 g t \boldsymbol{g}_t gt​為小批量随機梯度。當 γ = 0 \gamma=0 γ=0時,動量法等價于小批量随機梯度下降。

我們先觀察一下梯度下降在使用動量法後的疊代軌迹。

def momentum_2d(x1, x2, v1, v2):
    v1 = gamma * v1 + eta * 0.2 * x1
    v2 = gamma * v2 + eta * 4 * x2
    return x1 - v1, x2 - v2, v1, v2

eta, gamma = 0.4, 0.5
d2l.show_trace_2d(f_2d, d2l.train_2d(momentum_2d))
           

輸出:

epoch 20, x1 -0.062843, x2 0.001202
           
【從零開始學習深度學習】39. 梯度下降優化之動量法介紹及其Pytorch實作1. 梯度下降中的問題2. 動量法介紹及原理3. 從零實作動量法4. 基于Pytorch簡潔實作動量法總結

可以看到使用較小的學習率 η = 0.4 \eta=0.4 η=0.4和動量超參數 γ = 0.5 \gamma=0.5 γ=0.5時,動量法在豎直方向上的移動更加平滑,且在水準方向上更快逼近最優解。下面使用較大的學習率 η = 0.6 \eta=0.6 η=0.6,此時自變量也不再發散。

eta = 0.6
d2l.show_trace_2d(f_2d, d2l.train_2d(momentum_2d))
           

輸出:

epoch 20, x1 0.007188, x2 0.002553
           
【從零開始學習深度學習】39. 梯度下降優化之動量法介紹及其Pytorch實作1. 梯度下降中的問題2. 動量法介紹及原理3. 從零實作動量法4. 基于Pytorch簡潔實作動量法總結

2.1 動量法的數學解釋—指數權重移動平均

為了從數學上了解動量法,讓我們先解釋一下指數權重移動平均(exponentially weighted moving average)。給定超參數 0 ≤ γ < 1 0 \leq \gamma < 1 0≤γ<1,目前時間步 t t t的變量 y t y_t yt​是上一時間步 t − 1 t-1 t−1的變量 y t − 1 y_{t-1} yt−1​和目前時間步另一變量 x t x_t xt​的線性組合:

y t = γ y t − 1 + ( 1 − γ ) x t . y_t = \gamma y_{t-1} + (1-\gamma) x_t. yt​=γyt−1​+(1−γ)xt​.

我們可以對 y t y_t yt​展開:

y t = ( 1 − γ ) x t + γ y t − 1 = ( 1 − γ ) x t + ( 1 − γ ) ⋅ γ x t − 1 + γ 2 y t − 2 = ( 1 − γ ) x t + ( 1 − γ ) ⋅ γ x t − 1 + ( 1 − γ ) ⋅ γ 2 x t − 2 + γ 3 y t − 3 … \begin{aligned} y_t &= (1-\gamma) x_t + \gamma y_{t-1}\\ &= (1-\gamma)x_t + (1-\gamma) \cdot \gamma x_{t-1} + \gamma^2y_{t-2}\\ &= (1-\gamma)x_t + (1-\gamma) \cdot \gamma x_{t-1} + (1-\gamma) \cdot \gamma^2x_{t-2} + \gamma^3y_{t-3}\\ &\ldots \end{aligned} yt​​=(1−γ)xt​+γyt−1​=(1−γ)xt​+(1−γ)⋅γxt−1​+γ2yt−2​=(1−γ)xt​+(1−γ)⋅γxt−1​+(1−γ)⋅γ2xt−2​+γ3yt−3​…​

令 n = 1 / ( 1 − γ ) n = 1/(1-\gamma) n=1/(1−γ),那麼 ( 1 − 1 / n ) n = γ 1 / ( 1 − γ ) \left(1-1/n\right)^n = \gamma^{1/(1-\gamma)} (1−1/n)n=γ1/(1−γ)。因為

lim ⁡ n → ∞ ( 1 − 1 n ) n = exp ⁡ ( − 1 ) ≈ 0.3679 , \lim_{n \rightarrow \infty} \left(1-\frac{1}{n}\right)^n = \exp(-1) \approx 0.3679, n→∞lim​(1−n1​)n=exp(−1)≈0.3679,

是以當 γ → 1 \gamma \rightarrow 1 γ→1時, γ 1 / ( 1 − γ ) = exp ⁡ ( − 1 ) \gamma^{1/(1-\gamma)}=\exp(-1) γ1/(1−γ)=exp(−1),如 0.9 5 20 ≈ exp ⁡ ( − 1 ) 0.95^{20} \approx \exp(-1) 0.9520≈exp(−1)。如果把 exp ⁡ ( − 1 ) \exp(-1) exp(−1)當作一個比較小的數,我們可以在近似中忽略所有含 γ 1 / ( 1 − γ ) \gamma^{1/(1-\gamma)} γ1/(1−γ)和比 γ 1 / ( 1 − γ ) \gamma^{1/(1-\gamma)} γ1/(1−γ)更高階的系數的項。例如,當 γ = 0.95 \gamma=0.95 γ=0.95時,

y t ≈ 0.05 ∑ i = 0 19 0.9 5 i x t − i . y_t \approx 0.05 \sum_{i=0}^{19} 0.95^i x_{t-i}. yt​≈0.05i=0∑19​0.95ixt−i​.

是以,在實際中,我們常常将 y t y_t yt​看作是對最近 1 / ( 1 − γ ) 1/(1-\gamma) 1/(1−γ)個時間步的 x t x_t xt​值的權重平均。例如,當 γ = 0.95 \gamma = 0.95 γ=0.95時, y t y_t yt​可以被看作對最近20個時間步的 x t x_t xt​值的權重平均;當 γ = 0.9 \gamma = 0.9 γ=0.9時, y t y_t yt​可以看作是對最近10個時間步的 x t x_t xt​值的權重平均。而且,離目前時間步 t t t越近的 x t x_t xt​值獲得的權重越大(越接近1)。

2.2 由指數權重移動平均了解動量法

現在,我們對動量法的速度變量做變形:

v t ← γ v t − 1 + ( 1 − γ ) ( η t 1 − γ g t ) . \boldsymbol{v}_t \leftarrow \gamma \boldsymbol{v}_{t-1} + (1 - \gamma) \left(\frac{\eta_t}{1 - \gamma} \boldsymbol{g}_t\right). vt​←γvt−1​+(1−γ)(1−γηt​​gt​).

由指數權重移動平均的形式可得,速度變量 v t \boldsymbol{v}_t vt​實際上對序列 { η t − i g t − i / ( 1 − γ ) : i = 0 , … , 1 / ( 1 − γ ) − 1 } \{\eta_{t-i}\boldsymbol{g}_{t-i} /(1-\gamma):i=0,\ldots,1/(1-\gamma)-1\} {ηt−i​gt−i​/(1−γ):i=0,…,1/(1−γ)−1}做了指數權重移動平均。換句話說,相比于小批量随機梯度下降,動量法在每個時間步的自變量更新量近似于将最近 1 / ( 1 − γ ) 1/(1-\gamma) 1/(1−γ)個時間步的普通更新量(即學習率乘以梯度)做了指數權重移動平均後再除以 1 − γ 1-\gamma 1−γ。是以,在動量法中,自變量在各個方向上的移動幅度不僅取決目前梯度,還取決于過去的各個梯度在各個方向上是否一緻。這樣,我們就可以使用較大的學習率,進而使自變量向最優解更快移動。

3. 從零實作動量法

相對于小批量随機梯度下降,動量法需要對每一個自變量維護一個同它一樣形狀的速度變量,且超參數裡多了動量超參數。實作中,我們将速度變量用更廣義的狀态變量

states

表示。

features, labels = d2l.get_data_ch7()

def init_momentum_states():
    v_w = torch.zeros((features.shape[1], 1), dtype=torch.float32)
    v_b = torch.zeros(1, dtype=torch.float32)
    return (v_w, v_b)

def sgd_momentum(params, states, hyperparams):
    for p, v in zip(params, states):
        v.data = hyperparams['momentum'] * v.data + hyperparams['lr'] * p.grad.data
        p.data -= v.data
           

我們先将動量超參數

momentum

設0.5,這時可以看成是特殊的小批量随機梯度下降:其小批量随機梯度為最近2個時間步的2倍小批量梯度的權重平均。

d2l.train_ch7(sgd_momentum, init_momentum_states(),
              {'lr': 0.02, 'momentum': 0.5}, features, labels)
           

輸出:

loss: 0.245518, 0.042304 sec per epoch
           
【從零開始學習深度學習】39. 梯度下降優化之動量法介紹及其Pytorch實作1. 梯度下降中的問題2. 動量法介紹及原理3. 從零實作動量法4. 基于Pytorch簡潔實作動量法總結

将動量超參數

momentum

增大到0.9,這時依然可以看成是特殊的小批量随機梯度下降:其小批量随機梯度為最近10個時間步的10倍小批量梯度的權重平均。我們先保持學習率0.02不變。

d2l.train_ch7(sgd_momentum, init_momentum_states(),
              {'lr': 0.02, 'momentum': 0.9}, features, labels)
           

輸出:

loss: 0.252046, 0.095708 sec per epoch
           
【從零開始學習深度學習】39. 梯度下降優化之動量法介紹及其Pytorch實作1. 梯度下降中的問題2. 動量法介紹及原理3. 從零實作動量法4. 基于Pytorch簡潔實作動量法總結

可見目标函數值在後期疊代過程中的變化不夠平滑。直覺上,10倍小批量梯度比2倍小批量梯度大了5倍,我們可以試着将學習率減小到原來的1/5。此時目标函數值在下降了一段時間後變化更加平滑。

d2l.train_ch7(sgd_momentum, init_momentum_states(),
              {'lr': 0.004, 'momentum': 0.9}, features, labels)
           

輸出:

loss: 0.242905, 0.073496 sec per epoch
           
【從零開始學習深度學習】39. 梯度下降優化之動量法介紹及其Pytorch實作1. 梯度下降中的問題2. 動量法介紹及原理3. 從零實作動量法4. 基于Pytorch簡潔實作動量法總結

4. 基于Pytorch簡潔實作動量法

在PyTorch中,隻需要通過參數

momentum

來指定動量超參數即可使用動量法。

d2l.train_pytorch_ch7(torch.optim.SGD, {'lr': 0.004, 'momentum': 0.9},
                    features, labels)
           

輸出:

loss: 0.253280, 0.060247 sec per epoch
           
【從零開始學習深度學習】39. 梯度下降優化之動量法介紹及其Pytorch實作1. 梯度下降中的問題2. 動量法介紹及原理3. 從零實作動量法4. 基于Pytorch簡潔實作動量法總結

總結

  • 動量法使用了指數權重移動平均的思想。它将過去時間步的梯度做了權重平均,且權重按時間步指數衰減。
  • 動量法使得相鄰時間步的自變量更新在方向上更加一緻。

如果文章内容對你有幫助,感謝點贊+關注!

歡迎關注下方GZH:阿旭算法與機器學習,共同學習交流~

繼續閱讀