zoukankan      html  css  js  c++  java
  • 深度分析-梯度下降

    梯度下降

    由于梯度下降法中负梯度方向作为变量的变化方向,所以有可能导 致最终求解的值是局部最优解,所以在使用梯度下降的时候,一般需 要进行一些调优策略: 学习率的选择:

    • 学习率过大,表示每次迭代更新的时候变化比较大,有可能 会跳过最优解;
    • 学习率过小,表示每次迭代更新的时候变化比较小,就会导 致迭代速度过慢,很长时间都不能结束;

    算法初始参数值的选择:

    • 初始值不同,最终获得的最小值也有可能不同,因为梯度下降法求解的是局部最优解,所以一般情况下,选择多次不同初始值 运行算法,并最终返回损失函数最小情况下的结果值;

    标准化:由于样本不同特征的取值范围不同,可能会导致在各个不同参数上 迭代速度不同,为了减少特征取值的影响,可以将特征进行标准化操作。

     梯度下降二维案例

    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()
  • 相关阅读:
    Django之数据库--ORM
    Vue 父子组件
    axios封装
    DRF常用功能
    DRF框架之Serializer序列化器的反序列化操作
    Django、DRF有什么不同
    RESTFUL风格
    判断ViewPager滑动方向
    Incompatible integer to pointer conversion sending 'NSInteger' (aka 'int') to parameter of type 'id'
    AchartEngine使用
  • 原文地址:https://www.cnblogs.com/TimVerion/p/11239307.html
Copyright © 2011-2022 走看看