深度分析-梯度下降


梯度下降

由於梯度下降法中負梯度方向作為變量的變化方向,所以有可能導 致最終求解的值是局部最優解,所以在使用梯度下降的時候,一般需 要進行一些調優策略: 學習率的選擇:

  • 學習率過大,表示每次迭代更新的時候變化比較大,有可能 會跳過最優解;
  • 學習率過小,表示每次迭代更新的時候變化比較小,就會導 致迭代速度過慢,很長時間都不能結束;

算法初始參數值的選擇:

  • 初始值不同,最終獲得的最小值也有可能不同,因為梯度下降法求解的是局部最優解,所以一般情況下,選擇多次不同初始值 運行算法,並最終返回損失函數最小情況下的結果值;

標准化:由於樣本不同特征的取值范圍不同,可能會導致在各個不同參數上 迭代速度不同,為了減少特征取值的影響,可以將特征進行標准化操作。

 梯度下降二維案例

import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
mpl.rcParams['font.sans-serif'] = [u'simHei']
mpl.rcParams['axes.unicode_minus'] = False

def f(x):
    return 0.5*(x-0.25)**2
def h(x):
    return x-1/4

X = []
Y = []
x =2
# 此時的學習率,是由一定區間的,過小會導致性能變慢,過大可能發散
step = 0.8
f_change = f(x)
f_current = f(x)
X.append(x)
Y.append(f_current)
# 當變化值小於1e-10的時候停止,也就是梯度
while f_change > 1e-10 and len(X)<100:
    # 這里是梯度下降的變化程度x = x - k*f(x)'
    x = x - step * h(x)
    tmp = f(x)
    f_change = np.abs(f_current - tmp)
    f_current = tmp
    X.append(x)
    Y.append(f_current)
fig = plt.figure()
X2 = np.arange(-2.1,2.65,0.05)
Y2 = 0.5*(X2-0.25)**2
plt.plot(X2,Y2,'-',color="#666666",linewidth = 2)
plt.plot(X,Y,'bo--')
plt.title("$y= 0.5*(x-0.25)^2$通過梯度下降法得到目標值x=%.2f,y=%.2f迭代次數%d"%(x,f_current,len(X)))
plt.show()

梯度下降三維案例

 

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
def f(x, y):
    return x ** 2 + y ** 2


def h(t):
    return 2 * t


X = []
Y = []
Z = []

x = 2
y = 2
f_change = x ** 2 + y ** 2
f_current = f(x, y)
step = 0.1
X.append(x)
Y.append(y)
Z.append(f_current)
while f_change > 1e-10:
    # 對於各自未知數求偏導
    x = x - step * h(x)
    y = y - step * h(y)
    f_change = np.abs(f_current - f(x,y))
    f_current = f(x,y)
    X.append(x)
    Y.append(y)
    Z.append(f_current)
fig = plt.figure()
ax = Axes3D(fig)
X2 = np.arange(-2,2,0.2)
Y2 = np.arange(-2,2,0.2)
X2,Y2 = np.meshgrid(X2,Y2)
Z2 = X2**2 + Y2**2
ax.plot_surface(X2,Y2,Z2,rstride=1,cstride=1,cmap='rainbow')
ax.plot(X,Y,Z,'ro--')

ax.set_title("")
plt.show()

再機器學習中梯度算法簡直是一個利刃,能夠幫助我們求解很多參數

分析解題步驟

目標函數θ求解 初始化θ(隨機初始化,可以初始為0) 沿着負梯度方向迭代,更新后的θ使J(θ)更小

 

梯度下降研究每次迭代N個數

BGD

from matplotlib import pyplot as plt
import numpy as np
from mpl_toolkits.mplot3d import Axes3D

fig = plt.figure()
w = np.arange(-5, 8, .25)
b = np.arange(-15, 15, .25)
x = np.array([1,2,3,4])
y = np.array([3.2,4.7,7.3,8.5])
w, b = np.meshgrid(w, b)
R = 0
for i in range(len(x)):
    R += (w*x[i]+b-y[i])**2
R /= len(x)
a = R<50
R = ~a*50+R*a

ax = plt.subplot()
plt.contourf(w, b, R,10,alpha=0.5)
plt.title("cost(w,b) = 1/N * Σ(w*x_i+b-y_i)^2")
w = 3.5
b = 3.5
W = []
B = []
for i in range(2000):
    W.append(w)
    B.append(b)
    w -= 0.02*1/len(x)*sum((w*x+b-y)*x)
    b -= 0.02*1/len(x)*sum((w*x+b-y))
    print(w,b)
plt.plot(W,B,"r*")
plt.xlabel("w")
plt.ylabel("b")
plt.show()

SGD

from matplotlib import pyplot as plt
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
import random
fig = plt.figure()
w = np.arange(-2, 5, .25)
b = np.arange(-15, 15, .25)
x = np.array([1,2,3,4])
y = np.array([3.2,4.7,7.3,8.5])
w, b = np.meshgrid(w, b)
R = 0
for i in range(len(x)):
    R += (w*x[i]+b-y[i])**2
R /= len(x)
a = R<50
R = ~a*50+R*a

ax = plt.subplot()
plt.contourf(w, b, R,10,alpha=0.5)
plt.title("cost(w,b) = 1/N * Σ(w*x_i+b-y_i)^2")
w = 3.5
b = 3.5
W = []
B = []
for i in range(2000):
    W.append(w)
    B.append(b)
    p = random.randint(0, len(x)-1)
    w -= 0.02*(w*x[p]+b-y[p])*x[p]
    b -= 0.02*(w*x[p]+b-y[p])
    print(w,b)
plt.plot(W,B,"r*")
plt.xlabel("w")
plt.ylabel("b")
plt.show()
  • SGD速度比BGD快(迭代次數少)
  • SGD在某些情況下(全局存在多個相對最優解/J(θ)不是一個二次),SGD有可能跳 出某些小的局部最優解,所以不會比BGD壞
  • BGD一定能夠得到一個局部最優解(在線性回歸模型中一定是得到一個全局最優 解),SGD由於隨機性的存在可能導致最終結果比BGD的差
  • 注意:優先選擇SGD

MBGD 

如果即需要保證算法的訓練過程比較快,又需要保證最終參數訓練的准確率,而 這正是小批量梯度下降法(Mini-batch Gradient Descent,簡稱MBGD)的初 衷。MBGD中不是每拿一個樣本就更新一次梯度,而且拿b個樣本(b一般為10)的 平均梯度作為更新方向。

BGD、SGD、MBGD的區別:

  • 當樣本量為m的時候,每次迭代BGD算法中對於參數值更新一次,SGD算法 中對於參數值更新m次,MBGD算法中對於參數值更新m/n次,相對來講 SGD算法的更新速度最快;
  • SGD算法中對於每個樣本都需要更新參數值,當樣本值不太正常的時候,就 有可能會導致本次的參數更新會產生相反的影響,也就是說SGD算法的結果 並不是完全收斂的,而是在收斂結果處波動的;
  • SGD算法是每個樣本都更新一次參數值,所以SGD算法特別適合樣本數據量 大的情況以及在線機器學習(Online ML)。

三個概念

導數

導數反映的是函數y=f(x)在某一點處沿x軸正方向的變化率。

比如y=x2,在x=1處的導數=2。

 

導數是通過極限來定義的,某一點的導數=tanψ,但是前提是△x趨近於0,此時tanψ=tanα=該點導數,公式如下:

偏導 

在多元函數中,偏導數指的是函數y(x1,x2,…,xn)沿某一坐標軸(x1,x2,…,xn)正方向的變化率。

比如,在(1,2)處的在x方向上的偏導數:

截取y=2的曲線,可以發現在x方向的導數=2

導數和偏導數都是沿坐標軸正方向的變化率。那么當我們討論函數沿任意方向的變化率時,也就引出了方向導數的定義,即:某一點在某一趨近方向上的導數值。

 比如,可以計算函數在點A(2,2,8)的導數。

 

梯度

梯度是一個向量,表示某一函數在該點處的方向導數沿着該方向取得最大值,即函數在該點處沿着該方向(此梯度的方向)變化最快,變化率最大(為該梯度的模)。

這里注意三點:

1)梯度是一個向量,即有方向有大小;

2)梯度的方向是最大方向導數的方向;

3)梯度的值是最大方向導數的值。

比如z=x2+y2+xy在點A(2,2,12)處的梯度為

梯度下降的缺點

1,選擇合適的學習率比較困難

2,對所有的參數更新使用同樣的學習率。對於稀疏數據或者特征,有時我們可能想更新快一些對於不經常出現的特征,對於常出現的特征更新慢一些,這時候SGD就不太能滿足要求了

3.SGD容易收斂到局部最優

梯度下降從偏導數上改進版

Momentum法

momentum是模擬物理里動量的概念,積累之前的動量來替代真正的梯度。

如果使用的是沒有動量的梯度下降法,則可能會停到第一個次優解

最直觀的理解就是,若當前的梯度方向與累積的歷史梯度方向一致,則當前的梯度會被加強,從而這一步下降的幅度更大。若當前的梯度方向與累積的梯度方向不一致,則會減弱當前下降的梯度幅度。

公式如下:

 其中,μ是動量因子

  • 下降初期,使用上一次參數更新,下降方向一致則乘上較大的μ能夠進行很好的加速
  • 下降中后期時,在局部最小值來回震盪的時候,gradient→0,μ使得更新幅度增大,跳出局部最優解
  • 在梯度改變方向的時候,μ能夠減少更新

總而言之,momentum項能夠在相關方向加速SGD,抑制振盪,從而加快收斂。

代碼:

from matplotlib import pyplot as plt
import numpy as np

fig = plt.figure()
x = np.arange(-0.8, 1.2, 0.025)
plt.plot(x,2*x**4-x**3-x**2)
plt.title("y = 2*x^4-x^3-x^2")
def f(x):
    return 2*x**4-x**3-x**2
def h(x):
    return 8*x**3 - 3*x**2 - 2*x
η = 0.05
α = 0.9
v = 0
x = -0.8
iters = 0
X = []
Y = []
while iters<12:
    iters+=1
    X.append(x)
    Y.append(f(x))
    v = α*v - η*h(x)
    x = x + v
    print(iters,x)
plt.plot(X,Y)
plt.scatter(X[-1],Y[-1])
plt.show()

Adagrad法

Adagrad其實是對學習率進行了一個約束。即:

可以看出,Adagrad算法中有自適應調整梯度的意味(adaptive gradient),學習率需要除以一個東西,這個東西就是前n次迭代過程中偏導數的平方和再加一個常量最后開根號
特點:

  • 前期根號下的值較小的時候, 學習率較大,能夠放大梯度
  • 后期根號下的值較大的時候,學習率較小,能夠約束梯度
  • 適合處理稀疏梯度

缺點:

  • 公式上可以看出,仍依賴於人工設置一個全局學習率η設置過大整個式子過於敏感,對梯度的調節太大
  • 中后期,分母上梯度平方的累加將會越來越大,使gradient→0,訓練過慢。

 

from matplotlib import pyplot as plt
import numpy as np
from mpl_toolkits.mplot3d import Axes3D

fig = plt.figure()
x = np.arange(-4, 4, 0.025)
plt.plot(x,x**2)
plt.title("y = x^2")
def f(x):
    return x**2
def h(x):
    return 2*x
η = 0.5
ε = 0.1
x = 4
iters = 0
sum_square_grad = 0
X = []
Y = []
while iters<40:
    iters+=1
    X.append(x)
    Y.append(f(x))
    sum_square_grad += h(x)**2
    x = x - η/np.sqrt(sum_square_grad+ε)*h(x)
    print(iters,x)
plt.plot(X,Y,"ro")
ax = plt.subplot()
for i in range(len(X)):
    if i%8==0:
        ax.text(X[i], (X[i])**2, "({:.3f},{:.3f})".format(X[i], (X[i])**2), color='red')
plt.show()

RMSprop

AdaGrad算法在迭代后期由於學習率過小,可能較難找到一個有用的解。為了解決這一問題,RMSprop算法對Adagrad算法做了一點小小的修改,RMSprop使用指數衰減只保留過去給定窗口大小的梯度,使其能夠在找到凸碗狀結構后快速收斂。

當β等於0.5的時候,g再求根的話就變成了求RMSE(均方根)

 

from matplotlib import pyplot as plt
import numpy as np
from mpl_toolkits.mplot3d import Axes3D

fig = plt.figure()
x = np.arange(-4, 4, 0.025)
plt.plot(x,x**2)
plt.title("y = x^2")
def f(x):
    return x**2
def h(x):
    return 2*x
g = 1
x = 4
ρ = 0.9
η = 0.01
ε = 10e-10
iters = 0
X = []
Y = []
while iters<420:
    iters+=1
    X.append(x)
    Y.append(f(x))
    g = ρ*g+(1-ρ)*h(x)**2
    x = x - η/np.sqrt(g+ε)*h(x)
    print(iters,x)

ax = plt.subplot()
for i in range(len(X)):
    if i % 40==0:
        plt.scatter(X[i], Y[i])
        ax.text(X[i], (X[i])**2, "({:.3f},{:.3f})".format(X[i], (X[i])**2), color='red')
plt.show()

優點:

  • RMSprop依然依賴於全局學習率
  • RMSprop算是Adagrad的變體,效果趨於二者之間
  • 適合處理非平穩目標
  • 對於RNN效果很好

Adam法

Adam實際上是把momentum和RMSprop結合起來的一種算法也就是帶有動量項的RMSprop

假設N元函數f(x),針對一個自變量研究Adam梯度下降的迭代過程,

優點:

  • 結合了Adagrad善於處理稀疏梯度和RMSprop善於處理非平穩目標的優點
  • 對內存需求較小
  • 為不同的參數計算不同的自適應學習率
  • 也適用於大多非凸優化
  • 適用於大數據集和高維空間

from matplotlib import pyplot as plt
import numpy as np
from mpl_toolkits.mplot3d import Axes3D

fig = plt.figure()
x = np.arange(-4, 4, 0.025)
plt.plot(x,x**2)
plt.title("y = x^2")
def f(x):
    return x**2
def h(x):
    return 2*x
x = 4
m = 0
v = 0
β1 = 0.9
β2 = 0.999
η = 0.061
ε = 10e-8
iters = 0
X = []
Y = []
while iters<120:
    iters+=1
    X.append(x)
    Y.append(f(x))
    m = β1*m + (1-β1)*h(x)
    v = β2*v + (1-β2)*h(x)**2
    m_het = m/(1-β1**iters)
    v_het = v/(1-β2**iters)
    x = x - η/np.sqrt(v_het+ε)*m_het
    print(iters,x)
ax = plt.subplot()
for i in range(len(X)):
    if i %20==0:
        plt.scatter(X[i],Y[i],)
        ax.text(X[i], (X[i])**2, "({:.3f},{:.3f})".format(X[i], (X[i])**2), color='red')
plt.show()

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM