目标函数的经典优化算法介绍

本文使用通俗的语言和形象的图示,介绍了随机梯度下降算法和它的三种经典变体,并提供了完整的实现代码。


GitHub 链接:https://github.com/ManuelGonzalezRivero/3dbabove


代价函数的多种优化方法


目标函数是衡量预测值和实际值的相似程度的指标。通常,我们希望得到使代价尽可能小的参数集,而这意味着你的算法性能不错。函数的最小可能代价被称为最小值。有时一个代价函数可以有多个局部极小值。幸运的是,在参数空间的维数非常高的情况下,阻碍目标函数充分优化的局部最小值并不经常出现,因为这意味着对象函数相对于每个参数在训练过程的早期都是凹的。但这并非常态,通常我们得到的是许多鞍点,而不是真正的最小值。


目标函数的经典优化算法介绍_第1张图片


找到生成最小值的一组参数的算法被称为优化算法。我们发现随着算法复杂度的增加,则算法倾向于更高效地逼近最小值。我们将在这篇文章中讨论以下算法:


  • 随机梯度下降法
  • 动量算法
  • RMSProp
  • Adam 算法


随机梯度下降法


我的「Logistic 回归深入浅出」的文章里介绍了一个随机梯度下降如何运作的例子。如果你查阅随机梯度下降法的资料(SGD),通常会遇到如下的等式:


69576%E5%BE%AE%E4%BF%A1%E5%9B%BE%E7%89%87_20171129135615.jpg


资料上会说,θ是你试图找到最小化 J 的参数,这里的 J 称为目标函数。最后,我们将学习率记为α。通常要反复应用上述等式,直到达到你所需的代价值。


这是什么意思?想一想,假如你坐在一座山顶上的雪橇上,望着另一座山丘。如果你滑下山丘,你会自然地往下移动,直到你最终停在山脚。如果第一座小山足够陡峭,你可能会开始滑上另一座山的一侧。从这个比喻中你可以想到:


目标函数的经典优化算法介绍_第2张图片


学习率越高意味着摩擦力越小,因此雪橇会像在冰上一样沿着山坡下滑。低的学习率意味着摩擦力高,所以雪橇会像在地毯上一样,难以滑下。我们如何用上面的方程来模拟这种效果?


随机梯度下降法:


  1. 初始化参数(θ,学习率)
  2. 计算每个θ处的梯度
  3. 更新参数
  4. 重复步骤 2 和 3,直到代价值稳定


让我们用一个简单的例子来看看它是如何运作的!


在这里我们看到一个目标函数和它的导数(梯度):


目标函数的经典优化算法介绍_第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()



目标函数的经典优化算法介绍_第4张图片


上图中有两个细节值得注意。首先,注意这个代价函数有几个极小值(大约在 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)



目标函数的经典优化算法介绍_第5张图片


这似乎运作得很好!您应该注意到,如果θ的初始值较大,则优化算法将在某一个局部极小处结束。然而,如上所述,在极高维度空间中这种可能性并不大,因为它要求所有参数同时满足凹函数。


你可能会想,「如果我们的学习率太大,会发生什么?」。如果步长过大,则算法可能永远不会找到如下的动画所示的最佳值。监控代价函数并确保它单调递减,这一点很重要。如果没有单调递减,可能需要降低学习率。


目标函数的经典优化算法介绍_第6张图片


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)



目标函数的经典优化算法介绍_第7张图片


动量 SGD


注意,传统 SGD 没有解决所有问题!通常,用户想要使用非常大的学习速率来快速学习感兴趣的参数。不幸的是,当代价函数波动较大时,这可能导致不稳定。你可以看到,在前面的视频中,由于缺乏水平方向上的最小值,y 参数方向的抖动形式。动量算法试图使用过去的梯度预测学习率来解决这个问题。通常,使用动量的 SGD 通过以下公式更新参数:


35955%E5%BE%AE%E4%BF%A1%E5%9B%BE%E7%89%87_20171129135645.jpg


γ 和 ν 值允许用户对 dJ(θ) 的前一个值和当前值进行加权来确定新的θ值。人们通常选择γ和ν的值来创建指数加权移动平均值,如下所示:


43595%E5%BE%AE%E4%BF%A1%E5%9B%BE%E7%89%87_20171129135650.jpg


β参数的最佳选择是 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)



目标函数的经典优化算法介绍_第8张图片


RMSProp


像工程中的其它事物一样,我们一直在努力做得更好。RMS prop 试图通过观察关于每个参数的函数梯度的相对大小,来改善动量函数。因此,我们可以取每个梯度平方的加权指数移动平均值,并按比例归一化梯度下降函数。具有较大梯度的参数的 sdw 值将变得比具有较小梯度的参数大得多,从而使代价函数平滑下降到最小值。可以在下面的等式中看到:


目标函数的经典优化算法介绍_第9张图片


请注意,这里的 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)



目标函数的经典优化算法介绍_第10张图片


Adam 算法


Adam 算法将动量和 RMSProp 的概念结合成一种算法,以获得两全其美的效果。公式如下:


目标函数的经典优化算法介绍_第11张图片


        
  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)



  • Adam 算法可能是目前深度学习中使用最广泛的优化算法,适用于多种应用。Adam 计算了一个 νdw^corr 的值,用于加快指数加权移动平均值的变化。它将通过增加它们的值来对它们进行标准化,与当前的迭代次数成反比。使用 Adam 时有一些很好的初始值可供尝试。它最好以 0.9 的 β_1 和 0.999 的 β_2 开头。


    总结


    优化目标函数的算法有相当多的选择。在上述示例中,我们发现各种方法的收敛速度越来越快:


    – SGD: 100 次迭代

    – SGD+Momentum: 50 次迭代

    – RMSProp: 10 次迭代

    – ADAM: 5 次迭代21897%E5%BE%AE%E4%BF%A1%E5%9B%BE%E7%89%87_20170924135925.png


    原文链接:https://3dbabove.com/2017/11/14/optimizationalgorithms/


    你可能感兴趣的:(目标函数的经典优化算法介绍)