梯度下降法是一种常用的迭代方法,其目的是让输入向量找到一个合适的迭代方向,使得输出值能达到局部最小值。
以为例:
如图,z的图像在3维中的表示,现有一点p(10,10) 想通过梯度算法得到该图像的最小值(即(0,0)处)。可对x和y两个方向上进行偏导得到新的x,y。
梯度:梯度的本意是一个向量(矢量),表示某一函数在该点处的方向导数沿着该方向取得最大值,即函数在该点处沿着该方向(此梯度的方向)变化最快,变化率最大(为该梯度的模)。
梯度公式为:X方向:,Y方向:
。每次x,y按照一定的步长减小,逼近最低点。
学习率(lr):每次算出偏导后都要乘学习率得到每次x,y变化的步长,学习率要取一个合理的值。
def func(x, y):
return x ** 2 + y ** 2
def gradient_descent(x0, y0, lr=0.0001, k=1e-10, iter=100000, epoch=0):
"""
:param x0: 初始x值
:param y0: 初始y值
:param lr: 学习率
:param k: 新旧位置的距离误差
:param iter: 迭代次数
:param epoch: 计算迭代次数
:return:
"""
print("初始点为(%d, %d)" % (x0, y0))
f0 = func(x0, y0)
for i in range(iter):
y1 = y0 - lr * (2 * y0) # y的偏导数
x1 = x0 - lr * (2 * x0) # x的偏导数
f1 = func(x1, y1) # 算出新的位置
print("第%d次迭代的结果为(%.10f, %.10f, %.10f)" % (i, f1, x1, y1))
if abs(f1 - f0) < k: # 新旧位置的距离小于k,说明收敛
epoch = i
break
else:
f0 = f1
y0 = y1
x0 = x1
print("*"*20)
print("经过%d次的迭代,得最优点为(%.10f, %.10f)" %(epoch, x1, y1))
if __name__ == "__main__":
gradient_descent(10, 10)
输出的结果:
程序结束后得到最低点(最优点):(0.0003534954, 0.0003534954) ,我们知道最小值其实在(0,0)点,通过修改学习率和新旧位置的距离误差可以无限逼近(0,0)点 。
附:python画出代码(jupyter):
%matplotlib inline
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
import math
from matplotlib import pyplot as plt
fig = plt.figure()
ax = Axes3D(fig)
x = np.arange(-3 * np.pi, 3 * np.pi, 0.1) # 创建一些x和y去画出
y = np.arange(-3 * np.pi, 3 * np.pi, 0.1) # 该二元函数图像
X, Y = np.meshgrid(x, y) # 网格的创建,这个是关键
Z = X **2 + Y ** 2 # 函数
plt.xlabel('x')
plt.ylabel('y')
ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap='rainbow')
plt.show()