深度學習(六)損失函數(shù)和優(yōu)化器

思考一下,神經(jīng)網(wǎng)絡是靠什么來判斷得到的結果是好還是壞呢?

答案是通過比較神經(jīng)網(wǎng)絡自己預測的輸出與真實標簽的差距,也就是Loss函數(shù)。從數(shù)學上推導loss函數(shù)可以看這里。

為了找到最小的loss,通常采用的是梯度下降(Gradient Descent)的方法。

記loss為 L(w) = \frac{1}{M}\sum_{m=1}^{M} L(w,x_m,y_m), M 是樣本的數(shù)量。

一、梯度下降 Gradient Descent

梯度下降的思想是計算loss的梯度, 并取當前點的梯度值的負數(shù),由梯度的定義可以直到,負的梯度指向區(qū)域最小值,如果是正值就是區(qū)域最大值。步長由學習率\eta決定,k+1次的權重值就是第k次的權重減去學習率乘梯度:
w^{k+1} = w^k - \eta\nabla L(w^{k},x,y)

By Gradient_descent.png: The original uploader was Olegalexandrov at English Wikipedia.derivative work: Zerodamage - This file was derived from: Gradient descent.png:, Public Domain, https://commons.wikimedia.org/w/index.php?curid=20569355

1. 批梯度下降 Batch gradient descent

上面的公式使用了數(shù)據(jù)集里的所有的樣本來計算梯度,這種方法被稱為批梯度下降(Batch gradient descent)。
如果我們每次迭代只計算一次梯度,那整體梯度下降的就太慢了,并且可能因為數(shù)據(jù)量太大,內(nèi)存放不下。不過對于凸誤差面,批梯度下降可以保證收斂到全局最小值,對于非凸面,可以保證收斂到局部最小值。
w = w- \eta\nabla_w L(w)

2. 隨機梯度下降 Stochastic gradient descent

隨機梯度下降每次只用一個樣本更新參數(shù),因此梯度下降的非常快,但不是很穩(wěn)定。SGD不會像批梯度下降一樣向最優(yōu)點逼近,可能會在最優(yōu)點附近震蕩。不過也有人說如果逐漸降低學習率,SGD也會想BGD一樣收斂到全局最小值或者局部最小值。
w = w- \eta\nabla_w L(w;x_i, y_i)

3. Mini-batch gradient descent

Mini-batch gradient descent是每次用B個樣本來更新參數(shù),B=1的時候是SGD,B=M的時候就是BGD。這樣做可以得到一個更加穩(wěn)定的梯度下降,同時也減少了需要計算的參數(shù),提高了優(yōu)化效率。一般來說B會取2的冪,比如2,4,8,16,32,64,128等,有利于GPU的加速。
w = w- \eta (\frac{1}{B}\nabla_w \sum_{b=1}^B L(w;x_b, y_b))
Mini-batch gradient descent 是現(xiàn)在最常用的方法,通常說的SGD指的也就是Mini-batch gradient descent。SGD 的缺點是選擇合適的學習率可能很困難。學習率太小會導致收斂緩慢,而學習率太大會阻礙收斂,并導致?lián)p失函數(shù)在最小值附近波動甚至發(fā)散。

二、 動量 Gradient Descent With Momentum

1. 動量 Momentum

上面提到了SGD的一個缺點,就是在每個更新參數(shù)的時候,不一定是朝著梯度下降的方向,比如下面左邊的圖,我們更希望在水平方向上下降的快一點,而在垂直方向上的變化量小一點。Momentum的作用就是在相關方向上加速SGD并且抑制震蕩。
如果我們增加一個變量來儲存上一步的更新向量:
v^{(k)} = \mu v^{k-1} - \eta \nabla L(w^{k})
w^{k+1}=w^{k}+v^{k}
通常取\mu={0.9,0.95,0.99}

SGD with &without momentum

接下來詳細講一下momentum的作用:




梯度向量

從圖上可以看到藍色的箭頭是不加動量時參數(shù)要指向的方向, 紅色箭頭是上一次的梯度下降指向的方向,如果是區(qū)域最小值在水平方向的右邊,那么綠色箭頭會比藍色箭頭更快到達區(qū)域最小值。
Momentum的優(yōu)點在于可以抑制震蕩,加速收斂,缺點是需要調(diào)整學習率。

2. Nesterov Accelerated Gradient

Nesterov Accelerated Gradient 在momentum上做了一些改變, 計算了下一次參數(shù)的loss,計算了梯度的二階梯度,所以比SGD+momentum收斂地更快[5]。
v^k = \mu v^{k-1}- \eta \nabla L(w^k + \mu v^{k-1})
w^{k+1}=w^k+v^k
這塊數(shù)學沒怎么懂,總之它收斂比momentum更快。

3. AdaGrad

上面的優(yōu)化方法都用的是固定的學習率,缺點就是需要自己調(diào)參數(shù)找最適合的學習率,所以我們要想辦法,讓學習率在優(yōu)化過程中一起優(yōu)化了。
AdaGrad 全稱Adpative Gradient,方法如下, 這里的*代表的是矩陣元素間的乘法:
g^k = \nabla L(w^k)
r^k =r^{k-1}+g^k * g^k
w^{k+1}=w_{k}- \frac{\eta}{\sqrt{r^k}+ \varepsilon} * g^k
可以看到與SGD的更新規(guī)則相比,上面公式里與學習率相同作用的,是一個基于梯度和上一次迭代里學習率的值。
Adagrad的主要好處之一是,它無需手動調(diào)整學習速度。 大多數(shù)實現(xiàn)使用默認值0.01并將其保留為默認值。缺點就是學習率下降的太快了。

4. RMSprop

g^k = \nabla L(w^k)
r^k = \rho r^{k-1}+(1- \rho)g^k * g^k
w^{k+1}=w_{k}- \frac{\eta}{\sqrt{r^k}+ \varepsilon} * g^k
通常取\rho = 0.9, \eta=0.001
RMSprop的優(yōu)點是不會讓學習率單調(diào)變小。

5. Adam

Adaptive Moment Estimation
g_k = \nabla L(w^k)
v_k=\mu v^{k-1}+(1- \mu)g^k
r^k = \rho r^{k-1} +(1-\rho)g^k * g^k
通常\mu = 0.9, \rho=0.999, \eta=0.001
Adam 看起來是RMSprop和 momentum的結合版, 一般推薦使用Adam來訓練。

6. AMSGrad

Adam 有時候被觀察到不能收斂到最優(yōu)解,于是就有人提出了解決方案:
v^{k}=max(v^{k-1}, v^k)
是不是比Adam更好還不確定。

小結

最常用的兩種優(yōu)化方法是SGD+momentum 和Adam,兩種方法各有優(yōu)缺點:

  • SGD + Nesterov momentum + 學習率衰減
    優(yōu)點: 可以穩(wěn)定的收斂,現(xiàn)在依舊被許多paper使用
    缺點:需要調(diào)整學習率衰減
  • Adam
    優(yōu)點:自適應的學習率
    缺點:loss曲線很難解釋

三、Pytorch實現(xiàn)

pytorch實現(xiàn)SGD和Adam

import torch.optim as optim

optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.9)
# optimizer = optim.Adam(model.parameters(), lr=learning_rate)

for input, target in dataset:
    # set zero gradient
    optimizer.zero_grad()
    output = model(input)
    loss = loss_fn(output, target)
    loss.backward()
    # take optimization step
    optimizer.step()

如果想只優(yōu)化特定的一些參數(shù):

params_to_update = []
for name, param in self.model.named_parameters():
    if param.requires_grad == True:
        params_to_update.append(param)

為不同參數(shù)設置不同的學習率

optimizer = torch.optim.SGD([
            {'params': model.parameters()},
            {'params': model.s, 'lr':1e-3}
        ], lr=learning_rate)

為不同的參數(shù)設置不同的優(yōu)化器:

class MultipleOptimizer(object):
    def __init__(self, *op):
        self.optimizers = op

    def zero_grad(self):
        for op in self.optimizers:
            op.zero_grad()

    def step(self):
        for op in self.optimizers:
            op.step()

 optimizer = MultipleOptimizer(optim.SGD([model.s], lr=0.01),
              optim.Adam(model.parameters(), lr=learning_rate))
optimizer.zero_grad()
loss.backward()
optimizer.step()

如果想要根據(jù)訓練的表現(xiàn)調(diào)節(jié)學習率,可以使用torch.optim.lr_scheduler里的函數(shù),可以根據(jù)各種條件調(diào)節(jié)學習率的變化。

Reference

  1. 幾種梯度下降方法對比
  2. An overview of gradient descent optimization algorithms
  3. Gradient Descent with Momentum
  4. pytorch文檔
  5. 比Momentum更快:揭開Nesterov Accelerated Gradient的真面目
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容