梯度下降+动量冲出鞍点(代码可视化)

时间:2020-9-3 作者:admin


写在最前面的话:

我个人觉得可视化在理解一些概念的时候会有很大的帮助,所以我尝试了很久才构造出这个符合我想法的函数,这个函数的可视化效果也很直观,应该能对大家理解 梯度下降+动量 有些帮助

1 效果展示

1.1 纯梯度下降3D效果图(epoch=1000)

梯度下降+动量冲出鞍点(代码可视化)

1.2 梯度下降+动量3D效果图(epoch=122)

梯度下降+动量冲出鞍点(代码可视化)

1.3 函数的等高线效果图

梯度下降+动量冲出鞍点(代码可视化)

2 代码讲解

2.1 三个函数(表达式都一样,功能不一样)

求偏导,进行梯度下降: zz=xx**2+yy**2+yy**3+xx*yy 
计算梯度下降的点的坐标: zp=xp**2+yp**2+yp**3+xp*yp 
画3D函数图: z=x**2+y**2+y**3+x*y 

2.2 简单梯度下降+动量算法(只要注释两行就是纯梯度下降)

lr=0.01 #学习率
xx,yy = sympy.symbols('x y')
zz=xx**2+yy**2+yy**3+xx*yy #设置函数
dzdx=sympy.diff(zz, xx) #函数求偏导
dzdy=sympy.diff(zz, yy)
x_val=4 #设置初始点
y_val=5
last_gx=0 #保存上一次的梯度
last_gy=0
result=np.array([[4,5]]) #记录梯度下降的点
for epoch in range(122): #迭代次数
    grad_x=dzdx.subs({xx: x_val, yy: y_val}) #给偏导函数赋值计算x方向上的梯度
    grad_y=dzdy.subs({xx: x_val, yy: y_val})
    x_val=x_val-(grad_x+last_gx*0.9)*lr #根据梯度更新
    y_val=y_val-(grad_y+last_gy*0.9)*lr
    #注释下面两行就是单纯梯度下降的算法
    last_gx=grad_x
    last_gy=grad_y
    result=np.append(result,[[x_val,y_val]],axis=0) #记录新的梯度下降的点
    #print(x_val,y_val)
    #print("-------------------------------------------")

print(grad_x,grad_y) #打印最终的梯度
#print(result)

2.3 画图(函数3D曲面图+梯度下降3D曲线图+函数等高线图)

#画函数3D曲面图
fig = plt.figure(figsize=(8, 6), dpi=100) #开一个窗口进行显示
ax = Axes3D(fig)
u=np.linspace(-6,6,600)
x,y=np.meshgrid(u,u) #x,y生成网格,画3D曲面图需要
print(x.shape)
z=x**2+y**2+y**3+x*y
ax.plot_surface(x,y,z,rstride=6,cstride=6,cmap=plt.get_cmap('rainbow'),alpha=0.5) #画3D曲面图,alpha调透明度

#画梯度下降3D曲线图
xp=result[:,0] #读取梯度下降点的x坐标
yp=result[:,1]
zp=xp**2+yp**2+yp**3+xp*yp #计算梯度下降的点的z坐标
print(zp.shape)
ax.plot(xp, yp, zp, 'o', c='r',linestyle="-", linewidth=2.5) #画3D曲线图
plt.title("3D-picture")# 设置标题

#画函数等高线图
plt.figure(figsize=(8, 6), dpi=80)
plt.contourf(x, y, z, 8, alpha=0.75, cmap=plt.cm.hot)
C = plt.contour(x, y, z, 10, colors = 'black', linewidth = 0.5)
plt.clabel(C, inline = True, fontsize = 10)# 显示各等高线的数据标签

plt.show()

2.4 完整代码(spyer编译通过)

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

lr=0.01 #学习率
xx,yy = sympy.symbols('x y')
zz=xx**2+yy**2+yy**3+xx*yy #设置函数
dzdx=sympy.diff(zz, xx) #函数求偏导
dzdy=sympy.diff(zz, yy)
x_val=4 #设置初始点
y_val=5
last_gx=0 #保存上一次的梯度
last_gy=0
result=np.array([[4,5]]) #记录梯度下降的点
for epoch in range(122): #迭代次数
    grad_x=dzdx.subs({xx: x_val, yy: y_val}) #给偏导函数赋值计算x方向上的梯度
    grad_y=dzdy.subs({xx: x_val, yy: y_val})
    x_val=x_val-(grad_x+last_gx*0.9)*lr #根据梯度更新
    y_val=y_val-(grad_y+last_gy*0.9)*lr
    #注释下面两行就是单纯梯度下降的算法
    last_gx=grad_x
    last_gy=grad_y
    result=np.append(result,[[x_val,y_val]],axis=0) #记录新的梯度下降的点
    #print(x_val,y_val)
    #print("-------------------------------------------")

print(grad_x,grad_y) #打印最终的梯度
#print(result)

#画函数3D曲面图
fig = plt.figure(figsize=(8, 6), dpi=100) #开一个窗口进行显示
ax = Axes3D(fig)
u=np.linspace(-6,6,600)
x,y=np.meshgrid(u,u) #x,y生成网格,画3D曲面图需要
print(x.shape)
z=x**2+y**2+y**3+x*y
ax.plot_surface(x,y,z,rstride=6,cstride=6,cmap=plt.get_cmap('rainbow'),alpha=0.5) #画3D曲面图,alpha调透明度

#画梯度下降3D曲线图
xp=result[:,0] #读取梯度下降点的x坐标
yp=result[:,1]
zp=xp**2+yp**2+yp**3+xp*yp #计算梯度下降的点的z坐标
print(zp.shape)
ax.plot(xp, yp, zp, 'o', c='r',linestyle="-", linewidth=2.5) #画3D曲线图
plt.title("3D-picture")# 设置标题

#画函数等高线图
plt.figure(figsize=(8, 6), dpi=80)
plt.contourf(x, y, z, 8, alpha=0.75, cmap=plt.cm.hot)
C = plt.contour(x, y, z, 10, colors = 'black', linewidth = 0.5)
plt.clabel(C, inline = True, fontsize = 10)# 显示各等高线的数据标签

plt.show()

3 小建议

3.1 图像方面

如果看到下方这种图,这种情况出现,说明你的 epoch 设置太大了,需要调小很多

纯梯度下降的话 epoch 设置到 2000 多,点都不会继续下降

梯度下降+动量的话 epoch 设置到 122 左右就已经接近最低点了

梯度下降+动量冲出鞍点(代码可视化)

3.2 代码方面

梯度下降算法代码中的 x_val=x_val-(grad_x+last_gx*0.9)*lr 的 0.9 表示的是多大程度保留前一次的梯度

简单来讲,可以类似于一个球滚下坡,刚到达平面的时候,球的还保留着之前下坡时的速度,这个 0.9 就是表示保留 90% 下坡时候的速度

这个 0.9 也正是这里的 动量(momentum)

动量的作用可以避免一些梯度下降时候的 鞍点局部最小值点

我设置的函数就是为了构造 鞍点 ,纯梯度下降就会陷在那个点出不来,而梯度下降+动量就可以避免这个情况,这也是我这篇文章想表达的核心,希望对大家能有帮助,respect!

for epoch in range(152): #迭代次数
    grad_x=dzdx.subs({xx: x_val, yy: y_val}) #给偏导函数赋值计算x方向上的梯度
    grad_y=dzdy.subs({xx: x_val, yy: y_val})
    x_val=x_val-(grad_x+last_gx*0.9)*lr #根据梯度更新
    y_val=y_val-(grad_y+last_gy*0.9)*lr
    #注释下面两行就是单纯梯度下降的算法
    last_gx=grad_x
    last_gy=grad_y
    result=np.append(result,[[x_val,y_val]],axis=0) #记录新的梯度下降的点
    
声明:本文内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎进行举报,并提供相关证据,工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。