深度分析-梯度下降

梯度下降

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

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

算法初始参数值的选择:

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

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

 梯度下降二维案例

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()

 

posted @ 2019-07-24 17:05  Timcode  阅读(1289)  评论(0编辑  收藏  举报