0 前言
经典的线性回归指的是一元线性回归,但显然机器学习中的线性回归不仅仅是一元线性回归,还包括多元线性回归,因此这里的经典线性回归指的是使用最小二乘法求解的多元线性回归。
1 多元线性回归的基本原理
多元线性回归的来源于以下这个方程
y
i
=
a
1
x
1
+
a
2
x
2
+
⋯
+
a
n
x
n
+
b
y
^
i
=
a
1
x
i
1
+
a
2
x
i
2
+
⋯
+
a
n
x
n
1
+
b
i
\begin{align*} y_i &= a_1 x_1 + a_2 x_2 +\cdots + a_n x_n + b\tag{1}\\ \hat{y}_i &= a_1 x_{i1} + a_2 x_{i2} +\cdots+a_n x_{n1} + b_i\tag{2} \end{align*}
yiy^i=a1x1+a2x2+⋯+anxn+b=a1xi1+a2xi2+⋯+anxn1+bi(1)(2)
式(1)表示了其代数关系,而(2)式表示了使用线性回归对
y
i
y_i
yi做出的估计
y
i
^
\hat{y_i}
yi^,称
a
⃗
\vec{a}
a为回归系数(coefficient),
b
⃗
\vec{b}
b为截距项(intercept),
x
i
⃗
\vec{x_i}
xi为特征项(自变量),
y
i
y_i
yi为标签项(因变量,目标),
y
i
^
\hat{y_i}
yi^为预测项(因变量的估计值)。用
x
i
1
,
x
i
2
,
⋯
,
x
i
n
x_{i1},x_{i2},\cdots,x_{in}
xi1,xi2,⋯,xin来表示一个样本的多个维度,那么具有多个样本时,可以表示为
y
⃗
=
X
⃗
a
⃗
+
b
⃗
y
^
⃗
=
X
⃗
a
⃗
+
b
⃗
\begin{align*} \vec{y} &= \vec{X}\vec{a} + \vec{b}\tag{3}\\ \vec{\hat{y}} &= \vec{X}\vec{a} + \vec{b}\tag{4} \end{align*}
yy^=Xa+b=Xa+b(3)(4)
显然,在面对一个回归任务时,我们希望预测值尽可能接近于真实值,即
y
^
⃗
\vec{\hat{y}}
y^尽可能接近于
y
⃗
\vec{y}
y,那么我们可以定义一个损失函数(loss function)来衡量预测值与真实值之间的差距,常见的损失函数有均方误差(mean squared error)和平均绝对误差(mean absolute error),分别表示为
L
M
S
E
=
1
n
∑
i
=
1
n
(
y
i
−
y
i
^
)
2
L
M
A
E
=
1
n
∑
i
=
1
n
∣
y
i
−
y
i
^
∣
\begin{align*} L_{MSE} &= \frac{1}{n}\sum_{i=1}^n(y_i-\hat{y_i})^2\tag{5}\\ L_{MAE} &= \frac{1}{n}\sum_{i=1}^n|y_i-\hat{y_i}|\tag{6} \end{align*}
LMSELMAE=n1i=1∑n(yi−yi^)2=n1i=1∑n∣yi−yi^∣(5)(6)
经典的线性回归模型就是希望找到一组回归系数
a
⃗
\vec{a}
a和截距项
b
⃗
\vec{b}
b,使得损失函数最小,即
a
⃗
∗
,
b
⃗
∗
=
arg
min
a
⃗
,
b
⃗
L
(
y
⃗
,
y
^
⃗
)
=
arg
min
a
⃗
,
b
⃗
L
(
y
⃗
,
X
⃗
a
⃗
+
b
⃗
)
\begin{align*} \vec{a}^*,\vec{b}^* &= \arg\min_{\vec{a},\vec{b}}L(\vec{y},\vec{\hat{y}})\tag{7}\\ &= \arg\min_{\vec{a},\vec{b}}L(\vec{y},\vec{X}\vec{a}+\vec{b})\tag{8} \end{align*}
a∗,b∗=arga,bminL(y,y^)=arga,bminL(y,Xa+b)(7)(8)
在线性回归中最小化误差项的方法是最小二乘法(least square method),即求误差项对参数项的偏导数
∂
∥
y
−
a
x
∥
2
2
∂
a
\frac{\partial{\Vert y-ax\Vert_2^2}}{\partial a}
∂a∂∥y−ax∥22为0的解,即
∂
∥
y
−
a
x
∥
2
2
∂
a
=
∂
(
y
−
a
x
)
⊤
(
y
−
a
x
)
∂
a
=
∂
(
y
⊤
−
x
⊤
a
⊤
)
(
y
−
a
x
)
∂
a
=
∂
(
y
⊤
y
−
y
⊤
a
x
−
x
⊤
a
⊤
y
+
x
⊤
a
⊤
a
x
)
∂
a
=
0
−
x
⊤
y
−
x
⊤
y
+
2
x
⊤
x
a
=
−
2
x
⊤
y
+
2
x
⊤
x
a
=
0
\begin{align*} \frac{\partial{\Vert y-ax\Vert_2^2}}{\partial a} &=\frac{\partial (y-ax)^\top (y-ax)}{\partial a}\\ &=\frac{\partial (y^\top - x^\top a^\top)(y-ax)}{\partial a}\\ &=\frac{\partial (y^\top y - y^\top ax - x^\top a^\top y + x^\top a^\top ax)}{\partial a}\\ &= 0 - x^\top y - x^\top y + 2x^\top x a\\ &= -2x^\top y + 2x^\top x a\tag{9}\\ &= 0 \end{align*}
∂a∂∥y−ax∥22=∂a∂(y−ax)⊤(y−ax)=∂a∂(y⊤−x⊤a⊤)(y−ax)=∂a∂(y⊤y−y⊤ax−x⊤a⊤y+x⊤a⊤ax)=0−x⊤y−x⊤y+2x⊤xa=−2x⊤y+2x⊤xa=0(9)
由式(9)为0可知,
a
=
(
x
⊤
x
)
−
1
x
⊤
y
a=(x^\top x)^{-1}x^\top y
a=(x⊤x)−1x⊤y,前提是
x
⊤
x
x^\top x
x⊤x可逆,即
x
x
x的各个维度之间线性无关,否则无法求解。那么,我们可以得到最小二乘法的解为
a
⃗
∗
=
(
X
⃗
⊤
X
⃗
)
−
1
X
⃗
⊤
y
⃗
b
⃗
∗
=
y
⃗
−
X
⃗
a
⃗
∗
\begin{align*} \vec{a}^* &= (\vec{X}^\top \vec{X})^{-1}\vec{X}^\top \vec{y}\tag{10}\\ \vec{b}^* &= \vec{y} - \vec{X}\vec{a}^*\tag{11} \end{align*}
a∗b∗=(X⊤X)−1X⊤y=y−Xa∗(10)(11)
对与线性回归中出现的多重共线性、前提假设等问题详见后续系列文章。
2 sklearn中的多元线性回归
sklearn中的经典线性回归模型是sklearn.linear_model.LinearRegression
,而在sklearn.linear_model
中还有其他的线性回归模型,如岭回归、Lasso回归、ElasticNet回归等,这些模型都是线性回归模型的变种,后续会详细介绍。这里我们先介绍sklearn.linear_model.LinearRegression
的使用方法。
2.1 线性回归器对象
sklearn.linear_model.LinearRegression
的主要参数如下:
fit_intercept
:是否计算截距项,默认为Truecopy_X
:是否复制X,默认为Truen_jobs
:int,计算时的CPU数量,默认为1positive
:是否强制回归系数为正,默认为False
属性包括:coef_
:回归系数intercept_
:截距项rank_
:X的秩singular_
:X的奇异值n_features_in_
:训练时的特征数量feature_names_in_
:训练时的特征名称
2.2 线性回归器的使用
先导入相关的包和所需数据:
from sklearn.metrics import mean_squared_error as mse
from toolkit import load_california_housing,build_optimization
import matplotlib as mpl
from matplotlib import pyplot as plt
mpl.rcParams['font.family'] = 'SimHei'
plt.rcParams['axes.unicode_minus'] = False
import sklearn
from sklearn.linear_model import LinearRegression as LR
x_reg_train,x_reg_test,y_reg_train,y_reg_test = load_california_housing()
之后建立模型,只使用默认参数:
reg = LR().fit(x_reg_train,y_reg_train)
y_reg_pred = reg.predict(x_reg_test)
print('sklearn的线性回归模型的MSE为:',mse(y_reg_test,y_reg_pred))
之后查看均方误差:
sklearn的线性回归模型的MSE为: 0.022488247605811043
还可以查看模型的各项属性:
# 查看模型各项属性
print(
'sklearn的线性回归模型的参数为:',reg.coef_,
'\n截距为:',reg.intercept_,
'\n模型拟合的数据矩阵列数(特征个数)为:',reg.n_features_in_,
'\n模型拟合的数据矩阵特征名称为:',reg.feature_names_in_,
'\n模型拟合的数据矩阵秩为:',reg.rank_,
'\n模型的奇异值为:',reg.singular_
)
得到结果:
sklearn的线性回归模型的参数为: [[ 1.29554539 0.09772236 -2.86904467 4.12599036 -0.05562903 -0.58601483 -0.81769524 -0.89876476]]
截距为: [0.74280783]
模型拟合的数据矩阵列数(特征个数)为: 8
模型拟合的数据矩阵特征名称为: ['MedInc' 'HouseAge' 'AveRooms' 'AveBedrms' 'Population' 'AveOccup' 'Latitude' 'Longitude']
模型拟合的数据矩阵秩为: 8
模型的奇异值为: [38.30039879 31.71719064 16.90965057 6.91233033 3.86834584 2.67444869 1.37551779 0.52616515]
2.3 线性回归器的评估
如何评估一个回归器的性能呢,在一般的回归算法模型中我们使用均方误差、绝对值误差等等,经典线性回归也不例外,但是在这两者之外,我们有其他的指标来描述回归器的性能,如下:
-
R
2
R^2
R2:决定系数,表示回归模型对总体观测值的拟合程度,取值范围为[0,1],越接近1表示拟合程度越好,越接近0表示拟合程度越差,计算公式为
R 2 = 1 − ∑ i = 1 n ( y i − y i ^ ) 2 ∑ i = 1 n ( y i − y ˉ ) 2 (12) R^2 = 1 - \frac{\sum_{i=1}^n(y_i-\hat{y_i})^2}{\sum_{i=1}^n(y_i-\bar{y})^2}\tag{12} R2=1−∑i=1n(yi−yˉ)2∑i=1n(yi−yi^)2(12)
其中 y ˉ \bar{y} yˉ表示 y y y的均值, y i ^ \hat{y_i} yi^表示第 i i i个样本的预测值, y i y_i yi表示第 i i i个样本的真实值。 - 调整后的
R
2
R^2
R2:调整后的决定系数,表示回归模型对总体观测值的拟合程度,取值范围为[0,1],越接近1表示拟合程度越好,越接近0表示拟合程度越差,计算公式为
R a d j 2 = 1 − ( 1 − R 2 ) ( n − 1 ) n − p − 1 (13) R^2_{adj} = 1 - \frac{(1-R^2)(n-1)}{n-p-1}\tag{13} Radj2=1−n−p−1(1−R2)(n−1)(13)
其中 n n n表示样本数量, p p p表示特征数量。
那么既然拥有了均方误差,我们为什么需要 R 2 R^2 R2呢?因为均方误差是一个绝对值,无法直观的表示模型的拟合程度,而 R 2 R^2 R2是一个相对值,可以直观的表示模型的拟合程度,因此 R 2 R^2 R2更加直观。 R 2 R^2 R2的含义是,模型对总体观测值的拟合程度,即模型对总体观测值的解释程度,因此 R 2 R^2 R2也被称为可决系数(coefficient of determination)。
在均方误差之外,可以查看 R 2 R^2 R2和调整后的 R 2 R^2 R2:
from sklearn.metrics import r2_score
print('sklearn的线性回归模型的R2为:',r2_score(y_reg_test,y_reg_pred))
# 手动计算调整后的R2
n = x_reg_test.shape[0]
p = x_reg_test.shape[1]
r2 = r2_score(y_reg_test,y_reg_pred)
r2_adj = 1-(1-r2)*(n-1)/(n-p-1)
print('sklearn的线性回归模型的调整后R2为:',r2_adj)
得到运行结果:
sklearn的线性回归模型的R2为: 0.5943232652466203
sklearn的线性回归模型的调整后R2为: 0.5935353521905322
通过绘制顺序图像来观察模型是否正确拟合了数据的分布,两条线重叠的部分越多,说明模型拟合的越好,如下图所示:
plt.plot(range(len(y_reg_test)),sorted(y_reg_test.values),c="black",label= "Data")
plt.plot(range(len(y_reg_pred)),sorted(y_reg_pred),c="red",label = "Predict")
plt.legend()
plt.show()
在这张图里可以看到,虽然预测值与真实值有很大的重复部分,但是在起始端与结束端还是有相当大量的值不符,这意味着如果出现数据的分布偏态,那么我们的预测值其实相当不靠谱,而 R 2 R^2 R2的值也印证了这一点, R 2 R^2 R2不到0.6,这只能解释部分数据的潜在规律,距离对算法模型的预期还有相当大的差距。
后续文章中会阐述经典线性回归算法模型的问题以及相应的改善措施,敬请期待。