一 需求解函数
f() 和 g()函数分别为求y值和求导数的函数。
目的:求该函数的最小值:
代码:
import numpy as np
import matplotlib.pyplot as plt
f = lambda x : (x - 3.5) ** 2 - 4.5 * x + 10
g = lambda x : 2 * (x - 3.5) - 4.5
x = np.linspace(0, 11.5, 100)
y = f(x)
plt.plot(x, y)
二 随机初始化一个值
在 0 - 12 中随机取一个值:10
k = np.random.randint(0, 12)
print('随机取到的值k:', k) # 10
三 查看此时的斜率
查看此时的切线方程:
k = 10
g(x) = 8.5 # 斜率
f(x) = 7.5 # y值
# 该点的切线方程:y = 8.5 * x - 77.5
plt.plot(x, y)
plt.scatter(k, f(k), color = 'red', s = 30)
# 生成x的范围
x_values = np.linspace(8, 11.5, 100)
# 计算对应的y值
y_values = 8.5 * x_values - 77.5
# 绘制直线
plt.plot(x_values, y_values, color='blue', label='Line')
四 根据斜率和学习率确定下一个点
设置学习率为0.2,与初始点的梯度反向进行下降,如果在上一个点斜率为正,说明需要x需要向左移动才能接近最小值:next_k = k - 学习率*斜率
学习率可以改,设置值比较大移动比较快,设置比较小移动比较慢。
求到的第一个K:8.3
learing_ratio = 0.2
next_k = k - learing_ratio*g(k)
plt.plot(x, y)
plt.scatter(k, f(k), color = 'red', s = 30)
plt.scatter(next_k, f(next_k), color = 'red', s = 30)
print(next_k, f(next_k))
# 8.3 -4.309999999999995
五 根据该逻辑继续计算下一个值
用上一次计算的 8.2 计算一个值:next_k2 = next_k - learing_ratio*g(next_k) 。
求到的第二个K:7.28
learing_ratio = 0.2
next_k2 = next_k - learing_ratio*g(next_k)
plt.plot(x, y)
plt.scatter(k, f(k), color = 'red', s = 30)
plt.scatter(next_k, f(next_k), color = 'red', s = 30)
plt.scatter(next_k2, f(next_k2), color = 'red', s = 30)
print(next_k2, f(next_k2))
# 7.28 -8.471599999999995
六 同时查看两次迭代过程中 y值的变化率
查看两次迭代过程中 y值的变化率分别为:-11.559999999999995 -4.1616
plt.plot(x, y)
plt.scatter([k, next_k, next_k2], [f(k), f(next_k), f(next_k2)],
color = 'red', s = 30)
# 绘制直线
plt.plot(x, [f(k)] * len(x), label='y=5', color='blue', linestyle='--')
plt.plot(x, [f(next_k)] * len(x), label='y=5', color='green', linestyle='--')
plt.plot(x, [f(next_k2)] * len(x), label='y=5', color='red', linestyle='--')
print(f(next_k) - f(k), f(next_k2) - f(next_k)) # -11.5599999999-4.1616
七 设置循环,求最接近的目标值
注意:截止条件设置的变化率是x 的变化率,就是当 斜率的变化值足够小 的时候截止,其实也是y值的变化率乘学习率后的变化值 !!
k = k - learing_ratio*g(k)
设置截止循环条件:precision = 0.0001
learing_ratio = 0.2
last_k = k + 0.1
# 精确度
precision = 0.0001
k_ = [k]
count = 0 # 记录迭代次数
while True:
if np.abs(k - last_k) < precision:
break
last_k = k
count += 1
k = k - learing_ratio*g(k) # 迭代
k_.append(k)
print(f'-> 迭代次数cnt:{count:2},更新后的x:{k:0.7f}, 实时的y:{f(k):0.7f}')
print(f'梯度下降的次数:{count}')
plt.plot(x, y)
print('最后的k值:', k) # 5.75009323204022
# 散点图,转换为array数组可以用f(x)直接求列表的y值
k_ = np.array(k_)
plt.scatter(k_, f(k_), color = 'red', s = 30)
PS:最后的列表直接求y值是先转换为了 np.array数组!!!
实际值:5.75
循环21次,最后求到的k值:5.75009323204022