根据菜菜的课程进行整理,方便记忆理解
代码位置如下:
多元线性回归LinearRegression
多元线性回归的基本原理
线性回归是机器学习中最简单的回归算法,多元线性回归指的就是一个样本有多个特征的线性回归问题。对于一个有n个特征的样本而言,它的回归结果可以写作一个几乎人人熟悉的方程:
被统称为模型的参数,其中被称为截距(intercept), ~ 被称为回归系数(regression coefficient),有时也是使用或者来表示。这个表达式,其实就和我们小学时就无比熟悉的是同样的性质。其中y是我们的目标变量,也就是标签。~ 是样本上的特征不同特征。如果考虑我们有m个样本,则回归结果可以被写作:
其中是包含了m个全部的样本的回归结果的列向量。注意,我们通常使用粗体的小写字母来表示列向量,粗体的大写字母表示矩阵或者行列式。我们可以使用矩阵来表示这个方程,其中可以被看做是一个结构为(n+1,1)的列矩阵,是一个结构为(m,n+1)的特征矩阵,则有:
线性回归的任务,就是构造一个预测函数来映射输入的特征矩阵和标签值的线性关系,这个预测函数在不同的教材上写法不同,可能写作, ,或者等等形式,但无论如何,这个预测函数的本质就是我们需要构建的模型,而构造预测函数的核心就是找出模型的参数向量。但我们怎样才能够求解出参数向量呢?
记得在逻辑回归和SVM中,我们都是先定义了损失函数,然后通过最小化损失函数或损失函数的某种变化来将求解参数向量,以此将单纯的求解问题转化为一个最优化问题。在多元线性回归中,我们的损失函数如下定义:
其中是样本i对应的真实标签, ,也就是是样本i在一组参数下的预测标签。
首先,这个损失函数代表了向量的L2范式的平方结果,L2范式的本质是就是欧式距离,即是两个向量上的每个点对应相减后的平方和再开平方,我们现在只实现了向量上每个点对应相减后的平方和,并没有开方,所以我们的损失函数是L2范式,即欧式距离的平方结果。
在这个平方结果下,我们的和分别是我们的真实标签和预测值,也就是说,这个损失函数实在计算我们的真实标签和预测值之间的距离。因此,我们认为这个损失函数衡量了我们构造的模型的预测结果和真实标签的差异,因此我们固然希望我们的预测结果和真实值差异越小越好。所以我们的求解目标就可以转化成:
其中右下角的2表示向量的L2范式,也就是我们的损失函数所代表的含义。在L2范式上开平方,就是我们的损失函数。这个式子,也正是sklearn当中,用在类Linear_model.LinerRegression背后使用的损失函数。我们往往称呼这个式子为SSE(Sum of Sqaured Error,误差平方和)或者RSS(Residual Sum of Squares 残差平方和)。在sklearn所有官方文档和网页上,我们都称之为RSS残差平方和
最小二乘法求解多元线性回归的参数
现在问题转换成了求解让RSS最小化的参数向量,这种通过最小化真实值和预测值之间的RSS来求解参数的方法叫做最小二乘法。求解极值的第一步往往是求解一阶导数并让一阶导数等于0,最小二乘法也不能免俗。因此,我们现在残差平方和RSS上对参数向量求导。这里的过程涉及到少数矩阵求导的内容,需要查表来确定,感兴趣可以走维基百科去查看矩阵求导的详细公式的表格:
接下来,我们就来对求导:
我们让求导后的一阶导数为0:
到了这里,我们希望能够将留在等式的左边,其他与特征矩阵有关的部分都放到等式的右边,如此就可以求出的最优解了。这个功能非常容易实现,只需要我们左乘的逆矩阵就可以。在这里,逆矩阵存在的充分必要条件是特征矩阵不存在多重共线性。我们将会在第四节详细讲解多重共线性这个主题。
假设矩阵的逆是存在的,此时我们的就是我们参数的最优解。求解出这个参数向量,我们就解出了我们的,也就能够计算出我们的预测值了。对于多元线性回归的理解,到这里就足够了,如果大家还希望继续深入,那我可以给大家一个方向:你们知道矩阵其实是使用奇异值分解来进行求解的吗?可以仔细去研究一下。以算法工程师和数据挖掘工程师为目标的大家,能够手动推导上面的求解过程是基本要求。
除了多元线性回归的推导之外,这里还需要提到一些在上面的推导过程中不曾被体现出来的问题。在统计学中,使用最小二乘法来求解线性回归的方法是一种”无偏估计“的方法,这种无偏估计要求因变量,也就是标签的分布必须服从正态分布。这是说,我们的y必须经由正太化处理(比如说取对数,或者使用在第三章《数据预处理与特征工程》中提到的类QuantileTransformer或者PowerTransformer)。在机器学习中,我们会先考虑模型的效果,如果模型效果不好,那我们可能考虑改变因变量的分布。
linear_model.LinearRegression
class sklearn.linear_model.LinearRegression (fit_intercept=True, normalize=False, copy_X=True, n_jobs=None)
| 参数 | 含义 |
|---|---|
| fit_intercept | 布尔值,可不填,默认为True 是否计算此模型的截距。如果设置为False,则不会计算截距 |
| normalize | 布尔值,可不填,默认为False 当fit_intercept设置为False时,将忽略此参数。如果为True,则特征矩阵X在进入回归之前将 会被减去均值(中心化)并除以L2范式(缩放)。如果你希望进行标准化,请在fit数据之前 使用preprocessing模块中的标准化专用类StandardScaler |
| copy_X | 布尔值,可不填,默认为True 如果为真,将在X.copy()上进行操作,否则的话原本的特征矩阵X可能被线性回归影响并覆盖 |
| n_jobs | 整数或者None,可不填,默认为None 用于计算的作业数。只在多标签的回归和数据量足够大的时候才生效。除非None在 joblib.parallel_backend上下文中,否则None统一表示为1。如果输入 -1,则表示使用全部 的CPU来进行计算。 |
线性回归的类可能是我们目前为止学到的最简单的类,仅有四个参数就可以完成一个完整的算法。并且看得出,这些参数中并没有一个是必填的,更没有对我们的模型有不可替代作用的参数。这说明,线性回归的性能,往往取决于数据本身,而并非是我们的调参能力,线性回归也因此对数据有着很高的要求。幸运的是,现实中大部分连续型变量之间,都存在着或多或少的线性联系。所以线性回归虽然简单,却很强大。
顺便一提,sklearn中的线性回归可以处理多标签问题,只需要在fit的时候输入多维度标签就可以了。
- 导入需要的模块和库
from sklearn.linear_model import LinearRegression as LR
from sklearn.model_selection import train_test_split
from sklearn.model_selection import cross_val_score
from sklearn.datasets import fetch_california_housing as fch #加利福尼亚房屋价值数据集
import pandas as pd
- 导入数据,探索数据
housevalue = fch() #会需要下载,大家可以提前运行试试看
housevalue.data
"""
array([[ 8.3252 , 41. , 6.98412698, ..., 2.55555556,
37.88 , -122.23 ],
[ 8.3014 , 21. , 6.23813708, ..., 2.10984183,
37.86 , -122.22 ],
[ 7.2574 , 52. , 8.28813559, ..., 2.80225989,
37.85 , -122.24 ],
...,
[ 1.7 , 17. , 5.20554273, ..., 2.3256351 ,
39.43 , -121.22 ],
[ 1.8672 , 18. , 5.32951289, ..., 2.12320917,
39.43 , -121.32 ],
[ 2.3886 , 16. , 5.25471698, ..., 2.61698113,
39.37 , -121.24 ]])
"""
X = pd.DataFrame(housevalue.data) #放入DataFrame中便于查看
X.shape
# (20640, 8)
# 并不是房价本身,而是对房价的打分
y = housevalue.target
y
# array([4.526, 3.585, 3.521, ..., 0.923, 0.847, 0.894])
y.min()
# 0.14999
y.max()
# 5.00001
y.shape
# (20640,)
housevalue.feature_names #特征名字
"""
['MedInc',
'HouseAge',
'AveRooms',
'AveBedrms',
'Population',
'AveOccup',
'Latitude',
'Longitude']
"""
"""
MedInc:该街区住户的收入中位数
HouseAge:该街区房屋使用年代的中位数
AveRooms:该街区平均的房间数目
AveBedrms:该街区平均的卧室数目
Population:街区人口
AveOccup:平均入住率
Latitude:街区的纬度
Longitude:街区的经度
"""
- 分训练集和测试集
Xtrain, Xtest, Ytrain, Ytest = train_test_split(X,y,test_size=0.3,random_state=420)
Xtest.head()
Xtrain.head()
#恢复索引
for i in [Xtrain, Xtest]:
i.index = range(i.shape[0])
Xtrain.shape
### (14448, 8)
- 建模
reg = LR().fit(Xtrain, Ytrain)
yhat = reg.predict(Xtest) #预测我们的yhat
yhat.min()
# -0.6528439725035824
yhat.max()
# 7.1461982142708464
- 探索建好的模型
reg.coef_ #w,系数向量
"""
array([ 4.37358931e-01, 1.02112683e-02, -1.07807216e-01, 6.26433828e-01,
5.21612535e-07, -3.34850965e-03, -4.13095938e-01, -4.26210954e-01])
"""
Xtrain.columns
"""
Index(['MedInc', 'HouseAge', 'AveRooms', 'AveBedrms', 'Population', 'AveOccup',
'Latitude', 'Longitude'],
dtype='object')
"""
[*zip(Xtrain.columns,reg.coef_)]
"""
[('MedInc', 0.4373589305968394),
('HouseAge', 0.010211268294493866),
('AveRooms', -0.10780721617317587),
('AveBedrms', 0.6264338275363752),
('Population', 5.216125353313394e-07),
('AveOccup', -0.003348509646333729),
('Latitude', -0.4130959378947716),
('Longitude', -0.4262109536208476)]
"""
reg.intercept_
# -36.25689322920396
| 属性 | 含义 |
|---|---|
| coef_ | 数组,形状为 (n_features, )或者(n_targets, n_features) 线性回归方程中估计出的系数。如果在fit中传递多个标签(当y为二维或以上的时候),则返回 的系数是形状为(n_targets,n_features)的二维数组,而如果仅传递一个标签,则返回的系 数是长度为n_features的一维数组 |
| intercept_ | 数组,线性回归中的截距项 |
建模的过程在sklearn当中其实非常简单,但模型的效果如何呢?接下来我们来看看多元线性回归的模型评估指标