梯度下降法
对于一个二元一次函数 y = ax + b
,我们只需要知道两个 (x,y)
点即可获取到 a
、b
的值,我们称其为精确解,如下图:
但是如果该函数中存在已知分布的噪声,那么又该如何求解:
我们可以假设 a
、b
为任意值,则根据输入 x
有预测输出 y'
,实际的 y
值与预测的 y'
的差距我们称为损失
,对于已知的若干 (x,y)
点的损失则为损失函数:
l
o
s
s
=
1
n
∑
i
=
1
n
(
y
i
−
y
′
i
)
2
loss = \frac{1}{n} \sum_{i=1}^n (y_i - {y'}_i)^2
loss=n1i=1∑n(yi−y′i)2
我们最终的目的是求得a
、b
使损失值最小,所以可以从当前的参数取值,一步步的按照损失函数下坡的方向下降,直到走到最低点。第一要保证 loss 是下降的,第二要使得下降的趋势尽可能的快。微积分的基础知识告诉我们:沿着梯度的反方向,是函数值下降最快的方向,所以只需要对损失函数求导,并且沿着导数的反方向
逐步移动,则会找到最佳的 a
、b
。我们称这种求解方法为梯度下降法
,称该问题为线性回归
问题。
代码实现
对 a
、b
分别求偏导数:
l
o
s
s
=
1
n
∑
i
=
1
n
(
a
x
i
+
b
−
y
i
)
2
loss = \frac{1}{n} \sum_{i=1}^n (ax_i + b - y_i)^2
loss=n1i=1∑n(axi+b−yi)2
∂
l
o
s
s
∂
a
=
2
n
∑
i
=
1
n
[
(
a
x
i
+
b
−
y
i
)
x
i
]
{\partial loss \over \partial a} = {2\over n}\sum_{i=1}^n [(ax_i+b-y_i)x_i]
∂a∂loss=n2i=1∑n[(axi+b−yi)xi]
∂
l
o
s
s
∂
b
=
2
n
∑
i
=
1
n
(
a
x
i
+
b
−
y
i
)
{\partial loss \over \partial b} = {2\over n}\sum_{i=1}^n (ax_i+b-y_i)
∂b∂loss=n2i=1∑n(axi+b−yi)
# 线性回归模型
import numpy as np
import matplotlib.pyplot as plt
# 创建样本 y = ax+b,其中x = 1.72,b = 3.69
def create_sample():
# 生成 0-100 之间 200 个随机数
x = np.random.rand(200) * 10
y = x * 1.72 + 3.69 + (np.random.normal(size=200))
# 转换为 [[x1,y1],[x2,y2]...[xn,yn]] 的矩阵
return np.array(list(zip(x, y)))
# 计算梯度,并更新 a,b 值
def gradient(a_cur, b_cur, points, learning_rate):
a_gradient = 0
b_gradient = 0
points_length = len(points)
# 计算梯度
for i in range(0, points_length):
x = points[i, 0]
y = points[i, 1]
a_gradient += (2 / points_length) * x * (a_cur * x + b_cur - y)
b_gradient += (2 / points_length) * (a_cur * x + b_cur - y)
# 更新 a、b 值
new_a = a_cur - learning_rate * a_gradient
new_b = b_cur - learning_rate * b_gradient
return [new_a, new_b]
# 计算损失
def computer_loss(a, b, points):
points_length = len(points)
loss = 0
for i in range(0, points_length):
x = points[i, 0]
y = points[i, 1]
loss += (a * x + b - y) ** 2
loss /= points_length
return loss
points = create_sample()
print("points", points)
a = 0
b = 0
loss_list = list()
for i in range(0, 100000):
[a, b] = gradient(a, b, points, 0.001)
loss_list.append(computer_loss(a, b, points))
#求得 a = 1.7219045715547612 b = 3.6145089870651086
print("a = {} b = {}".format(a, b))
# 绘制损失函数
plt.plot(loss_list)
plt.show()
最终损失区域0