深度學習入門--引數更新的優化
神經網路學習的目的是找到使損失函式的值儘可能小的引數。這是尋找最優引數的問題,解決這個問題的過程稱為 最優化 。而在深度神經網路中,引數的數量非常龐大,最優化問題也就十分複雜。
之前我們學過 隨機梯度下降法(SGD) 來尋找最優引數,大致思路是:將引數的梯度作為線索,沿梯度方向更新引數,並重復步驟多次,從而逐漸靠近最優引數。
在隨機梯度下降法中有兩種求梯度的方式:
- 利用數值微分求梯度(簡單、速度慢)
- 利用誤差反向傳播法求梯度(複雜、速度快)
將SGD實現為一個Python類:
class SGD: def __init__(self, lr=0.01): self.lr = lr# 學習率 def update(self, params, grads): for key in params.keys(): params[key] -= self.lr * grads[key]
雖然SGD簡單,但在解決某些問題時,也會顯得十分低效。低效的根本原因是:梯度的方向並沒有指向最小值的方法。
為了避免SGD的缺點,還有Momentum、AdaGrad、Adam這三種方法可以取代SGD。。
Momentum
Momentum是“動量”的意思,和物理有關。用Python表示如下:
class Momentum: def __init__(self, lr=0.01, momentum=0.9): self.lr = lr self.momentum = momentum self.v = None def update(self, params, grads): if self.v is None self.v = {} for key, val, in params.items(): self.v[key] = np.zeros_like(val) for key in params.keys(): self.v[key] = self.momentum*self.v[key] - self.lr*grads[key] params[key] += self.v[key]
AdaGrad
在神經網路的學習中,學習率的值 lr
很重要,學習率過小,會導致學習花費過多時間;反過來,學習率過大會導致學習發散而不能正確進行。
在關於學習率的有效技巧中,有一種被稱為 學習率衰減 的方法,即隨著學習的進行,學習率不斷減小。AdaGrad正是基於這種思想,AdaGrad會為引數的每一個元素適當的調整學習率。
class AdaGrad: def __init__(self, lr=0.01): self.lr = lr self.h = None def update(self, params, grads): if self.h is None: self.h = {} for key, val in params.items(): self.h[key] = np.zeros_like(val) for key in params.keys(): self.h[key] += grads[key] * grads[key] params[key] -= self.lr * grads[key] / (np.sqrt(self.h[key]) + 1e-7) # 加上微小值1e-7,防止除數為零
Adam
Adam融合了Momentum和AdaGrad兩個方法的優點,有望實現引數空間的高效搜尋。此外,進行超引數的“偏置校正”也是Adam的特徵。
class Adam: def __init__(self, lr=0.001, beta1=0.9, beta2=0.999): self.lr = lr self.beta1 = beta1 self.beta2 = beta2 self.iter = 0 self.m = None self.v = None def update(self, params, grads): if self.m is None: self.m, self.v = {}, {} for key, val in params.items(): self.m[key] = np.zeros_like(val) self.v[key] = np.zeros_like(val) self.iter += 1 lr_t= self.lr * np.sqrt(1.0 - self.beta2**self.iter) / (1.0 - self.beta1**self.iter) for key in params.keys(): self.m[key] += (1 - self.beta1) * (grads[key] - self.m[key]) self.v[key] += (1 - self.beta2) * (grads[key]**2 - self.v[key]) params[key] -= lr_t * self.m[key] / (np.sqrt(self.v[key]) + 1e-7)
對於SGD、Momentum、AdaGrad和Adam這四種更新引數的方式。它們各自的最優化的更新路徑如下圖:

其中SGD最簡單,使用也最廣泛。Adam的綜合性能最好,最受歡迎。
我們以MNIST手寫數字集為例,比較SGD、Momentum、AdaGrad和Adam這四種方法在學習進展上有多大程度的差異:

如上圖所示,SGD的學習進度最慢,損失函式的值最大;Momentum、AdaGrad和Adam這三種方法的學習進度較快,其中AdaGrad的學習效果最好。
每天學習一點點,每天進步一點點。