【深度學習】神經網路的優化方法
前言
我們都知道,神經網路的學習目的是找到使損失函式的值儘可能小的引數,這是一個尋找最優引數的問題,解決這個問題的過程可以稱為最優化,但由於引數空間非常複雜,無法輕易找到最優解,而且在深度學習中,引數的數量非常大,導致最優化問題更加複雜。
在這之前,我們是將引數的梯度(導數)作為線索,使引數沿著梯度方向更新,並重復執行多次,從而逐漸靠近最優引數,這個過程稱為隨機梯度下降法(SGD)
SGD是一個簡單的方法,但是比起胡亂地搜尋引數空間,也算是“聰明”的方法
但是,針對不同的問題,SGD並不一定是最好的,SGD也存在自身的缺陷
下面我們介紹幾種常用的最優化方法,並python實現
SGD
- 數學表示式
為權重引數, 為學習率, 表示損失函式關於 的梯度
一般 取 0.01 或 0.001
- python實現
class SGD:
"""隨機梯度下降法"""
def __init__(self, lr=0.01):
self.lr = lr # 學習率
# params,grads是字典型資料,比如params['W1'],grads['W1']
# 儲存了權重引數和他們的梯度
def update(self, params, grads):
for key in params.keys():
# 根據SGD公式
params[key] -= self.lr * grads[key]
- 神經網路中如何使用
# 構建網路
network = TwoLayerNet(...)
# 選擇最優化方法(如果需要其他方法,可以改用其他的方法)
optimizer = SGD()
# 學習
for i in range(10000):
# 批資料
x_batch, t_batch = get_mini_batch(...)
# 求梯度
grads = network.gradient(x_batch, t_batch)
# 權重引數
params = network.params
# 優化,更新權重引數
optimizer.update(params, grads)
上面程式碼是進行神經網路引數更新的一般步驟,其中optimizer 就是進行引數最優化的物件
- 如果我們有了新的最優化方法,就可以建立一個和SGD類似的類,裡面也包括update方法,這樣只需要將上面程式碼中的optimizer = SGD()換成自己定義的類
- SGD的缺點
如果,梯度方向不指向最小值的方向,就會以相對低效的路徑更新,出現如圖的“之”型
容易收斂到區域性最優,並且容易被困在鞍點
為了改正SGD的缺陷,出現了Momentum、AdaGrad、Adam等方法
Momentum
- 數學表示式
這裡出現了一個新變數 ,對應物理上的速度
式中, 這一項表示在物體不受任何力時,該項承擔物體逐漸減速的任務( 設定為0.9之類)
一開始,梯度為負,小球加速向下執行(梯度可以表示為加速度),並且加速度減小
當梯度為0(加速度為0),由於 ,速度逐漸減小,也就是小球還會往右邊運動,這樣避免陷入區域性最小值
- python實現
class Momentum:
"""Momentum SGD"""
def __init__(self, lr=0.01, momentum=0.9):
self.lr = lr # 學習率
self.momentum = momentum #動量
self.v = None # 什麼都不儲存,第一次呼叫update時,會以字典形式儲存與引數結構相同的資料
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]
- 評價
和SGD相比,Momentum可以更快地朝最小值靠近,減弱“之”字型的變動程度
AdaGrad
在神經網路學習中,學習率的值很重要。學習率過小,會導致學習花費過多時間;學習率過大,會導致學習發散而不能正確進行
- 學習率衰減
隨著學習的進行,是學習率逐漸減小,一開始“多”學,然後逐漸“少”學
上面學習率衰減是針對所有引數的,而AdaGrad是針對“每個”引數,賦予其“定製”的值,也就是每個引數的學習率不同;AdaGrad會為每個元素適當的調整學習率
- 數學表示式
表示對應矩陣元素相乘,在更新引數時,通過更改 就可以調整學習的尺度
這意味著,引數的元素中變化較大的元素的學習率將變小,這樣就可以按引數的元素進行學習率衰減,使變動大的引數的學習率逐漸減小
- python實現
class AdaGrad:
"""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為了避免有0
Adam
Adam的理論有些複雜,直觀上,就是上面兩種方法的結合。
-
數學表示式