目录
完整实现代码
import numpy as np
from matplotlib import pyplot as plt
def show_iter_process(w, b, train_x, train_y, i):
# 可视化迭代情况
if i%10==0:
predict_y = w[0]*train_x**2 + w[1]*train_x + b
plt.plot(train_x, train_y)
plt.plot(train_x, predict_y)
plt.legend(['true', 'predict'], loc='upper right')
plt.show(block=False)
plt.pause(0.5) # 显示0.5秒
plt.close()
def create_data():
"""根据函数 y=2x²+500 生成100个数据点"""
x = np.linspace(0, 15, 100) # x不要有负值, 现实数据都是正值
y = 2*x**2 + 500
return x,y
def grad_test(train_x, train_y):
"""猜测数据模型为 y=ax²+bx+c, 用梯度下降法, 更新猜测模型的参数使得逼近目标函数 y=2x²+500"""
# 虽然输入数据只有一个 特征值x, 但我们假设 数据有两个 特征值x²和x, 所以扩张输入数据为两个属性.
# 这种扩张不增加输入数据的维度, 因为两个属性x²和x具有关联性
train_x_expand = np.array([[x**2, x] for x in train_x])
w = [1,1] # 初始化权重
b = 1 # 初始化偏置
lrate = [0.001, 0.000001] # [b的学习率, w的学习率]. 可以体验调参的重要性
update_times = 1000 # 迭代次数
old_loss = [] # 记录上一次误差损失, 当迭代出现损失值不降时停止迭代
for i in range(update_times):
# 梯度, b偏置部分
grad_b = (np.dot(train_x_expand, w) + b - train_y).sum()
# 梯度, w权重部分
grad_w = np.dot(np.dot(train_x_expand, w) + b - train_y, train_x_expand)
# 更新w,b
b = b - lrate[0] * grad_b, 2
w = w - lrate[1] * grad_w, 2
# 计算平方误差
loss = ((np.dot(train_x_expand, w) + b - train_y)**2).sum()
# print("loss:", round(loss, 2))
if not old_loss:
old_loss.append(loss)
elif loss == old_loss[-1]:
print("迭代次数:", i, "最优损失值:", loss)
return w,b,old_loss
else:
# print("迭代次数:", i, "损失值:", loss)
# 可视化迭代情况
show_iter_process(w, b, train_x, train_y, i)
old_loss.append(loss)
return w,b,old_loss
train_x, train_y = create_data()
w,b,loss_line = grad_test(train_x, train_y)
print(f"最优参数: w = {w}, b = {b}")
print(f"预测函数: y = {w[0]}x² + {w[1]}x + {b}")
# 损失值变化曲线
# plt.plot(range(len(loss_line)), loss_line)
# plt.xlabel("iter times")
# plt.ylabel("loss")
# plt.show()
# 原数据真实曲线及预测曲线
predict_y = w[0]*train_x**2 + w[1]*train_x + b
plt.plot(train_x, train_y)
plt.plot(train_x, predict_y)
plt.legend(['true', 'predict'], loc='upper right')
plt.show()
核心公式
学习率η>0,X、Y是矩阵,是预测结果
公式含义的讨论
①b = b-(y测-y实)
讨论这种情况
这种情况说明预测结果高于实际值,需要将预测函数下移
回到公式,对照图像,因为的平均值>的平均值,
所以<
推论:b↑则y测上移,b↓则y测下移
②w = w-(y测-y实)X
讨论这种情况
假设与关于交点对称,与关于对称,即 = 0
这种情况预测函数需要逆时针转一定角度才能跟上实际函数
回到公式,结合图像,有:
所以 >
推论:w↑则y测逆时针转
③推理合理性讨论
用y=kx+b验证:
- b↑则y↑,所以①中推论合理
- k↑(斜率增加),函数图像逆时针转,所以②的推论合理
详细公式推理
设函数关系为
损失值计算方法为
是已知量,需要求解的是:当取什么值时,损失值最小
根据损失计算方法,设损失函数设为
初始化 值为1,计算损失值,以梯度下降法更新 的值,使得降低。
一阶泰勒展开公式:
则
①
②
①②应该用一个式子写,但为了清晰,拆成两个式子
合并写法是:
目标是降低的值,也就是
(这里的数学解释非常不专业)的最大值是梯度值,我个人把梯度值理解为广义的正值,负梯度值是广义的负值,且 |偏导值|≤梯度值
所以,对①式,要使降低,可令
【 其中是梯度方向】
两边乘以▽,然后两边除以▽的模得:
令【学习率η>0,通常取0.01】
所以 更新的公式为:
【这里的和是多维的】
理解为:比小了0.01倍梯度值
求偏导举例
更新公式举例:
求导结果中的“2”归入学习率,或者说忽略常数“2”
x = (x1,…,xn)是一个样例,这个样例有n个属性值
所以上述更新公式只针对单个数据样例,为了在所用数据上使用该公式,需要调整
以举例
针对所有样例求,并求和,然后更新。更新同理
公式-矩阵形式 n个属性值
公式对应代码
# train_x 形如 [ [ 0. 0.]
[ 25. 5.]
[100. 10.]
[225. 15.]]
# train_y 形如 [ 5. 55. 205. 455.]
# 注:train_x**2表示样例的第一个属性值是x²,train_x表示样例的第二个属性值是x
for i in range(update_times):
# 梯度, b偏置部分
grad_b = (np.dot(train_x_expand, w) + b - train_y).sum()
# 梯度, w权重部分
grad_w = np.dot(np.dot(train_x_expand, w) + b - train_y, train_x_expand)
# 更新w,b
b = b - lrate[0] * grad_b, 2
w = w - lrate[1] * grad_w, 2
参考文章
梯度下降详解csdnhttps://blog.csdn.net/maizousidemao/article/details/106540576