梯度與梯度下降法

本文主要使用markdown進行編輯的。

概述

在講述梯度下降算法之前,我們先需要了解一下導數(derivative)、偏導數(partial derivative)和方向導數(directional derivative),然後我們看看梯度下降法(Gradient Descent),瞭解爲什麼在優化問題中使用梯度下降法來優化目標函數。

導數

一張關於導數和微分的圖:
這裏寫圖片描述
導數定義如下:

f(x0)=limΔx0ΔyΔx=limΔx0f(x0+Δxf(x0))Δx

反映的是函數y=f(x)在某一點處沿x軸正方向的變化率。再強調一遍,是函數f(x)在x軸上某一點處沿着x軸正方向的變化率/變化趨勢。直觀地看,也就是在x軸上某一點處,如果f’(x)>0,說明f(x)的函數值在x點沿x軸正方向是趨於增加的;如果f’(x)<0,說明f(x)的函數值在x點沿x軸正方向是趨於減少的。

 這裏補充上圖中的Δy、dy等符號的意義及關係如下:
 Δx:x的變化量;
 dx:x的變化量Δx趨於0時,則記作微元dx;
 Δy:Δy=f(x0+Δx)-f(x0),是函數的增量;
 dy:dy=f’(x0)dx,是切線的增量;
 當Δx→0時,dy與Δy都是無窮小,dy是Δy的主部,即Δy=dy+o(Δx).

導數與偏導數

偏導數的定義如下:

xjf(x0,x1,...,xn)=limΔx0ΔyΔx=limΔx0f(x0,...,xj+Δx,...,xn)f(x0,...,xj,...,xn)Δx

可以看到,導數與偏導數本質是一致的,都是當自變量的變化量趨於0時,函數值的變化量與自變量變化量比值的極限。直觀地說,偏導數也就是函數在某一點上沿座標軸正方向的的變化率。
 區別在於:
 導數,指的是一元函數中,函數y=f(x)在某一點處沿x軸正方向的變化率;
 偏導數,指的是多元函數中,函數y=f(x1,x2,...,xn) 在某一點處沿某一座標軸(x1,x2,...,xn) 正方向的變化率。

導數與方向導數

方向導數的定義如下:

xjf(x0,x1,...,xn)=limρ0ΔyΔx=limρ0f(x0+Δx0,...,xj+Δxj,...,xn+Δxn)f(x0,...,xj,...,xn)ρ

ρ=(Δx0)2+...+(Δxj)2+...+(Δxn)2

在前面導數和偏導數的定義中,均是沿座標軸正方向討論函數的變化率。那麼當我們討論函數沿任意方向的變化率時,也就引出了方向導數的定義,即:某一點在某一趨近方向上的導數值。
 通俗的解釋是:
 我們不僅要知道函數在座標軸正方向上的變化率(即偏導數),而且還要設法求得函數在其他特定方向上的變化率。而方向導數就是函數在其他特定方向上的變化率。

導數與梯度

梯度的定義如下:

gradf(x0,x1,...,xn)=(x0,...,xj,...,xn)

梯度的提出只爲回答一個問題:
 函數在變量空間的某一點處,沿着哪一個方向有最大的變化率?
 梯度定義如下:
 函數在某一點的梯度是這樣一個向量,它的方向與取得最大方向導數的方向一致,而它的模爲方向導數的最大值。
 這裏注意三點:
 1)梯度是一個向量,即有方向有大小;
 2)梯度的方向是最大方向導數的方向;
 3)梯度的值(這裏指的是模,值有點歧義)是某方向上最大方向導數(的值,導數本身就是值)
 
補充:
現在,我們可以來換一個角度看這個問題:從某點出發,沿着所有的路徑我們往下滑,當都在 Z 方向上下降了同樣的高度(比如 1),最陡的那條路徑,在 X-Y 平面上的投影一定是最短的。(不然爲甚叫做最陡?)

梯度下降

梯度下降算法(Gradient Descent)是神經網絡模型訓練最常用的優化算法。
梯度下降算法背後的原理:目標函數J(θ) 關於參數θ 的梯度將是目標函數上升最快的方向。對於最小化優化問題,只需要將參數沿着梯度相反的方向前進一個步長,就可以實現目標函數的下降。即給定一個與參數 θ 有關的目標函數J(θ) , 求使得 J 最小的參數θ .我們把這個步長又稱爲學習速率η 。參數更新公式如下:

θθηθJ(θ)

其中θJ(θ) 是參數的梯度,根據計算目標函數J(θ) 採用數據量的不同,梯度下降算法又可以分爲批量梯度下降算法(Batch Gradient Descent),隨機梯度下降算法(Stochastic GradientDescent)和小批量梯度下降算法(Mini-batch Gradient Descent)。對於批量梯度下降算法,其J(θ) 是在整個訓練集上計算的,如果數據集比較大,可能會面臨內存不足問題,而且其收斂速度一般比較慢。隨機梯度下降算法是另外一個極端,J(θ) 是針對訓練集中的一個訓練樣本計算的,又稱爲在線學習,即得到了一個樣本,就可以執行一次參數更新。所以其收斂速度會快一些,但是有可能出現目標函數值震盪現象,因爲高頻率的參數更新導致了高方差。小批量梯度下降算法是折中方案,選取訓練集中一個小批量樣本計算J(θ) ,這樣可以保證訓練過程更穩定,而且採用批量訓練方法也可以利用矩陣計算的優勢。這是目前最常用的梯度下降算法。

Momentum optimization

衝量梯度下降算法是BorisPolyak在1964年提出的,其基於這樣一個物理事實:將一個小球從山頂滾下,其初始速率很慢,但在加速度作用下速率很快增加,並最終由於阻力的存在達到一個穩定速率。對於衝量梯度下降算法,其更新方程如下:

mγm+ηθJ(θ)

θθm

可以看到,參數更新時不僅考慮當前梯度值,而且加上了一個積累項(衝量),但多了一個超參γ ,一般取接近1的值如0.9。相比原始梯度下降算法,衝量梯度下降算法有助於加速收斂。當梯度與衝量方向一致時,衝量項會增加,而相反時,衝量項減少,因此衝量梯度下降算法可以減少訓練的震盪過程。

NAG

NAG算法全稱Nesterov Accelerated Gradient,是YuriiNesterov在1983年提出的對衝量梯度下降算法的改進版本,其速度更快。其變化之處在於計算“超前梯度”更新衝量項,具體公式如下:

mγm+ηθJ(θγm)

θθm

既然參數要沿着γm 更新,不妨計算未來位置θγm 的梯度,然後合併兩項作爲最終的更新項,其具體效果如圖所示,可以看到一定的加速效果。
這裏寫圖片描述
圖1 NAG效果圖

AdaGrad

AdaGrad是Duchi在2011年提出的一種學習速率自適應的梯度下降算法。在訓練迭代過程,其學習速率是逐漸衰減的,經常更新的參數其學習速率衰減更快,這是一種自適應算法。其更新過程如下:

ss+θJ(θ)θJ(θ)

θθηs+ϵθJ(θ)

其中是梯度平方的積累量,在進行參數更新時,學習速率要除以這個積累量的平方根,其中加上一個很小值是爲了防止除0的出現。由於是該項逐漸增加的,那麼學習速率是衰減的。考慮如圖所示的情況,目標函數在兩個方向的坡度不一樣,如果是原始的梯度下降算法,在接近坡底時收斂速度比較慢。而當採用AdaGrad,這種情況可以被改觀。由於比較陡的方向梯度比較大,其學習速率將衰減得更快,這有利於參數沿着更接近坡底的方向移動,從而加速收斂。
這裏寫圖片描述

前面說到AdaGrad其學習速率實際上是不斷衰減的,這會導致一個很大的問題,就是訓練後期學習速率很小,導致訓練過早停止,因此在實際中AdaGrad一般不會被採用,下面的算法將改進這一致命缺陷。

RMSprop

RMSprop是Hinton在他的課程上講到的,其算是對Adagrad算法的改進,主要是解決學習速率過快衰減的問題。其實思路很簡單,類似Momentum思想,引入一個超參數,在積累梯度平方項進行衰減:

sγs+(1γ)θJ(θ)θJ(θ)

θθηs+ϵθJ(θ)

可以認爲僅僅對距離時間較近的梯度進行積累,其中一般取值0.9,其實這樣就是一個指數衰減的均值項,減少了出現的爆炸情況,因此有助於避免學習速率很快下降的問題。同時Hinton也建議學習速率設置爲0.001。RMSprop是屬於一種比較好的優化算法了。

Adam

Adam全稱Adaptive moment estimation,是Kingma等在2015年提出的一種新的優化算法,其結合了Momentum和RMSprop算法的思想。相比Momentum算法,其學習速率是自適應的,而相比RMSprop,其增加了衝量項。所以,Adam是兩者的結合體。

學習速率

前面也說過學習速率的問題,對於梯度下降算法,這應該是一個最重要的超參數。如果學習速率設置得非常大,那麼訓練可能不會收斂,就直接發散了;如果設置的比較小,雖然可以收斂,但是訓練時間可能無法接受;如果設置的稍微高一些,訓練速度會很快,但是當接近最優點會發生震盪,甚至無法穩定。不同學習速率的選擇影響可能非常大,如圖3所示。
這裏寫圖片描述

不同學習速率的訓練效果

理想的學習速率是:剛開始設置較大,有很快的收斂速度,然後慢慢衰減,保證穩定到達最優點。所以,前面的很多算法都是學習速率自適應的。除此之外,還可以手動實現這樣一個自適應過程,如實現學習速率指數式衰減:

η(t)=η010t/r

代碼示例

import matplotlib
import matplotlib.pyplot as plt
matplotlib.use('Agg')
%matplotlib inline
import random as random
import numpy as np
import csv
x_data = [ 338.,  333.,  328. , 207. , 226.  , 25. , 179. ,  60. , 208.,  606.]
y_data = [  640.  , 633. ,  619.  , 393.  , 428. ,   27.  , 193.  ,  66. ,  226. , 1591.]
x = np.arange(-200,-100,1) #bias
y = np.arange(-5,5,0.1) #weight
Z =  np.zeros((len(x), len(y)))
X, Y = np.meshgrid(x, y)
for i in range(len(x)):
    for j in range(len(y)):
        b = x[i]
        w = y[j]
        Z[j][i] = 0
        for n in range(len(x_data)):
            Z[j][i] = Z[j][i] +  (y_data[n] - b - w*x_data[n])**2
        Z[j][i] = Z[j][i]/len(x_data)
# ydata = b + w * xdata 
b = -120 # initial b
w = -4 # initial w
lr = 1 # learning rate
iteration = 100000

b_lr = 0.0
w_lr = 0.0

# Store initial values for plotting.
b_history = [b]
w_history = [w]

# Iterations
for i in range(iteration):

    b_grad = 0.0
    w_grad = 0.0
    for n in range(len(x_data)):        
        b_grad = b_grad  - 2.0*(y_data[n] - b - w*x_data[n])*1.0
        w_grad = w_grad  - 2.0*(y_data[n] - b - w*x_data[n])*x_data[n]

    b_lr = b_lr + b_grad**2
    w_lr = w_lr + w_grad**2

    # Update parameters.
    b = b - lr/np.sqrt(b_lr) * b_grad 
    w = w - lr/np.sqrt(w_lr) * w_grad

    # Store parameters for plotting
    b_history.append(b)
    w_history.append(w)

# plot the figure
plt.contourf(x,y,Z, 50, alpha=0.5, cmap=plt.get_cmap('jet'))
plt.plot([-188.4], [2.67], 'x', ms=12, markeredgewidth=3, color='orange')
plt.plot(b_history, w_history, 'o-', ms=3, lw=1.5, color='black')
plt.xlim(-200,-100)
plt.ylim(-5,5)
plt.xlabel(r'$b$', fontsize=16)
plt.ylabel(r'$w$', fontsize=16)
plt.show()

這裏寫圖片描述

參考文獻

  1. [機器學習] ML重要概念:梯度(Gradient)與梯度下降法(Gradient Descent)
  2. 方向導數與梯度
  3. 方向導數與梯度
  4. 一文看懂常用的梯度下降算法
  5. ML Lecture 1: Regression - Demo
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章