Python-Level5-day02:回归问题:线性模型定义,训练(损失函数,梯度下降);线性回归代码实现与评价指标;多项式回归模型定义与代码实现;过拟合欠拟合

一、线性模型

1. 概述

线性模型是自然界最简单的模型之一,它描述了一个(或多个)自变量对另一个因变量的影响是呈简单的比例、线性关系.例如:

  • 住房每平米单价为1万元,100平米住房价格为100万元,120平米住房为120万元;

  • 一台挖掘机每小时挖100m^3沙土,工作4小时可以挖掘400m^3沙土.

线性模型在二维空间内表现为一条直线,在三维空间内表现为一个平面,更高维度下的线性模型很难用几何图形来表示(称为超平面).如下图所示:

二维空间下线性模型表现为一条直线

三维空间下线性模型表现为一个平面

线性回归是要根据一组输入值和输出值(称为样本),寻找一个线性模型,能最佳程度上拟合于给定的数值分布,从而再给定新的输入时预测输出。样本如下表所示:

输入(x)输出(y)
0.55.0
0.65.5
0.86.0
1.16.8
1.46.8

根据样本拟合的线性模型如下图所示:

2. 线性模型定义

设给定一组属性x, x=(x_1;x_2;...;x_n),线性方程的一般表达形式为:

$$
y = w_1x_1 + w_2x_2 + w_3x_3 + ... + w_nx_n + b
$$

写成向量形式为:

$$
y = w^Tx + b
$$

其中,w=(w_1;w_2;...;w_n), x=(x_1;x_2;...;x_n),w和b经过学习后,模型就可以确定. 当自变量数量为1时,上述线性模型即为平面下的直线方程:

$$
y = wx + b
$$

线性模型形式简单、易于建模,却蕴含着机器学习中一些重要的基本思想. 许多功能强大的非线性模型可以在线性模型基础上引入层级结构或高维映射而得. 此外,由于w直观表达了各属性在预测中的重要性,因此线性模型具有很好的可解释性.例如,判断一个西瓜是否为好瓜,可以用如下表达式来判断:

$$
f_{好瓜}(x) = 0.2x_{色泽} + 0.5x_{根蒂} + 0.3x_{敲声} + 1
$$

上述公式可以解释为,一个西瓜是否为好瓜,可以通过色泽、根蒂、敲声等因素共同判断,其中根蒂最重要(权重最高),其次是敲声和色泽.

3. 模型训练

在二维平面中,给定两点可以确定一条直线.但在实际工程中,可能有很多个样本点,无法找到一条直线精确穿过所有样本点,只能找到一条与样本”足够接近“或”距离足够小“的直线,近似拟合给定的样本.如下图所示:

如何确定直线到所有样本足够近呢?可以使用损失函数来进行度量.

1)损失函数

损失函数用来度量真实值(由样本中给出)和预测值(由模型算出)之间的差异。损失函数值越小,表明模型预测值和真实值之间差异越小,模型性能越好;损失函数值越大,模型预测值和真实值之间差异越大,模型性能越差.在回归问题中,均方差是常用的损失函数,其表达式如下所示:

$$
E = \frac{1}{2}\sum_{i=1}^{n}{(y - y')^2}
$$

其中,y为模型预测值,y'为真实值. 均方差具有非常好的几何意义,对应着常用的欧几里得距离(简称欧式距离). 线性回归的任务是要寻找最优线性模型,是的损失函数值最小,即:

$$
(w^*, b^*) = arg min \frac{1}{2}\sum_{i=1}^{n}{(y - y')^2} \\ = arg min \frac{1}{2}\sum_{i=1}^{n}{(y' - wx_i - b)^2}
$$

基于均方误差最小化来进行模型求解的方法称为“最小二乘法”. 线性回归中,最小二乘法就是试图找到一条直线,是所有样本到直线的欧式距离之和最小. 可以将损失函数对w和b分别求导,得到损失函数的导函数,并令导函数为0即可得到w和b的最优解.

2)梯度下降法

① 为什么使用梯度下降

在实际计算中,通过最小二乘法求解最优参数有一定的问题,很少用最小二乘法:

(1)最小二乘法需要计算逆矩阵,有可能逆矩阵不存在;

(2)当样本特征数量较多时,计算逆矩阵非常耗时甚至不可行.

所以,在工程实际计算中,通常采用梯度下降法来求解损失函数的极小值,从而找到模型的最优参数.

② 什么是梯度下降

梯度(gradient)是一个向量(矢量,有方向),表示某一函数在该点处的方向导数沿着该方向取得最大值,即函数在该点处沿着该方向(此梯度的方向)变化最快,变化率最大.损失函数沿梯度相反方向收敛最快(即能最快找到极值点).当梯度向量为零(或接近于零),说明到达一个极值点,这也是梯度下降算法迭代计算的终止条件.

这种按照负梯度不停地调整函数权值的过程就叫作“梯度下降法”.通过这样的方法,改变权重让损失函数的值下降得更快,进而将值收敛到损失函数的某个极小值.

通过损失函数,我们将“寻找最优参数”问题,转换为了“寻找损失函数最小值”问题.梯度下降法算法描述如下:

(1)损失是否足够小?如果不是,计算损失函数的梯度. (2)按梯度的反方向走一小步,以缩小损失. (3)循环到(1).

梯度下降法中通过沿着梯度负方向不断调整w参数,从而逐步接近损失函数极小值所在点. 如下图所示:这里损失函数的自变量是w与b,线性方程的自变量是x

③ 参数更新法则

在直线方程中,有两个参数需要学习,w_0和w_1(即w与b),梯度下降过程中,分别对这两个参数单独进行调整,调整法则如下:

$$
w_0 = w_0 + \Delta w_0\\ w_1 = w_1 + \Delta w_1
$$

\Delta w_0和\Delta w_1可表示为:

$$
\Delta w_0 = -\eta \frac{\Delta loss}{\Delta w_0}\\ \Delta w_1 = -\eta \frac{\Delta loss}{\Delta w_1}\\
$$

其中,\eta称为学习率(控制收敛步数大小),其中\frac{\Delta loss}{\Delta w_i}为梯度(即损失函数关于参数w_i的偏导数). 引入学习率发现,梯度越大更新的步数就越大,梯度越小,更新的步数就越小。损失函数表达式为:

$$
loss =\frac{1}{2}\sum(y - y')^2 = \frac{1}{2}\sum((y-(w_0+w_1x))^2)
$$

对损失函数求导(求导过程见补充知识),可得w_0, w_1的偏导数为(记住这个求导结果就行):

$$
\frac{\Delta loss}{\Delta w_0} = \sum((y - y')(-1)) = -\sum(y - y')\\ \frac{\Delta loss}{\Delta w_1} = \sum((y - y')(-x)) = -\sum(x(y - y'))
$$

4. 实现线性回归

① 自己编码实现

以下是实现线性回归的代码:

# 线性回归示例
import numpy as np
import matplotlib.pyplot as mp
from mpl_toolkits.mplot3d import axes3d
import sklearn.preprocessing as sp

# 训练数据集
train_x = np.array([0.5, 0.6, 0.8, 1.1, 1.4])  # 输入集
train_y = np.array([5.0, 5.5, 6.0, 6.8, 7.0])  # 输出集

n_epochs = 1000  # 迭代次数
lrate = 0.01  # 学习率
epochs = []  # 记录迭代次数
losses = []  # 记录损失值

w0, w1 = [1], [1]  # 模型初始值

for i in range(1, n_epochs + 1):
    epochs.append(i)  # 记录第几次迭代

    y = w0[-1] + w1[-1] * train_x  # 取出最新的w0,w1计算线性方程输出
    # 损失函数(均方差)
    loss = (((train_y - y) ** 2).sum()) / 2
    losses.append(loss)  # 记录每次迭代的损失值

    print("%d: w0=%f, w1=%f, loss=%f" % (i, w0[-1], w1[-1], loss))

    # 计算w0,w1的偏导数
    d0 = -(train_y - y).sum()
    d1 = -(train_x * (train_y - y)).sum()

    # 更新w0,w1
    w0.append(w0[-1] - (d0 * lrate))
    w1.append(w1[-1] - (d1 * lrate)) 

程序执行结果:

<span style="background-color:#f8f8f8"><span style="color:#333333">1 w0=1.00000000 w1=1.00000000 loss=44.17500000
2 w0=1.20900000 w1=1.19060000 loss=36.53882794
3 w0=1.39916360 w1=1.36357948 loss=30.23168666
4 w0=1.57220792 w1=1.52054607 loss=25.02222743
5 w0=1.72969350 w1=1.66296078 loss=20.71937337
......
996 w0=4.06506160 w1=2.26409126 loss=0.08743506
997 w0=4.06518850 w1=2.26395572 loss=0.08743162
998 w0=4.06531502 w1=2.26382058 loss=0.08742820
999 w0=4.06544117 w1=2.26368585 loss=0.08742480
1000 w0=4.06556693 w1=2.26355153 loss=0.08742142</span></span>

可以给数据加上可视化,让结果更直观.添加如下代码:

###################### 训练过程可视化 ######################
# 训练过程可视化
## 损失函数收敛过程
w0 = np.array(w0[:-1])
w1 = np.array(w1[:-1])

mp.figure("Losses", facecolor="lightgray")  # 创建一个窗体
mp.title("epoch", fontsize=20)
mp.ylabel("loss", fontsize=14)
mp.grid(linestyle=":")  # 网格线:虚线
mp.plot(epochs, losses, c="blue", label="loss")
mp.legend()  # 图例
mp.tight_layout()  # 紧凑格式

## 显示模型直线
pred_y = w0[-1] + w1[-1] * train_x  # 根据x预测y
mp.figure("Linear Regression", facecolor="lightgray")
mp.title("Linear Regression", fontsize=20)
mp.xlabel("x", fontsize=14)
mp.ylabel("y", fontsize=14)
mp.grid(linestyle=":")
mp.scatter(train_x, train_y, c="blue", label="Traing")  # 绘制样本散点图
mp.plot(train_x, pred_y, c="red", label="Regression")
mp.legend()

# 显示梯度下降过程(复制粘贴即可,不需要编写)
# 计算损失函数曲面上的点 loss = f(w0, w1)
arr1 = np.linspace(0, 10, 500)  # 0~9间产生500个元素的均匀列表
arr2 = np.linspace(0, 3.5, 500)  # 0~3.5间产生500个元素的均匀列表

grid_w0, grid_w1 = np.meshgrid(arr1, arr2)  # 产生二维矩阵

flat_w0, flat_w1 = grid_w0.ravel(), grid_w1.ravel()  # 二维矩阵扁平化
loss_metrix = train_y.reshape(-1, 1)  # 生成误差矩阵(-1,1)表示自动计算维度
outer = np.outer(train_x, flat_w1)  # 求外积(train_x和flat_w1元素两两相乘的新矩阵)
# 计算损失:((w0 + w1*x - y)**2)/2
flat_loss = (((flat_w0 + outer - loss_metrix) ** 2).sum(axis=0)) / 2
grid_loss = flat_loss.reshape(grid_w0.shape)

mp.figure('Loss Function')
ax = mp.gca(projection='3d')
mp.title('Loss Function', fontsize=14)
ax.set_xlabel('w0', fontsize=14)
ax.set_ylabel('w1', fontsize=14)
ax.set_zlabel('loss', fontsize=14)
ax.plot_surface(grid_w0, grid_w1, grid_loss, rstride=10, cstride=10, cmap='jet')
ax.plot(w0, w1, losses, 'o-', c='orangered', label='BGD', zorder=5)
mp.legend(loc='lower left')

mp.show()

数据可视化结果如下图所示:

回归得到的线性模型

损失函数收敛过程

梯度下降过程

② 通过sklearn API实现

同样,可以使用sklearn库提供的API实现线性回归.代码如下:


# 利用LinearRegression实现线性回归
import numpy as np
import sklearn.linear_model as lm  # 线性模型# 线性模型
import sklearn.metrics as sm  # 模型性能评价模块
import matplotlib.pyplot as mp

train_x = np.array([[0.5], [0.6], [0.8], [1.1], [1.4]])  # 输入集
train_y = np.array([5.0, 5.5, 6.0, 6.8, 7.0])  # 输出集

#核心三板斧
# 创建线性回归器
model = lm.LinearRegression()
# 用已知输入、输出数据集训练回归器
model.fit(train_x, train_y)
# 根据训练模型预测输出
pred_y = model.predict(train_x)

print("coef_:", model.coef_)  # 系数
print("intercept_:", model.intercept_)  # 截距

# 可视化回归曲线
mp.figure('Linear Regression', facecolor='lightgray')
mp.title('Linear Regression', fontsize=20)
mp.xlabel('x', fontsize=14)
mp.ylabel('y', fontsize=14)
mp.tick_params(labelsize=10)
mp.grid(linestyle=':')

# 绘制样本点
mp.scatter(train_x, train_y, c='blue', alpha=0.8, s=60, label='Sample')

# 绘制拟合直线
mp.plot(train_x,  # x坐标数据
        pred_y,  # y坐标数据
        c='orangered', label='Regression')

mp.legend()
mp.show()

执行结果:

5. 回归模型评价指标

(1)平均绝对误差(Mean Absolute Deviation):单个观测值与算术平均值的偏差的绝对值的平均;

(2)均方误差:单个样本到平均值差值的平方平均值;(上面的损失函数就的用它)

(3)MAD(中位数绝对偏差):与数据中值绝对偏差的中值;

 

(4)R2决定系数:趋向于1,模型综合性能越好;趋向于0,模型综合性能越差.

总结:1.什么是线性回归:利用线性模型(最高次项为1)执行预测。

2.为什么线性回归:根据已知结果去预测未知。

3.什么情况用线性回归:数据分布呈现线性分布(直线与平面)

4.如何实现线性回归:先给一个y=wx+b一个初始模型;然后构建损失函数用来度量与实际差异;接着利用梯度下降法优化参数w,b ;最后利用优化好模型预测

二、多项式回归

1. 什么是多项式回归

线性回归适用于数据呈线性分布的回归问题.如果数据样本呈明显非线性分布,线性回归模型就不再适用(下图左),而采用多项式回归可能更好(下图右).例如:

2. 多项式模型定义

与线性模型相比,多项式模型引入了高次项,自变量的指数大于1,例如一元二次方程:

$$
y = w_0 + w_1x + w_2x^2
$$

一元三次方程:

$$
y = w_0 + w_1x + w_2x^2 + w_3x ^ 3
$$

推广到一元n次方程:

$$
y = w_0 + w_1x + w_2x^2 + w_3x ^ 3 + ... + w_nx^n
$$

上述表达式可以简化为:

$$
y = \sum_{i=1}^N w_ix^i
$$

3. 与线性回归的关系

多项式回归可以理解为线性回归的扩展,在线性回归模型中添加了新的特征值.例如,要预测一栋房屋的价格,有x_1, x_2, x_3三个特征值,分别表示房子长、宽、高,则房屋价格可表示为以下线性模型:

$$
y = w_1 x_1 + w_2 x_2 + w_3 x_3 + b
$$

对于房屋价格,也可以用房屋的体积,而不直接使用x_1, x_2, x_3三个特征:

$$
y = w_0 + w_1x + w_2x^2 + w_3x ^ 3
$$

相当于创造了新的特征x, x = 长 * 宽 * 高. 以上两个模型可以解释为:

  • 房屋价格是关于长、宽、高三个特征的线性模型

  • 房屋价格是关于体积的多项式模型

因此,可以将一元n次多项式变换成n元一次线性模型.

4. 多项式回归实现

对于一元n次多项式,同样可以利用梯度下降对损失值最小化的方法,寻找最优的模型参数w_0, w_1, w_2, ..., w_n.可以将一元n次多项式,变换成n元一次多项式,求线性回归.以下是一个多项式回归的实现.

# 多项式回归示例
import numpy as np
# 线性模型
import sklearn.linear_model as lm
# 模型性能评价模块
import sklearn.metrics as sm
import matplotlib.pyplot as mp
# 管线模块
import sklearn.pipeline as pl
import sklearn.preprocessing as sp

train_x, train_y = [], []   # 输入、输出样本
with open("poly_sample.txt", "rt") as f:
    for line in f.readlines():
        data = [float(substr) for substr in line.split(",")]
        train_x.append(data[:-1])
        train_y.append(data[-1])

train_x = np.array(train_x)  # 二维数据形式的输入矩阵,一行一样本,一列一特征
train_y = np.array(train_y)  # 一维数组形式的输出序列,每个元素对应一个输入样本
# print(train_x)
# print(train_y)

# 将多项式特征扩展预处理,和一个线性回归器串联为一个管线
# 多项式特征扩展:对现有数据进行的一种转换,通过将数据映射到更高维度的空间中
# 进行多项式扩展后,我们就可以认为,模型由以前的直线变成了曲线
# 从而可以更灵活的去拟合数据
# pipeline连接两个模型
model = pl.make_pipeline(sp.PolynomialFeatures(3), # 多项式特征扩展,扩展最高次项为3,根据经验来
                         lm.LinearRegression())

# 用已知输入、输出数据集训练回归器
model.fit(train_x, train_y)
# print(model[1].coef_)
# print(model[1].intercept_)

# 根据训练模型预测输出
pred_train_y = model.predict(train_x)

# 评估指标
err4 = sm.r2_score(train_y, pred_train_y)  # R2得分, 范围[0, 1], 分值越大越好
print(err4)

# 在训练集之外构建测试集
test_x = np.linspace(train_x.min(), train_x.max(), 1000)
pre_test_y = model.predict(test_x.reshape(-1, 1)) # 对新样本进行预测

# 可视化回归曲线
mp.figure('Polynomial Regression', facecolor='lightgray')
mp.title('Polynomial Regression', fontsize=20)
mp.xlabel('x', fontsize=14)
mp.ylabel('y', fontsize=14)
mp.tick_params(labelsize=10)
mp.grid(linestyle=':')
mp.scatter(train_x, train_y, c='dodgerblue', alpha=0.8, s=60, label='Sample')

mp.plot(test_x, pre_test_y, c='orangered', label='Regression')

mp.legend()
mp.show()

打印输出:

0.9224401504764776

执行结果:

5. 过拟合与欠拟合

1)什么是欠拟合、过拟合

在上一小节多项式回归示例中,多项特征扩展器PolynomialFeatures()进行多项式扩展时,指定了最高次数为3,该参数为多项式扩展的重要参数,如果选取不当,则可能导致不同的拟合效果.下图显示了该参数分别设为1、20时模型的拟合图像:

这两种其实都不是好的模型. 前者没有学习到数据分布规律,模型拟合程度不够,预测准确度过低,这种现象称为“欠拟合”;后者过于拟合更多样本,以致模型泛化能力(新样本的适应性)变差,这种现象称为“过拟合”. 欠拟合模型一般表现为训练集、测试集下准确度都比较低;过拟合模型一般表现为训练集下准确度较高、测试集下准确度较低. 一个好的模型,不论是对于训练数据还是测试数据,都有接近的预测精度,而且精度不能太低.

【思考1】以下哪种模型较好,哪种模型较差,较差的原因是什么?

训练集R2值测试集R2值
0.60.5
0.90.6
0.90.88

【答案】第一个模型欠拟合(平时与测试都考不好);第二个模型过拟合(平时考好,测试考不好);第三个模型适中,为可接受的模型.

【思考2】以下哪个曲线为欠拟合、过拟合,哪个模型拟合最好?

【答案】第一个模型欠拟合;第三个模型过拟合;第二个模型拟合较好.

2)如何处理欠拟合、过拟合

  • 欠拟合:提高模型复杂度,如增加特征、增加模型最高次幂等等.(本身模型复杂度不够,特征太少)

  • 过拟合:降低模型复杂度,如减少特征、降低模型最高次幂等等. (本身模型过复杂,特征太多,如喜欢吃零食与薪资无关)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

dpq666dpq666

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值