目標函式的經典優化演算法介紹

劉曉坤發表於2017-11-27
本文使用通俗的語言和形象的圖示,介紹了隨機梯度下降演算法和它的三種經典變體,並提供了完整的實現程式碼。

GitHub 連結:https://github.com/ManuelGonzalezRivero/3dbabove

代價函式的多種優化方法

目標函式是衡量預測值和實際值的相似程度的指標。通常,我們希望得到使代價儘可能小的引數集,而這意味著你的演算法效能不錯。函式的最小可能代價被稱為最小值。有時一個代價函式可以有多個區域性極小值。幸運的是,在引數空間的維數非常高的情況下,阻礙目標函式充分優化的區域性最小值並不經常出現,因為這意味著物件函式相對於每個引數在訓練過程的早期都是凹的。但這並非常態,通常我們得到的是許多鞍點,而不是真正的最小值。

目標函式的經典優化演算法介紹

找到生成最小值的一組引數的演算法被稱為優化演算法。我們發現隨著演算法複雜度的增加,則演算法傾向於更高效地逼近最小值。我們將在這篇文章中討論以下演算法:

  • 隨機梯度下降法
  • 動量演算法
  • RMSProp
  • Adam 演算法

隨機梯度下降法

我的「Logistic 迴歸深入淺出」的文章裡介紹了一個隨機梯度下降如何運作的例子。如果你查閱隨機梯度下降法的資料(SGD),通常會遇到如下的等式:

目標函式的經典優化演算法介紹

資料上會說,θ是你試圖找到最小化 J 的引數,這裡的 J 稱為目標函式。最後,我們將學習率記為α。通常要反覆應用上述等式,直到達到你所需的代價值。

這是什麼意思?想一想,假如你坐在一座山頂上的雪橇上,望著另一座山丘。如果你滑下山丘,你會自然地往下移動,直到你最終停在山腳。如果第一座小山足夠陡峭,你可能會開始滑上另一座山的一側。從這個比喻中你可以想到:

目標函式的經典優化演算法介紹

學習率越高意味著摩擦力越小,因此雪橇會像在冰上一樣沿著山坡下滑。低的學習率意味著摩擦力高,所以雪橇會像在地毯上一樣,難以滑下。我們如何用上面的方程來模擬這種效果?

隨機梯度下降法:

  1. 初始化引數(θ,學習率)
  2. 計算每個θ處的梯度
  3. 更新引數
  4. 重複步驟 2 和 3,直到代價值穩定

讓我們用一個簡單的例子來看看它是如何運作的!

在這裡我們看到一個目標函式和它的導數(梯度):

目標函式的經典優化演算法介紹

我們可以用下面的程式碼生成函式和梯度值/30 的圖:

  1. import numpy as np

  2. def minimaFunction(theta):

  3.    return np.cos(3*np.pi*theta)/theta

  4. def minimaFunctionDerivative(theta):

  5.    const1 = 3*np.pi

  6.    const2 = const1*theta

  7.    return -(const1*np.sin(const2)/theta)-np.cos(const2)/theta**2

  8. theta = np.arange(.1,2.1,.01)

  9. Jtheta = minimaFunction(theta)

  10. dJtheta = minimaFunctionDerivative(theta)

  11. plt.plot(theta,Jtheta,label = r'$J(\theta)$')

  12. plt.plot(theta,dJtheta/30,label = r'$dJ(\theta)/30$')

  13. plt.legend()

  14. axes = plt.gca()

  15. #axes.set_ylim([-10,10])

  16. plt.ylabel(r'$J(\theta),dJ(\theta)/30$')

  17. plt.xlabel(r'$\theta$')

  18. plt.title(r'$J(\theta),dJ(\theta)/30 $ vs $\theta$')

  19. plt.show()

目標函式的經典優化演算法介紹

上圖中有兩個細節值得注意。首先,注意這個代價函式有幾個極小值(大約在 0.25、1.0 和 1.7 附近取得)。其次,注意在最小值處的導數在零附近的曲線走向。這個點就是我們所需要的新參。

我們可以在下面的程式碼中看到上面四個步驟的實現。它還會生成一個視訊,顯示每個步驟的θ和梯度的值。

  1. import numpy as np

  2. import matplotlib.pyplot as plt

  3. import matplotlib.animation as animation

  4. def optimize(iterations, oF, dOF,params,learningRate):

  5.    """

  6.    computes the optimal value of params for a given objective function and its derivative

  7.    Arguments:

  8.        - iteratoins - the number of iterations required to optimize the objective function

  9.        - oF - the objective function

  10.        - dOF - the derivative function of the objective function

  11.        - params - the parameters of the function to optimize

  12.        - learningRate - the learning rate

  13.    Return:

  14.        - oParams - the list of optimized parameters at each step of iteration

  15.    """

  16.    oParams = [params]

  17.    #The iteration loop

  18.    for i in range(iterations):

  19.        # Compute the derivative of the parameters

  20.        dParams = dOF(params)

  21.        # Compute the update

  22.        params = params-learningRate*dParams

  23.        # app end the new parameters

  24.        oParams.append(params)    

  25.    return np.array(oParams)

  26. def minimaFunction(theta):

  27.    return np.cos(3*np.pi*theta)/theta

  28. def minimaFunctionDerivative(theta):

  29.    const1 = 3*np.pi

  30.    const2 = const1*theta

  31.    return -(const1*np.sin(const2)/theta)-np.cos(const2)/theta**2

  32. theta = .6

  33. iterations=45

  34. learningRate = .0007

  35. optimizedParameters = optimize(iterations,\

  36.                               minimaFunction,\

  37.                               minimaFunctionDerivative,\

  38.                               theta,\

  39.                               learningRate)

目標函式的經典優化演算法介紹

這似乎運作得很好!您應該注意到,如果θ的初始值較大,則優化演算法將在某一個區域性極小處結束。然而,如上所述,在極高維度空間中這種可能性並不大,因為它要求所有引數同時滿足凹函式。

你可能會想,「如果我們的學習率太大,會發生什麼?」。如果步長過大,則演算法可能永遠不會找到如下的動畫所示的最佳值。監控代價函式並確保它單調遞減,這一點很重要。如果沒有單調遞減,可能需要降低學習率。

目標函式的經典優化演算法介紹

SGD 也適用於多變數引數空間的情況。我們可以將二維函式繪製成等高線圖。在這裡你可以看到 SGD 對一個不對稱的碗形函式同樣有效。

  1. import numpy as np

  2. import matplotlib.mlab as mlab

  3. import matplotlib.pyplot as plt

  4. import scipy.stats

  5. import matplotlib.animation as animation

  6. def minimaFunction(params):

  7.    #Bivariate Normal function

  8.    X,Y = params

  9.    sigma11,sigma12,mu11,mu12 = (3.0,.5,0.0,0.0)

  10.    Z1 = mlab.bivariate_normal(X, Y, sigma11,sigma12,mu11,mu12)

  11.    Z = Z1

  12.    return -40*Z

  13. def minimaFunctionDerivative(params):

  14.    # Derivative of the bivariate normal function

  15.    X,Y = params

  16.    sigma11,sigma12,mu11,mu12 = (3.0,.5,0.0,0.0)

  17.    dZ1X = -scipy.stats.norm.pdf(X, mu11, sigma11)*(mu11 - X)/sigma11**2

  18.    dZ1Y = -scipy.stats.norm.pdf(Y, mu12, sigma12)*(mu12 - Y)/sigma12**2

  19.    return (dZ1X,dZ1Y)

  20. def optimize(iterations, oF, dOF,params,learningRate,beta):

  21.    """

  22.    computes the optimal value of params for a given objective function and its derivative

  23.    Arguments:

  24.        - iteratoins - the number of iterations required to optimize the objective function

  25.        - oF - the objective function

  26.        - dOF - the derivative function of the objective function

  27.        - params - the parameters of the function to optimize

  28.        - learningRate - the learning rate

  29. - beta - The weighted moving average parameter

  30.    Return:

  31.        - oParams - the list of optimized parameters at each step of iteration

  32.    """

  33.    oParams = [params]

  34.    vdw     = (0.0,0.0)

  35.    #The iteration loop

  36.    for i in range(iterations):

  37.        # Compute the derivative of the parameters

  38.        dParams = dOF(params)

  39.        #SGD in this line Goes through each parameter and applies parameter = parameter -learningrate*dParameter

  40.        params = tuple([par-learningRate*dPar for dPar,par in zip(dParams,params)])

  41.        # append the new parameters

  42.        oParams.append(params)    

  43.    return oParams

  44. iterations=100

  45. learningRate = 1

  46. beta = .9

  47. x,y = 4.0,1.0

  48. params = (x,y)

  49. optimizedParameters = optimize(iterations,\

  50.                               minimaFunction,\

  51.                               minimaFunctionDerivative,\

  52.                               params,\

  53.                               learningRate,\

  54.                               beta)


目標函式的經典優化演算法介紹


動量 SGD


注意,傳統 SGD 沒有解決所有問題!通常,使用者想要使用非常大的學習速率來快速學習感興趣的引數。不幸的是,當代價函式波動較大時,這可能導致不穩定。你可以看到,在前面的視訊中,由於缺乏水平方向上的最小值,y 引數方向的抖動形式。動量演算法試圖使用過去的梯度預測學習率來解決這個問題。通常,使用動量的 SGD 通過以下公式更新引數:

目標函式的經典優化演算法介紹

γ 和 ν 值允許使用者對 dJ(θ) 的前一個值和當前值進行加權來確定新的θ值。人們通常選擇γ和ν的值來建立指數加權移動平均值,如下所示:

目標函式的經典優化演算法介紹


β引數的最佳選擇是 0.9。選擇一個等於 1-1/t 的β值可以讓使用者更願意考慮νdw 的最新 t 值。這種簡單的改變可以使優化過程產生顯著的結果!我們現在可以使用更大的學習率,並在儘可能短的時間內收斂!

  1. import numpy as np

  2. import matplotlib.mlab as mlab

  3. import matplotlib.pyplot as plt

  4. import scipy.stats

  5. import matplotlib.animation as animation

  6. def minimaFunction(params):

  7.    #Bivariate Normal function

  8.    X,Y = params

  9.    sigma11,sigma12,mu11,mu12 = (3.0,.5,0.0,0.0)

  10.    Z1 = mlab.bivariate_normal(X, Y, sigma11,sigma12,mu11,mu12)

  11.    Z = Z1

  12.    return -40*Z

  13. def minimaFunctionDerivative(params):

  14.    # Derivative of the bivariate normal function

  15.    X,Y = params

  16.    sigma11,sigma12,mu11,mu12 = (3.0,.5,0.0,0.0)

  17.    dZ1X = -scipy.stats.norm.pdf(X, mu11, sigma11)*(mu11 - X)/sigma11**2

  18.    dZ1Y = -scipy.stats.norm.pdf(Y, mu12, sigma12)*(mu12 - Y)/sigma12**2

  19.    return (dZ1X,dZ1Y)

  20. def optimize(iterations, oF, dOF,params,learningRate,beta):

  21.    """

  22.    computes the optimal value of params for a given objective function and its derivative

  23.    Arguments:

  24.        - iteratoins - the number of iterations required to optimize the objective function

  25.        - oF - the objective function

  26.        - dOF - the derivative function of the objective function

  27.        - params - the parameters of the function to optimize

  28.        - learningRate - the learning rate

  29. - beta - The weighted moving average parameter for momentum

  30.    Return:

  31.        - oParams - the list of optimized parameters at each step of iteration

  32.    """

  33.    oParams = [params]

  34.    vdw     = (0.0,0.0)

  35.    #The iteration loop

  36.    for i in range(iterations):

  37.        # Compute the derivative of the parameters

  38.        dParams = dOF(params)

  39.        # Compute the momentum of each gradient vdw = vdw*beta+(1.0+beta)*dPar

  40.        vdw = tuple([vDW*beta+(1.0-beta)*dPar for dPar,vDW in zip(dParams,vdw)])

  41.        #SGD in this line Goes through each parameter and applies parameter = parameter -learningrate*dParameter

  42.        params = tuple([par-learningRate*dPar for dPar,par in zip(vdw,params)])

  43.        # append the new parameters

  44.        oParams.append(params)    

  45.    return oParams

  46. iterations=100

  47. learningRate = 5.3

  48. beta = .9

  49. x,y = 4.0,1.0

  50. params = (x,y)

  51. optimizedParameters = optimize(iterations,\

  52.                               minimaFunction,\

  53.                               minimaFunctionDerivative,\

  54.                               params,\

  55.                               learningRate,\

  56.                               beta)


目標函式的經典優化演算法介紹


RMSProp


像工程中的其它事物一樣,我們一直在努力做得更好。RMS prop 試圖通過觀察關於每個引數的函式梯度的相對大小,來改善動量函式。因此,我們可以取每個梯度平方的加權指數移動平均值,並按比例歸一化梯度下降函式。具有較大梯度的引數的 sdw 值將變得比具有較小梯度的引數大得多,從而使代價函式平滑下降到最小值。可以在下面的等式中看到:

目標函式的經典優化演算法介紹


請注意,這裡的 epsilon 是為數值穩定性而新增的,可以取 10e-7。這是為什麼暱?

  1. import numpy as np

  2. import matplotlib.mlab as mlab

  3. import matplotlib.pyplot as plt

  4. import scipy.stats

  5. import matplotlib.animation as animation

  6. def minimaFunction(params):

  7.    #Bivariate Normal function

  8.    X,Y = params

  9.    sigma11,sigma12,mu11,mu12 = (3.0,.5,0.0,0.0)

  10.    Z1 = mlab.bivariate_normal(X, Y, sigma11,sigma12,mu11,mu12)

  11.    Z = Z1

  12.    return -40*Z

  13. def minimaFunctionDerivative(params):

  14.    # Derivative of the bivariate normal function

  15.    X,Y = params

  16.    sigma11,sigma12,mu11,mu12 = (3.0,.5,0.0,0.0)

  17.    dZ1X = -scipy.stats.norm.pdf(X, mu11, sigma11)*(mu11 - X)/sigma11**2

  18.    dZ1Y = -scipy.stats.norm.pdf(Y, mu12, sigma12)*(mu12 - Y)/sigma12**2

  19.    return (dZ1X,dZ1Y)

  20. def optimize(iterations, oF, dOF,params,learningRate,beta):

  21.    """

  22.    computes the optimal value of params for a given objective function and its derivative

  23.    Arguments:

  24.        - iteratoins - the number of iterations required to optimize the objective function

  25.        - oF - the objective function

  26.        - dOF - the derivative function of the objective function

  27.        - params - the parameters of the function to optimize

  28.        - learningRate - the learning rate

  29. - beta - The weighted moving average parameter for RMSProp

  30.    Return:

  31.        - oParams - the list of optimized parameters at each step of iteration

  32.    """

  33.    oParams = [params]

  34.    sdw     = (0.0,0.0)

  35.    eps = 10**(-7)

  36.    #The iteration loop

  37.    for i in range(iterations):

  38.        # Compute the derivative of the parameters

  39.        dParams = dOF(params)

  40.        # Compute the momentum of each gradient sdw = sdw*beta+(1.0+beta)*dPar^2

  41.        sdw = tuple([sDW*beta+(1.0-beta)*dPar**2 for dPar,sDW in zip(dParams,sdw)])

  42.        #SGD in this line Goes through each parameter and applies parameter = parameter -learningrate*dParameter

  43.        params = tuple([par-learningRate*dPar/((sDW**.5)+eps) for sDW,par,dPar in zip(sdw,params,dParams)])

  44.        # append the new parameters

  45.        oParams.append(params)    

  46.    return oParams

  47. iterations=10

  48. learningRate = .3

  49. beta = .9

  50. x,y = 5.0,1.0

  51. params = (x,y)

  52. optimizedParameters = optimize(iterations,\

  53.                               minimaFunction,\

  54.                               minimaFunctionDerivative,\

  55.                               params,\

  56.                               learningRate,\

  57.                               beta)

目標函式的經典優化演算法介紹

Adam 演算法

Adam 演算法將動量和 RMSProp 的概念結合成一種演算法,以獲得兩全其美的效果。公式如下:

目標函式的經典優化演算法介紹


  1. import numpy as np

  2. import matplotlib.mlab as mlab

  3. import matplotlib.pyplot as plt

  4. import scipy.stats

  5. import matplotlib.animation as animation

  6. def minimaFunction(params):

  7.    #Bivariate Normal function

  8.    X,Y = params

  9.    sigma11,sigma12,mu11,mu12 = (3.0,.5,0.0,0.0)

  10.    Z1 = mlab.bivariate_normal(X, Y, sigma11,sigma12,mu11,mu12)

  11.    Z = Z1

  12.    return -40*Z

  13. def minimaFunctionDerivative(params):

  14.    # Derivative of the bivariate normal function

  15.    X,Y = params

  16.    sigma11,sigma12,mu11,mu12 = (3.0,.5,0.0,0.0)

  17.    dZ1X = -scipy.stats.norm.pdf(X, mu11, sigma11)*(mu11 - X)/sigma11**2

  18.    dZ1Y = -scipy.stats.norm.pdf(Y, mu12, sigma12)*(mu12 - Y)/sigma12**2

  19.    return (dZ1X,dZ1Y)

  20. def optimize(iterations, oF, dOF,params,learningRate,beta1,beta2):

  21.    """

  22.    computes the optimal value of params for a given objective function and its derivative

  23.    Arguments:

  24.        - iteratoins - the number of iterations required to optimize the objective function

  25.        - oF - the objective function

  26.        - dOF - the derivative function of the objective function

  27.        - params - the parameters of the function to optimize

  28.        - learningRate - the learning rate

  29. - beta1 - The weighted moving average parameter for momentum component of ADAM

  30. - beta2 - The weighted moving average parameter for RMSProp component of ADAM

  31.    Return:

  32.        - oParams - the list of optimized parameters at each step of iteration

  33.    """

  34.    oParams = [params]

  35.    vdw     = (0.0,0.0)

  36.    sdw     = (0.0,0.0)

  37.    vdwCorr = (0.0,0.0)

  38.    sdwCorr = (0.0,0.0)

  39.    eps = 10**(-7)

  40.    #The iteration loop

  41.    for i in range(iterations):

  42.        # Compute the derivative of the parameters

  43.        dParams = dOF(params)

  44.        # Compute the momentum of each gradient vdw = vdw*beta+(1.0+beta)*dPar

  45.        vdw     = tuple([vDW*beta1+(1.0-beta1)*dPar for dPar,vDW in zip(dParams,vdw)])

  46.        # Compute the rms of each gradient sdw = sdw*beta+(1.0+beta)*dPar^2

  47.        sdw     = tuple([sDW*beta2+(1.0-beta2)*dPar**2.0 for dPar,sDW in zip(dParams,sdw)])

  48.        # Compute the weight boosting for sdw and vdw

  49.        vdwCorr = tuple([vDW/(1.0-beta1**(i+1.0)) for vDW in vdw])

  50.        sdwCorr = tuple([sDW/(1.0-beta2**(i+1.0)) for sDW in sdw])

  51.        #SGD in this line Goes through each parameter and applies parameter = parameter -learningrate*dParameter

  52.        params = tuple([par-learningRate*vdwCORR/((sdwCORR**.5)+eps) for sdwCORR,vdwCORR,par in zip(vdwCorr,sdwCorr,params)])

  53.        # append the new parameters

  54.        oParams.append(params)    

  55.    return oParams

  56. iterations=100

  57. learningRate = .1

  58. beta1 = .9

  59. beta2 = .999

  60. x,y = 5.0,1.0

  61. params = (x,y)

  62. optimizedParameters = optimize(iterations,\

  63.                               minimaFunction,\

  64.                               minimaFunctionDerivative,\

  65.                               params,\

  66.                               learningRate,\

  67.                               beta1,\

  68.                               beta2)</div>

Adam 演算法可能是目前深度學習中使用最廣泛的優化演算法,適用於多種應用。Adam 計算了一個 νdw^corr 的值,用於加快指數加權移動平均值的變化。它將通過增加它們的值來對它們進行標準化,與當前的迭代次數成反比。使用 Adam 時有一些很好的初始值可供嘗試。它最好以 0.9 的 β_1 和 0.999 的 β_2 開頭。

總結

優化目標函式的演算法有相當多的選擇。在上述示例中,我們發現各種方法的收斂速度越來越快:

– SGD: 100 次迭代

– SGD+Momentum: 50 次迭代

– RMSProp: 10 次迭代

– ADAM: 5 次迭代目標函式的經典優化演算法介紹

原文連結:https://3dbabove.com/2017/11/14/optimizationalgorithms/

相關文章