本篇介绍求解最优化问题的一种数值算法-- 梯度下降算法。
在微积分中我们学过,沿着梯度grad(f)方向,函数f的方向导数有最大值。所以要找到函数的极大值,最好的方法是沿着该函数的梯度方向探寻,称之为梯度上升算法。同理,要找到函数的极小值,沿着该函数的梯度的相反方向探寻,称之为梯度下降算法。在机器学习领域,我们常需求解权重参数取何值时损失函数最小,梯度下降算法是一种很重要的算法。
上述公式就是在梯度下降算法中,用于迭代求解各自变量的值。其中alpha 为迭代步长(需人为调参)。当函数值的变化量足够小,满足精度要求,或者迭代步数已足够时,就可以退出迭代。
下面以一个普通的二元函数为例,介绍梯度下降算法的基本实现。
二元函数的梯度公式如下:
此例中二元函数为:
z(x,y)= x**2 + 2*y**2 +2*x*y +4*x - 16*y +10
下面我们先利用python的符号计算模块sympy来计算它的理论最小值:
from sympy import *
x, y = symbols("x y")#创建符号变量x和y
z = x**2 + 2*y**2 +2*x*y +4*x - 16*y +10
print("z对x的一阶偏导数:",diff(z,x))
print("z对x的二阶偏导数:",diff(z,x,2))
print("z对y的一阶偏导数:",diff(z,y))
print("z对y的二阶偏导数:",diff(z,y,2))
print("两个二阶偏导数都为正,所以存在极小值")
print()
print("x, y 如下时:")
r = solve([diff(z,x), diff(z,y)],x,y) #求解方程组,返回一字典
print(r)
print("z取极小值,值为:", end =''); print(z.subs({x :r[x], y:r[y]}))
print("理论解 求解完毕!",end ="\n\n")
结果如下:
z对x的一阶偏导数: 2*x + 2*y + 4
z对x的二阶偏导数: 2
z对y的一阶偏导数: 2*x + 4*y - 16
z对y的二阶偏导数: 4
两个二阶偏导数都为正,所以存在极小值
x, y 如下时:
{x: -12, y: 10}
z取极小值,值为:-94
理论解 求解完毕!
下面是梯度下降算法的示例:
gx= diff(z,x)
gy= diff(z,y)
print("梯度下降算法")
func_z = lambda x,y : x**2 + 2*y**2 +2*x*y +4*x - 16*y +10
x_, y_ = 0, 0 #随便初始化 x 和 y
z_last = func_z(x_, y_)
alpha = 0.1 #步长,可以调整
x_ -= alpha * gx.evalf(subs ={x:x_, y:y_}, n =21)
y_ -= alpha * gy.evalf(subs ={x:x_, y:y_}, n =21)
z_offset = abs( func_z(x_, y_) - z_last)
i=1
while z_offset>0.000000001:
#print(x_,y_,z_last)
x_ -= alpha * gx.evalf(subs ={x:x_, y:y_}, n =21)
y_ -= alpha * gy.evalf(subs ={x:x_, y:y_}, n =21)
z_new = func_z(x_, y_)
z_offset = abs(z_new - z_last)
z_last = z_new
i += 1
print("x, y, z_min:") ; print(x_, y_, z_last)
print("迭代步数:%d, 精度: %.21f" %(i, z_offset))
利用迭代公式依次更新x,y 和 z 的值,当z的变化量很小很小,满足精度要求时就可以停止迭代。
结果如下,可以看到结果收敛很快,精度很高。
x, y, z_min:
-11.9999033284856889273 9.99994395025263471318 -93.9999999952082978705
迭代步数:135, 精度: 0.000000000919632075122
注意,由于函数可能有多个极小值,所以,梯度下降算法有可能求得局部的最小值。需要合理设置各自变量的初始值,以及迭代步长,以免陷入局部最优解。
本文分享自 Python可视化编程机器学习OpenCV 微信公众号,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文参与 腾讯云自媒体分享计划 ,欢迎热爱写作的你一起参与!