问题引入
现有一组训练数据train_x
和train_y
:
train_x = [1, 2, 3, 5, 6]
train_y = [2, 4, 5, 7, 9]
执行下述代码后
mp.scatter(train_x, train_y)
mp.show()
得到散点图:
通过观察我们可以发现, 训练集中的5个点近似分布在一条直线上, 我们将这条直线用y = w0 + w1*x
表示. 线性回归的目的, 就是求出一组合适的w0
和w1
损失函数
我们如何判断求得的w0
和w1
是否合适呢? 显然, 我们求得的直线应该尽可能地靠近训练集中的5个点.
我们用损失函数loss_function
来描述数据"靠近"回归直线的程度.
假设对于某一个x
, x
对应的值为y
, 我们将’x’带入直线方程中得到为y'
, 则损失定义为0.5*(y'-y)^2
, 即0.5*(w0+w1*x-y)^2
. 损失函数即为全部数据损失的和.
此时, 问题转化成了: 如何求得一组w0
和w1
, 使得损失函数取得极小值.
梯度下降法
梯度下降法能够帮助我们高效地找到损失函数的极小值(或近似极小值), 思路如下:
- 规定梯度下降次数
times
和学习率lrate
times = 1000
lrate = 0.01
- 随机选择一组
w0
和w1
的取值
w0, w1 = 1, 1
- 对
w0
和w1
分别求偏导数d0
和d1
d0 = (w0 + w1 * train_x - train_y).sum()
d1 = (train_x * (w0 + w1*train_x - train_y)).sum()
- 更新
w0
和w1
的值
w0 = w0 - lrate*d0
w1 = w1 - lrate*d1
- 重复步骤
3,4
, 次数为times
完整代码
times = 1000
lrate = 0.01
w0, w1 = 1, 1
for i in range(0, times):
d0 = (w0 + w1 * train_x - train_y).sum()
d1 = (train_x * (w0 + w1*train_x - train_y)).sum()
w0 = w0 - lrate * d0
w1 = w1 - lrate * d1
print("w0=", w0)
print("w1=", w1)
# w0= 1.0116288204591268
# w1= 1.2906974643094398
得到的回归直线如下图所示:
调参
在上述训练过程中, times
和lrate
的取值会对结果产生比较大的影响, 为此我们需要调整二者的取值, 以达到更好的训练效果.
我们适当调整一下训练代码, 将每一次梯度下降后的w0
,w1
和loss
都保存下来, 最后以图像的形式展示出来.
times = 1000
lrate = 0.001
w0, w1, losses, cnt = [3], [3], [], []
for i in range(1, times+1):
cnt.append(i)
loss = ((w0[-1] + w1[-1] * train_x - train_y) ** 2).sum() / 2
losses.append(loss)
d0 = (w0[-1] + w1[-1] * train_x - train_y).sum()
d1 = (train_x * (w0[-1] + w1[-1] * train_x - train_y)).sum()
w0.append(w0[-1] - lrate * d0)
w1.append(w1[-1] - lrate * d1)
line_x = np.linspace(train_x.min(), train_x.max(), 100)
line_y = w0[-1] + w1[-1] * line_x
print(w0[-1], w1[-1])
mp.figure('result')
mp.scatter(train_x, train_y)
mp.plot(line_x, line_y, color="orange")
mp.figure('process')
mp.subplot(311)
mp.ylabel(r'$w_0$')
mp.plot(cnt, w0[1:], color="blue")
mp.subplot(312)
mp.ylabel(r'$w_1$')
mp.plot(cnt, w1[1:], color="green")
mp.subplot(313)
mp.ylabel(r'$loss$')
mp.plot(cnt, losses, color="blue")
mp.show()
得到下图:
我们观察到, 虽然loss
已经基本不变, 但w0
仍然在变化, 所以我们应当适当增加times
的取值, 不妨令times = 5000
, 得到下图:
调用API
import numpy as np
import matplotlib.pyplot as mp
import sklearn.linear_model as lm
train_x = np.array([1, 2, 3, 5, 6])
train_y = np.array([2, 4, 5, 7, 9])
# 创建模型
model = lm.LinearRegression()
# 训练模型
# 输入为一个二维数组(一行一样本,一列一特征)
# 输出为一个一维数组
train_x = train_x.reshape(-1, 1) # 将train_x转化为一个n行1列的二维数组
model.fit(train_x, train_y)
# 模型预测
# 输入为一个二维数组(一行一样本,一列一特征)
# 输出为一个一维数组
pre_y = model.predict(train_x)
# 绘制回归线
mp.figure('result')
mp.scatter(train_x, train_y)
mp.plot(train_x, pre_y, color="orange")
mp.show()
得到下图: