线性回归是用来确定 2 种或 2 种以上变量间相互关系的一种统计分析方法。线性回归的结果是一个连续值,而不是离散值。
接下来,我们将从以下几个方面介绍下线性回归:
- 线性回归概述
- 损失函数
- 优化方法
- sklearn 线性回归 API 使用
1. 线性回归概述
在机器学习中,线性回归模型被用来解决回归问题。线性回归从数据集中,学习出不同特征的权重,从而绘制出一条直线来近似表示不同特征对于某个预测任务的重要程度(此处把直线换成超平面可能更合适一些)。
当我们拿到未知数据集时,只需要将该数据集不同特征乘以对应的特征的权重,就可以得出预测结果。
接下来,我们举个例子:
已知数据集 D = {( x1, y1), ( x2, y2) … ( xn, yn), ( x1, y1)},其中:
① xi 是一个 n 维的向量,即:xi = (xi1; xi2; … xin);
② yi 是一个任意的实数,表示要拟合的值;
我们以二维数据集为例,则数据集在二维平面内的分布如下图所示:
数据集 D 在坐标空间中,表现得是一堆散点。线性回归就是希望能够根据这些散点计算出一条近似直线,能够很好的反应出整个散点的变化趋势。如下图所示:
这条直线就叫做回归直线。需要注意的是,这条直线并不能准确反应出各个散点的 X 坐标和 Y 坐标之间的准确关系,而是一条近似直线。我们用下列公式表示这条回归直线:
① w = (w1; w2; w3 … wn)
② x = (x1; x2; x3; … xn)
假设: 特征权重 w、偏置 b 的值已经确定,我们就可以将样本 (x1, x2, x3, … xn) 代入公式,即可得到预测值。这就是线性回归模型的基本形式。
2. 损失函数
对于上述的线性回归公式,向量 x 表示样本为已知值,而 w、b 则是模型的参数,是未知的。我们的目标则是根据已知训练集去求解合适的 w、b 参数值。
对于 w、b 参数的组合有无数种,那么也就存在无数条直线能够拟合数据集。此时,就需要损失函数来评估那条直线是我们想要的。
损失函数,是用来衡量模型优劣的一个函数。从损失函数入手,即可得到最佳的 w、b 参数的值。
现在,我们要去定义一个损失函数。经过思考,我们发现模型预测的结果和真实结果之间差距越小,则模型拟合效果越好,所以定义线性回归模型的损失函数为:
① yi 表示真实结果② f(xi) 表示预测结果
我们计算所有样本的预测值和真实值的差距,取总差距最小的直线作为最终的直线(模型)。此时,最优的 w、b 的值我们就得到了。
我们把该损失函数叫做:平方损失函数。
3. 优化方法
通过最小化损失函数得到模型参数 w、b 的方法,可以叫做损失函数的优化方法,有时叫求解方法可能更容易理解一些。
我们使用 2 种优化方法来求解模型参数,分别是:正规方程、梯度下降法。
3.1 正规方程
正规方程通过一步到位的方式最小化损失函数,并求解出最优的模型参数 w、b。其求解的原理(公式推导)如下:
其中 ,X 表示样本集矩阵:
特征0 | 特征1 | 特征2 | 特征3 |
1 | x11 | x12 | x13 |
1 | x21 | x22 | x23 |
1 | x31 | x32 | x33 |
1 | x41 | x42 | x43 |
1 | x51 | x52 | x53 |
w 表示特征权重列向量:
b |
w1 |
w2 |
w3 |
y 表示真实值列向量:
y1 |
y2 |
y2 |
y4 |
y5 |
注意: 偏置 b 被包含在了 w 矩阵中的第一个位置。
【图-5】公式的理解如下:
接下来,将【图-5】公式展开如下:
【图-5】公式的理解如下:
接下来使用【图-7】公式对 w 求导,计算当 w 值为多少的时候,损失函数的值最小。得出结果如下:
计算过程如下:
由【图-9】公式推导得出:
至此,【图-11】公式即为正规方程根据已知样本计算出 w 的公式。
我们使用 Python 实现的最小二乘法代码为:
import matplotlib.pyplot as plt import numpy as np from sklearn.linear_model import LinearRegression # 最小二乘法 def linear_regression_custom(xs, ys): """ 最小二乘法求解系数 """ # 1. 矩阵转换 xs = np.mat([np.ones(len(xs)), xs]) ys = np.mat(ys) # 2. 最小二乘法计算参数[矩阵A行的列和矩阵B的行必须相等] a = (xs * xs.transpose()) # 2x2 b = (ys * xs.transpose()) # 1x2 params = a ** -1 * b.transpose() # [2x2] * [2x1] = 2*1 b, w = np.array(params)[0][0], np.array(params)[1][0] print(b, w) # 3. 创建绘制回归直线的数据集 x = np.linspace(-10, 10, 100) y = [w * v + b for v in x] return x, y def linear_regression_sklearn_API(xs, ys): """ sklearn 线性回归 """ linear_mode = LinearRegression() # 创建线性回归对象 linear_mode.fit(np.mat(xs).transpose(), ys) # 截距和权重系数 b, w = linear_mode.intercept_, linear_mode.coef_[0] # 截距 print(b, w) x = np.linspace(-10, 10, 100) y = [w * v + b for v in x] return x, y # 绘制散点图 def draw_regression_line(xs, ys, px, py): # 设置坐标轴 ax = plt.gca() ax.spines['right'].set_color('none') ax.spines['top'].set_color('none') # X 轴移动到中间位置,Y 轴移动到中间位置 ax.spines['left'].set_position(('data', 0)) ax.spines['bottom'].set_position(('data', 0)) # 设置坐标轴区间 plt.xlim((-8, 8)) plt.ylim((-8, 8)) # 绘制散点图 plt.scatter(xs, ys, marker="o", c="red") # 绘制回归直线 plt.plot(px, py) # 显示图形 plt.show() if __name__ == '__main__': # 1. 数据集 xs = np.array([-2, -1, 1, 1, 2, 0, 4, -4, -4, -3]) ys = np.array([-1, -2, 2, 4, 1, 2, 2, -3, -2, -3]) # 2. 最小二乘法 px, py = linear_regression_custom(xs, ys) # px, py = linear_regression_sklearn_API(xs, ys) # 3. 绘制回归直线 draw_regression_line(xs, ys, px, py)
代码运行结果:
3.2 梯度下降法
在求解损失函数的最小值时,可以通过梯度下降法来一步步的迭代求解,得到最小化的损失函数和模型参数值。反过来,如果我们需要求解损失函数的最大值,这时就需要用梯度上升法来迭代了。
下面为梯度简要推导过程:
① 对 w 求偏导:
② 对 b 求偏导:
接下来,我们使用梯度下降法求解回归直线模型的参数:
import matplotlib.pyplot as plt import numpy as np # 梯度下降法 def regresion_line_gradient_descent(xs, ys): w, b = 0.0, 0.0 # 模型参数 s = 0.001 # 迭代步长 n = 1000 # 迭代器次数 # 迭代求解模型参 c数 for _ in range(n): # 求当前位置的梯度 g_w, g_b = 0.0, 0.0 for i in range(len(xs)): g_w += (w * xs[i] + b - ys[i]) * xs[i] g_b += (w * xs[i] + b - ys[i]) # 使用梯度下降公式迭代求解 w = w - s * g_w b = b - s * g_w # 创建两个绘制直线的点 px = [-6, 6] py = [w * v + b for v in px] return px, py # 绘制散点图 def draw_regression_line(xs, ys, ps, py): # 设置坐标轴 ax = plt.gca() ax.spines['right'].set_color('none') ax.spines['top'].set_color('none') # X 轴移动到中间位置,Y 轴移动到中间位置 ax.spines['left'].set_position(('data', 0)) ax.spines['bottom'].set_position(('data', 0)) # 设置坐标轴区间 plt.xlim((-8, 8)) plt.ylim((-8, 8)) # 绘制散点图 plt.scatter(xs, ys, marker="o", c="red") # 绘制回归直线 plt.plot(ps, py) # 显示图形 plt.show() if __name__ == '__main__': # 1. 数据集 xs = [-2, -1, 1, 1, 2, 0, 4, -4, -4, -3] ys = [-1, -2, 2, 4, 1, 2, 2, -3, -2, -3] # 2. 梯度下降法 px, py = regresion_line_gradient_descent(xs, ys) # 3. 绘制回归直线 draw_regression_line(xs, ys, px, py)
该代码执行结果如下:
2.3 正规方程和梯度下降法对比
问题 | 正规方程(优缺点) | 梯度下降法(优缺点) |
---|---|---|
选择学习率 | 无学习率 | 需设置学习率参数 |
迭代次数 | 1次计算 | 多次迭代计算 |
计算量 | 特征数量非常大时,逆矩阵的时间复杂度 O( n3) | 更优 |
逆矩阵不不存在 | 无法使用 | 仍可使用 |
非线性拟合 | 无法使用 | 仍可使用 |
应用场景 | 小规模数据 | 大规模数据 |
至此,本篇文章完毕!