菜菜的scikit-learn课堂——sklearn中的线性回归大家族
图片Github项目-100-Days-Of-ML-Code
1 概述
1.1 线性回归
回归是一种应用广泛的预测建模技术,这种技术的核心在于预测的结果是连续型变量。
通常来说,我们认为统计学注重先验,而机器学习看重结果(后验),因此机器学习中不会提前为线性回归排除共线性等可能会影响模型的因素,反而会先建立模型以查看效果。模型确立之后,如果效果不好,我们就根据统计学的指导来排除可能影响模型的因素。而统计学角度则在建模前,必须满足一系列条件才能建模。
回归需求在现实中非常多,所以我们自然也有各种各样的回归类算法。
- 线性回归
- 逻辑回归
- 岭回归
- Lasso
- 弹性网
除此之外,还有众多分类算法改进后的回归,比如
- 回归树
- 随机森林的回归
- 支持向量回归
- 贝叶斯回归等
除此之外,我们还有各种鲁棒的回归:比如RANSAC,Theil-Sen估计,胡贝尔回归等等。
1.2 sklearn中的线性回归
2 多元线性回归LinearRegression
2.1 多元线性回归的基本原理
这里与 线性模型 一样
2.2 最小二乘法求解多元线性回归的参数
最小二乘估计的推导过程 略
在这里,逆矩阵存在的充分必要条件是特征矩阵不存在多重共线性。
在统计学中,使用最小二乘法来求解线性回归的方法是一种”无偏估计“的方法,这种无偏估计要求因变量,也就是标签的分布必须服从正态分布。
在机器学习中,我们会先考虑模型的效果,如果模型效果不好,那我们可能考虑改变因变量的分布。
2.3 linear_model.LinearRegression
sklearn.linear_model.LinearRegression (
fit_intercept=True,
normalize=False,
copy_X=True,
n_jobs=None)
线性回归的性能,往往取决于数据本身,而并非是我们的调参能力,线性回归也因此对数据有着很高的要求。
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() #会需要下载,大家可以提前运行试试看
X = pd.DataFrame(housevalue.data) # 特征矩阵
y = housevalue.target
X.shape # (20640, 8)
y.shape # (20640,)
housevalue.feature_names
X.columns = housevalue.feature_names # 设置列名
"""
MedInc:该街区住户的收入中位数
HouseAge:该街区房屋使用年代的中位数
AveRooms:该街区平均的房间数目
AveBedrms:该街区平均的卧室数目
Population:街区人口
AveOccup:平均入住率
Latitude:街区的纬度
Longitude:街区的经度
"""
# 分训练集和测试集
Xtrain, Xtest, Ytrain, Ytest = train_test_split(X,y,test_size=0.3,random_state=420)
# 重置 训练特征集和测试特征集 索引
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
'''
array([1.51384887, 0.46566247, 2.2567733 , ..., 2.11885803, 1.76968187,0.73219077])
'''
# 探索建好的模型
reg.coef_ # 系数向量
'''
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])
'''
[*zip(Xtrain.columns,reg.coef_)]
'''
[('MedInc', 0.4373589305968401),
('HouseAge', 0.010211268294493672),
('AveRooms', -0.10780721617317635),
('AveBedrms', 0.6264338275363786),
('Population', 5.21612535341231e-07),
('AveOccup', -0.0033485096463336797),
('Latitude', -0.413095937894771),
('Longitude', -0.42621095362084827)]
'''
reg.intercept_ # 截距
# -36.256893229204074
使用 Python统计建模分析工具包statsmodels 进行比较
在前面的基础上
import statsmodels.api as sm
results = sm.OLS(Ytrain, Xtrain).fit()
print(results.summary())
OLS Regression Results
=======================================================================================
Dep. Variable: y R-squared (uncentered): 0.893
Model: OLS Adj. R-squared (uncentered): 0.893
Method: Least Squares F-statistic: 1.500e+04
Date: Thu, 02 Jul 2020 Prob (F-statistic): 0.00
Time: 21:40:41 Log-Likelihood: -16805.
No. Observations: 14448 AIC: 3.363e+04
Df Residuals: 14440 BIC: 3.369e+04
Df Model: 8
Covariance Type: nonrobust
==============================================================================
coef std err t P>|t| [0.025 0.975]
------------------------------------------------------------------------------
MedInc 0.5110 0.005 101.115 0.000 0.501 0.521
HouseAge 0.0165 0.001 29.789 0.000 0.015 0.018
AveRooms -0.1801 0.007 -24.588 0.000 -0.194 -0.166
AveBedrms 0.8416 0.035 23.971 0.000 0.773 0.910
Population 1.37e-05 6.11e-06 2.243 0.025 1.73e-06 2.57e-05
AveOccup -0.0042 0.001 -7.858 0.000 -0.005 -0.003
Latitude -0.0626 0.004 -14.636 0.000 -0.071 -0.054
Longitude -0.0158 0.001 -11.657 0.000 -0.018 -0.013
==============================================================================
Omnibus: 3132.950 Durbin-Watson: 2.016
Prob(Omnibus): 0.000 Jarque-Bera (JB): 10273.893
Skew: 1.093 Prob(JB): 0.00
Kurtosis: 6.505 Cond. No. 1.02e+04
==============================================================================
Warnings:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
[2] The condition number is large, 1.02e+04. This might indicate that there are
strong multicollinearity or other numerical problems.
两者得出的系数还是有所不同
3 回归类的模型评估指标
在回归类算法中,有两种不同的角度来看待回归的效果:
- 第一,是否预测到了正确的数值。
- 第二,是否拟合到了足够的信息。
这两种角度,分别对应着不同的模型评估指标。
3.1 是否预测了正确的数值
sklearn中使用均方误差MSE(mean squared error)来衡量我们的预测值和真实值的差异:
M S E = 1 m ∑ i = 1 m ( y i − y ^ i ) 2 MSE=\frac{1}{m}\sum_{i=1}^m{\left( y_i-\hat{y}_i \right) ^2} MSE=m1i=1∑m(yi−y^i)2
均方误差,本质是在RSS的基础上除以了样本总量,得到了每个样本量上的平均误差。有了平均误差,就可以将平均误差和我们的标签的取值范围在一起比较,以此获得一个较为可靠的评估依据。
在sklearn当中,我们有两种方式调用这个评估指标,
- —种是使用sklearn专用的模型评估模块 metrics 里的类 mean.squared.error,
- 另-种是调用交叉验证的类cross__val._score并使用里面的scoring参数来设置使用均方误差。
第一种方法
使用sklearn专用的模型评估模块 metrics 里的类 mean.squared.error
from sklearn.metrics import mean_squared_error as MSE
MSE(yhat,Ytest) # 0.5309012639324571
y.mean() # 2.068558169089147
平均误差大约为0.53。而 y 的均值才2.07左右,所以误差太大
第二种方法
调用交叉验证的类cross__val._score并使用里面的scoring参数来设置使用均方误差。
在sklearn中的参数scoring下,均方误差作为评判标准时,是计算”负均方误差“(neg_mean_squared_error)。
这是因为sklearn在计算模型评估指标的时候,会考虑指标本身的性质,均方误差本身是一种误差,所以被sklearn划分为模型的一种损失(loss)。
在sklearn当中,所有的损失都使用负数表示,因此均方误差也被显示为负数了。真正的均方误差MSE的数值,其实就是neg_mean_squared_error去掉负号的数字。
除了MSE,还有与MSE类似的MAE(Mean absolute error,绝对均值误差):
M
A
E
=
1
m
∑
i
=
1
m
∣
y
i
−
y
^
i
∣
MAE=\frac{1}{m}\sum_{i=1}^m{\left| y_i-\hat{y}_i \right|}
MAE=m1i=1∑m∣yi−y^i∣
其表达的概念与均方误差完全一致,不过在真实标签和预测值之间的差异外我们使用的是L1范式(绝对值)。
现实使用中,MSE和MAE选一个来使用就好了。在sklearn当中,使用命令
from sklearn.metrics import mean_absolute_error
来调用MAE,同时,也可以使用交叉验证中的scoring = "neg_mean_absolute_error"
,以此在交叉验证时调用MAE。
cross_val_score(reg,X,y,cv=10,scoring="neg_mean_squared_error")
'''
array([-0.48922052, -0.43335865, -0.8864377 , -0.39091641, -0.7479731 ,
-0.52980278, -0.28798456, -0.77326441, -0.64305557, -0.3275106 ])
'''
cross_val_score(reg,X,y,cv=10,scoring="neg_mean_squared_error").mean()
# -0.550952429695659
3.2 是否拟合了足够的信息
在多元线性回归模型中,也可以用可决系数
R
2
R^2
R2来衡量样本回归线对样本观测值的拟合优度。根据多元线性和回归模型可以将总离差平方和TSS分解为回归平方和ESS以及残差平方和RSS两个部分,即
∑
(
y
i
−
y
ˉ
)
2
=
∑
(
y
^
i
−
y
ˉ
)
2
+
∑
(
y
i
−
y
^
i
)
2
\sum{\left( y_i-\bar{y} \right) ^2}=\sum{\left( \hat{y}_i-\bar{y} \right)}^2+\sum{\left( y_i-\hat{y}_i \right) ^2}
∑(yi−yˉ)2=∑(y^i−yˉ)2+∑(yi−y^i)2
T
S
S
=
E
S
S
+
R
S
S
TSS=ESS+RSS
TSS=ESS+RSS
因此在多元线性回归中,定义可决系数为:
R
2
=
E
S
S
T
S
S
=
1
−
R
S
S
S
S
T
R^2=\frac{ESS}{TSS}=1-\frac{RSS}{SST}
R2=TSSESS=1−SSTRSS
用 R 2 R^2 R2 表示捕获到的信息量占真实标签中所带的信息量的比例
R 2 R^2 R2 作为检验回归方程与样本值拟合优度的指标, R 2 ( 0 ≤ R 2 ≤ 1 ) R^2\left( 0\le R^2\le 1 \right) R2(0≤R2≤1)越大,表示回归方程与样本值拟合的越好;反之,回归方程与样本值拟合的较差。
在现实应用过程中,如果在模型中增加一个解释变量,
R
2
R^2
R2往往增大,而由增加解释变量个数引起的
R
2
R^2
R2的增大与拟合好坏无关,因此在多元回归模型之间比较拟合优度,R^2就不是一个合适的指标,必须加以调整。将残差平方和与总离差平方和分别除以各自的自由度,以剔除变量个数对拟合优度的影响。记为调整的可决系数,则有
R
ˉ
2
=
1
−
S
S
R
/
(
n
−
p
−
1
)
S
S
T
/
(
n
−
1
)
\bar{R}^2=1-\frac{SSR/\left( n-p-1 \right)}{SST/\left( n-1 \right)}
Rˉ2=1−SST/(n−1)SSR/(n−p−1)
R 2 R^2 R2 可以使用三种方式来调用,
- 第一种是直接从metrics中导入r2_score,输入预测值和真实值后打分。
- 第二种是直接从线性回归LinearRegression的接口score来进行调用。
- 第三种是在交叉验证中,输入"r2"来调用。
#调用R2
# 第一种方法
from sklearn.metrics import r2_score
r2_score(yhat,Ytest)
# 0.3380653761555984
# 第二种方法
r2 = reg.score(Xtest,Ytest)
r2
# 0.6043668160178817
两种方法, R 2 R^2 R2 却不一样,相同的评估指标不同的结果。
这是因为,在调用metrcis模块中的模型评估指标的时候,必须要检查清楚,指标的参数中,究竟是要求先输入真实值还是先输入预测值。
#调用R2
# 第一种方法
from sklearn.metrics import r2_score
r2_score(yhat,Ytest)
# 0.3380653761555984
#使用shift tab键来检查究竟哪个值先进行输入
r2_score(Ytest,yhat)
# 0.6043668160178817
#或者你也可以指定参数,就不必在意顺序了
r2_score(y_true = Ytest,y_pred = yhat)
# 0.6043668160178817
绘图来看看拟合情况
没有正确的拟合模型分布
虽然大部分数据被拟合得比较好,但是图像的开头和结尾处却又着较大的拟合误差。如果在图像右侧分布着更多的数据,模型就会越来越偏离我们真正的标签。
负的 R 2 R^2 R2
公式 TSS = RSS + ESS 不是永远成立的!
∣
T
S
S
=
∑
(
y
i
−
y
ˉ
)
2
=
∑
(
y
i
−
y
^
i
+
y
^
i
−
y
ˉ
)
2
=
展开
R
S
S
+
E
S
S
+
2
∑
(
y
i
−
y
^
i
)
(
y
^
i
−
y
ˉ
)
\left| \begin{array}{l} TSS=\sum_{}^{}{\left( y_i-\bar{y} \right) ^2}=\sum_{}^{}{\left( y_i-\hat{y}_i+\hat{y}_i-\bar{y} \right) ^2}\\ \\ \xlongequal{\text{展开}}RSS+ESS+2\sum_{}^{}{\left( y_i-\hat{y}_i \right) \left( \hat{y}_i-\bar{y} \right)}\\ \end{array} \right.
∣∣∣∣∣∣TSS=∑(yi−yˉ)2=∑(yi−y^i+y^i−yˉ)2展开RSS+ESS+2∑(yi−y^i)(y^i−yˉ)
两 边 同 时 除 以 TSS , 则 有
∣
1
−
R
S
S
T
S
S
=
E
S
S
+
2
∑
(
y
i
−
y
^
i
)
(
y
^
i
−
y
ˉ
)
T
S
S
R
2
=
E
S
S
+
2
∑
(
y
i
−
y
^
i
)
(
y
^
i
−
y
ˉ
)
T
S
S
\left| \begin{array}{l} 1-\frac{RSS}{TSS}=\frac{ESS+2\sum_{}^{}{\left( y_i-\hat{y}_i \right) \left( \hat{y}_i-\bar{y} \right)}}{TSS}\\ \\ R^2=\frac{ESS+2\sum_{}^{}{\left( y_i-\hat{y}_i \right) \left( \hat{y}_i-\bar{y} \right)}}{TSS}\\ \end{array} \right.
∣∣∣∣∣∣∣1−TSSRSS=TSSESS+2∑(yi−y^i)(y^i−yˉ)R2=TSSESS+2∑(yi−y^i)(y^i−yˉ)
当一定的条件成立时,满足
2
∑
(
y
i
−
y
^
i
)
(
y
^
i
−
y
ˉ
)
=
0
2\sum_{}^{}{\left( y_i-\hat{y}_i \right) \left( \hat{y}_i-\bar{y} \right)}=0
2∑(yi−y^i)(y^i−yˉ)=0
才有
T
S
S
=
E
S
S
+
R
S
S
TSS=ESS+RSS
TSS=ESS+RSS
说明, R 2 R^2 R2 有机会是一个负数,一个负的 R 2 R^2 R2 是合理的。
也就是说,当我们的 R 2 R^2 R2 显示为负的时候,这证明模型对数据的拟合非常糟糕,模型完全不能使用。
4 多重共线性:岭回归与Lasso
4.1 多重共线性
多重共线性是指线性回归模型中的解释变量之间由于存在精确相关关系或高度相关关系而使模型估计失真或难以估计准确。
即一个解释变量的变化引起另一个解释变量地变化。
多元线性回归模型的基本假设之一是解释变量是互相独立的,若某两个或多个解释变量之间有相关性,就会出现多重共线性。
如果存在
c
1
X
1
i
+
c
2
X
2
i
+
⋯
+
c
k
X
k
i
=
0
,
i
=
1
,
2
,
⋯
,
n
c_1X_{1i}+c_2X_{2i}+\cdots +c_kX_{ki}=0\text{,}i=1,2,\cdots ,n
c1X1i+c2X2i+⋯+ckXki=0,i=1,2,⋯,n
其中 c i c_i ci 不全为0,则称为解释变量间存在完全共线性
如果存在
c
1
X
1
i
+
c
2
X
2
i
+
⋯
+
c
k
X
k
i
+
v
i
=
0
,
i
=
1
,
2
,
⋯
,
n
c_1X_{1i}+c_2X_{2i}+\cdots +c_kX_{ki}+v_i=0\text{,}i=1,2,\cdots ,n
c1X1i+c2X2i+⋯+ckXki+vi=0,i=1,2,⋯,n
其中 c i c_i ci 不全为0, v i v_i vi 为随机误差项,则称为近似共线性
产生的原因主要有3个方面:
(1)经济变量相关的共同趋势
(2)经济变量间存在较密切的关系
(3)采用滞后变量作为解释变量
(3)样本资料的限制
多重共线性产生的后果:
(1)多重共线性使参数估计值的方差增大
(2)参数估计量经济含义不合理
(3)变量的显著性检验失去意义,可能将重要的解释变量排除在模型之外
(4)模型的预测功能失效。变大的方差容易使区间预测的“区间”变大,使预测失去意义。
多重共线性处理方法:
(1)手动移除出共线性的变量
(2)逐步回归法
(3)增加样本容量
(4)改进线性回归
4.2 岭回归
4.2.1 岭回归解决多重共线性问题
如果实际研究中并不想剔除掉某些自变量,某些自变量很重要,不能剔除。此时可能只有岭回归最为适合了。岭回归是当前解决共线性问题最有效的解释办法。
岭回归在多元线性回归的损失函数上加上了正则项,表达为系数 β \beta β 的L2范式(即系数 β \beta β 的平方项)乘以正则化系数。
岭回归的损失函数的完整表达式写作:
min
β
∥
X
β
−
y
∥
2
2
+
α
∥
β
∥
2
2
\underset{\beta}{\min}\lVert \boldsymbol{X\beta }-\boldsymbol{y} \rVert _2^2+\boldsymbol{\alpha }\lVert \boldsymbol{\beta } \rVert _2^2
βmin∥Xβ−y∥22+α∥β∥22
在线性回归中通过在损失函数上对
β
\beta
β 求导来求解极值,在这里虽然加上了正则项,依然使用最小二乘法来求解。
整理
对于线性回归模型,回归系数
β
\beta
β 的岭估计定义为:
β
^
(
α
)
=
(
X
′
X
+
α
I
)
−
1
X
′
y
\boldsymbol{\hat{\beta}}\left( \alpha \right) =\left( \boldsymbol{X'X}+\alpha \boldsymbol{I} \right) ^{-1}\boldsymbol{X'y}
β^(α)=(X′X+αI)−1X′y
这里
α
>
0
\alpha >0
α>0 是可选择参数,称为岭参数或偏参数。当
α
\alpha
α 取不同的值时,可以得到不同的估计,因此岭估计
β
^
(
α
)
\boldsymbol{\hat{\beta}}\left( \alpha \right)
β^(α) 是一个估计类,当
α
=
0
\alpha =0
α=0 时,
β
^
(
α
)
=
(
X
′
X
)
−
1
X
′
y
\boldsymbol{\hat{\beta}}\left( \alpha \right) =\left( \boldsymbol{X'X} \right) ^{-1}\boldsymbol{X'y}
β^(α)=(X′X)−1X′y
就是通常的最小二乘估计
于是,最小二乘估计是岭估计类中的一个估计,但是一般情况下,提及的岭估计总是不包括最小二乘估计
因为对一切 α ≠ 0 \alpha \ne 0 α=0 和 β ≠ 0 \boldsymbol{\beta }\ne 0 β=0
E
β
^
(
α
)
=
(
X
′
X
+
α
I
)
−
1
X
′
E
y
=
(
X
′
X
+
α
I
)
−
1
X
′
X
β
≠
β
E\boldsymbol{\hat{\beta}}\left( \alpha \right) =\left( \boldsymbol{X'X}+\alpha \boldsymbol{I} \right) ^{-1}\boldsymbol{X'}E\boldsymbol{y}=\left( \boldsymbol{X'X}+\alpha \boldsymbol{I} \right) ^{-1}\boldsymbol{X'X\beta }\ne \beta
Eβ^(α)=(X′X+αI)−1X′Ey=(X′X+αI)−1X′Xβ=β
因此,岭估计是有偏估计,这与最小二乘估计不同(无偏估计)
对于一个估计的均方误差由方差和偏差的平方和两部分组成
即
M
S
E
(
θ
^
)
=
D
(
θ
^
)
+
[
E
(
θ
^
)
−
θ
]
2
MSE\left( \hat{\theta} \right) =D\left( \hat{\theta} \right) +\left[ E\left( \hat{\theta} \right) -\theta \right] ^2
MSE(θ^)=D(θ^)+[E(θ^)−θ]2
当存在多重共线性时,最小二乘估计虽然仍保持偏差部分为0,但是它的方差部分却很大,最终使它的均方误差很大。
因此,引入岭回归是以牺牲无偏性,换取方差部分的大幅度减小,最终降低均方误差,要达到这个目的, α \alpha α 的选取至关重要。
α \alpha α 如果太大,也会导致 β \beta β 的估计出现较大的偏移,无法正确拟合数据的真实面貌。因此,在使用中需要找出 α \alpha α 让模型效果变好的最佳取值。
4.2.2 linear_model.Ridge
在sklearn中,岭回归由线性模型库中的Ridge类来调用:
class sklearn.linear_model.Ridge (
alpha=1.0,
fit_intercept=True,
normalize=False,
copy_X=True,
max_iter=None,
tol=0.001,
solver=’auto’,
random_state=None)
例子
import numpy as np
import pandas as pd
from sklearn.linear_model import Ridge, LinearRegression, Lasso
from sklearn.model_selection import train_test_split as TTS
from sklearn.model_selection import cross_val_score
from sklearn.datasets import fetch_california_housing as fch
import matplotlib.pyplot as plt
housevalue = fch()
X = pd.DataFrame(housevalue.data)
y = housevalue.target
X.columns = ["住户收入中位数","房屋使用年代中位数","平均房间数目","平均卧室数目","街区人口","平均入住率","街区的纬度","街区的经度"]
X.head()
Xtrain,Xtest,Ytrain,Ytest = TTS(X,y,test_size=0.3,random_state=420)
#数据集索引恢复
for i in [Xtrain,Xtest]:
i.index = range(i.shape[0])
#使用岭回归来进行建模
reg = Ridge(alpha=1).fit(Xtrain,Ytrain)
reg.score(Xtest,Ytest)
# 0.6043610352312279
与线性回归相比,岭回归的结果并没有改变多少,初步判断不是多重贡献性问题
对比曲线
alpharange = np.arange(1,1001,100)
ridge, lr = [], [] # 岭回归(结果),线性回归(结果)
for alpha in alpharange:
reg = Ridge(alpha=alpha)
linear = LinearRegression()
regs = cross_val_score(reg,X,y,cv=5,scoring = "r2").mean()
ridge.append(regs)
linears = cross_val_score(linear,X,y,cv=5,scoring = "r2").mean()
lr.append(linears)
plt.plot(alpharange,ridge,color="red",label="Ridge")
plt.plot(alpharange,lr,color="orange",label="LR")
plt.title("Mean")
plt.legend()
plt.show()
#细化一下学习曲线
alpharange = np.arange(1,201,10)
ridge, lr = [], [] # 岭回归(结果),线性回归(结果)
for alpha in alpharange:
reg = Ridge(alpha=alpha)
linear = LinearRegression()
regs = cross_val_score(reg,X,y,cv=5,scoring = "r2").mean()
ridge.append(regs)
linears = cross_val_score(linear,X,y,cv=5,scoring = "r2").mean()
lr.append(linears)
plt.plot(alpharange,ridge,color="red",label="Ridge")
plt.plot(alpharange,lr,color="orange",label="LR")
plt.title("Mean")
plt.legend()
plt.show()
可以看出,在数据集上,岭回归的结果轻微上升,随后骤降。可以说数据集带有很轻微的一部分共线性,这种共线性被正则化参数(岭参数)
α
\alpha
α 消除后,模型的效果提升了一点点,但是对于整个模型而言是杯水车薪。
在过了控制多重共线性的点后,模型的效果飞速下降,显然是正则化的程度太重,挤占了参数 β \beta β 本来的估计空间。从这个结果可以看出,数据集的核心问题不在于多重共线性,岭回归不能够提升模型表现。
模型方差如何变化?
alpharange = np.arange(1,1001,100)
ridge, lr = [], []
for alpha in alpharange:
reg = Ridge(alpha=alpha)
linear = LinearRegression()
varR = cross_val_score(reg,X,y,cv=5,scoring="r2").var()
varLR = cross_val_score(linear,X,y,cv=5,scoring="r2").var()
ridge.append(varR)
lr.append(varLR)
plt.plot(alpharange,ridge,color="red",label="Ridge")
plt.plot(alpharange,lr,color="orange",label="LR")
plt.title("Variance")
plt.legend()
plt.show()
可以发现,模型的方差上升快速,不过方差的值本身很小,其变化不超过 R 2 R^2 R2 上升部分的1/3,因此只要噪声的状况维持恒定,模型的泛化误差可能还是一定程度上降低了的。虽然岭回归和Lasso不是设计来提升模型表现,而是专注于解决多重共线性问题的,但当 α \alpha α 在一定范围内变动的时候,消除多重共线性也许能够一定程度上提高模型的泛化能力。
例子 2
from sklearn.datasets import load_boston
from sklearn.model_selection import cross_val_score
X = load_boston().data
y = load_boston().target
Xtrain,Xtest,Ytrain,Ytest = TTS(X,y,test_size=0.3,random_state=420)
查看方差的变化
alpharange = np.arange(1,1001,100)
ridge, lr = [], []
for alpha in alpharange:
reg = Ridge(alpha=alpha)
linear = LinearRegression()
varR = cross_val_score(reg,X,y,cv=5,scoring="r2").var()
varLR = cross_val_score(linear,X,y,cv=5,scoring="r2").var()
ridge.append(varR)
lr.append(varLR)
plt.plot(alpharange,ridge,color="red",label="Ridge")
plt.plot(alpharange,lr,color="orange",label="LR")
plt.title("Variance")
plt.legend()
plt.show()
可以,看到随着 α \alpha α 逐渐增大,岭回归模型方差逐渐下降,说明极有可能存在多重线性性
进一步,查看R2的变化
alpharange = np.arange(1,1001,100)
ridge, lr = [], []
for alpha in alpharange:
reg = Ridge(alpha=alpha)
linear = LinearRegression()
regs = cross_val_score(reg,X,y,cv=5,scoring = "r2").mean()
linears = cross_val_score(linear,X,y,cv=5,scoring = "r2").mean()
ridge.append(regs)
lr.append(linears)
plt.plot(alpharange,ridge,color="red",label="Ridge")
plt.plot(alpharange,lr,color="orange",label="LR")
plt.title("Mean")
plt.legend()
plt.show()
可以看到,看到随着
α
\alpha
α 逐渐增大,模型拟合优度
R
2
R^2
R2 也逐渐增大,结合上面方差情况,说明存在多重线性性。可见使用岭回归还是起到了一定的作用,模型的泛化能力是有可能会上升的。
细化学习曲线
alpharange = np.arange(100,300,10)
ridge, lr = [], []
for alpha in alpharange:
reg = Ridge(alpha=alpha)
linear = LinearRegression()
regs = cross_val_score(reg,X,y,cv=5,scoring = "r2").mean()
linears = cross_val_score(linear,X,y,cv=5,scoring = "r2").mean()
ridge.append(regs)
lr.append(linears)
plt.plot(alpharange,ridge,color="red",label="Ridge")
# plt.plot(alpharange,lr,color="orange",label="LR")
plt.title("Mean")
plt.legend()
plt.show()
4.2.3 选取最佳的正则化参数(岭参数) α \alpha α 取值
使用交叉验证来选择 α \alpha α
在sklearn中,有带交叉验证的岭回归可以使用
sklearn.linear_model.RidgeCV (
alphas=(0.1, 1.0, 10.0),
fit_intercept=True,
normalize=False,
scoring=None,
cv=None,
gcv_mode=None,
store_cv_values=False)
在输入
α
\alpha
α 的时候可以传入元祖作为系数的备选
import numpy as np
import pandas as pd
from sklearn.linear_model import RidgeCV, LinearRegression
from sklearn.model_selection import train_test_split as TTS
from sklearn.datasets import fetch_california_housing as fch
import matplotlib.pyplot as plt
housevalue = fch()
X = pd.DataFrame(housevalue.data)
y = housevalue.target
X.columns = ["住户收入中位数","房屋使用年代中位数","平均房间数目","平均卧室数目","街区人口","平均入住率","街区的纬度","街区的经度"]
Ridge_ = RidgeCV(alphas=np.arange(1,1001,100)
#,scoring="neg_mean_squared_error"
,store_cv_values=True
#,cv=5
).fit(X, y)
# 交叉验证的模式,默认是cv=None,表示默认进行留一交叉验证
#无关交叉验证的岭回归结果
Ridge_.score(X,y)
# 0.606025176733844
#调用所有交叉验证的结果
Ridge_.cv_values_.shape
# (20640, 10)
#进行平均后可以查看每个正则化系数取值下的交叉验证结果
Ridge_.cv_values_.mean(axis=0)
# array([0.52823795, 0.52787439, 0.52807763, 0.52855759, 0.52917958,
# 0.52987689, 0.53061486, 0.53137481, 0.53214638, 0.53292369])
#查看被选择出来的最佳正则化系数
Ridge_.alpha_
# 101
4.3 Lasso
4.3.1 Lasso与多重共线性
除了岭回归之外,最常被提到还有模型 Lasso。
Lasso全称最小绝对收缩和选择算子,和岭回归一样,Lasso是被创造来作用于多重共线性问题的算法,不过Lasso使用的是系数 β \beta β 的L1范式(L1范式则是系数 β \beta β 的绝对值)乘以正则化系数 ,所以Lasso的损失函数表达式为:
min β ∥ X β − y ∥ 2 2 + α ∥ β ∥ 1 \underset{\beta}{\min}\lVert \boldsymbol{X\beta }-\boldsymbol{y} \rVert _2^2+\alpha \lVert \boldsymbol{\beta } \rVert _1 βmin∥Xβ−y∥22+α∥β∥1
不太严谨说法是,Lasso与岭回归非常相似,都是利用正则项来对原本的损失函数形成一个惩罚,以此来防止多重共线性。
Lasso无法解决特征之间”精确相关“的问题。当我们使用最小二乘法求解线性回归时,如果线性回归无解或者报除零错误,换Lasso不能解决任何问题。
而岭回归可以解决特征间的精确相关关系导致的最小二乘法无法使用的问题,而Lasso不行。
在现实中我们其实会比较少遇到“精确相关”的多重共线性问题,大部分多重共线性问题应该是“高度相关“,而如果我们假设方阵 X T X \boldsymbol{X}^{\boldsymbol{T}}\boldsymbol{X} XTX 的逆是一定存在的,可以有:
β = ( X T X ) − 1 ( X T y − α I 2 ) \boldsymbol{\beta }=\left( \boldsymbol{X}^{\boldsymbol{T}}\boldsymbol{X} \right) ^{-1}\left( \boldsymbol{X}^{\boldsymbol{T}}\boldsymbol{y}-\frac{\alpha \boldsymbol{I}}{2} \right) β=(XTX)−1(XTy−2αI)
通过增大 α \alpha α ,可以为 β \beta β 的计算增加一个负项,从而限制参数估计中 β \beta β 的大小,而防止多重共线性引起的参数 β \beta β 被估计过大导致模型失准的问题。
Lasso 不是从根本上解决多重共线性问题,而是限制多重共线性带来的影响。何况,这
还是在我们假设所有的系数都为正的情况下,假设系数
β
\beta
β 无法为正,则很有可能我们需要将我们的正则项参数
β
\beta
β 设定为负,因此 可以取负数,并且负数越大,对共线性的限制也越大。
Lasso 虽然是为了限制多重共线性被创造出来的,但其实并不使用它来抑制多重共线性,反而接受了它在其他方面的优势,
由于,L2正则化只会将系数压缩到尽量接近0,但L1正则化主导稀疏性,因此会将系数压缩到0。
因此,Lasso成为了线性模型中的特征选择工具首选。
4.3.2 Lasso的核心作用:特征选择
sklearn.linear_model.Lasso(
alpha=1.0,
fit_intercept=True,
normalize=False,
precompute=False,
copy_X=True,
max_iter=1000,
tol=0.0001,
warm_start=False,
positive=False,
random_state=None,
selection=’cyclic’)
sklearn中使用类Lasso来调用Lasso回归,比较在意的就是参数 α \alpha α ,正则化系数。另外需要注意的就是参数positive。当这个参数为"True"的时候,是要求Lasso回归出的系数必须为正数,以此来保证我们的一定以增大来控制正则化的程度。
sklearn中的Lasso类不是使用最小二乘法来进行求解,而是使用坐标下降。
因为Lasso不能够从根本解决多重共线性引起的最小二乘法无法使用的问题
使用坐标下降法求解Lasso
import numpy as np
import pandas as pd
from sklearn.linear_model import Ridge, LinearRegression, Lasso
from sklearn.model_selection import train_test_split as TTS
from sklearn.datasets import fetch_california_housing as fch
import matplotlib.pyplot as plt
# 加载数据集
housevalue = fch()
X = pd.DataFrame(housevalue.data)
y = housevalue.target
X.columns = ["住户收入中位数","房屋使用年代中位数","平均房间数目","平均卧室数目","街区人口","平均入住率","街区的纬度","街区的经度"]
# 划分数据集
Xtrain,Xtest,Ytrain,Ytest = TTS(X,y,test_size=0.3,random_state=420)
#恢复索引
for i in [Xtrain,Xtest]:
i.index = range(i.shape[0])
#线性回归进行拟合
reg = LinearRegression().fit(Xtrain,Ytrain)
(reg.coef_*100).tolist()
'''
[43.73589305968401,
1.0211268294493672,
-10.780721617317635,
62.64338275363785,
5.2161253534123096e-05,
-0.33485096463336794,
-41.3095937894771,
-42.62109536208483]
'''
#岭回归进行拟合
Ridge_ = Ridge(alpha=0).fit(Xtrain,Ytrain)
(Ridge_.coef_*100).tolist()
'''
[43.73589305968398,
1.0211268294494116,
-10.78072161731758,
62.643382753637276,
5.2161253532697085e-05,
-0.33485096463335795,
-41.3095937894769,
-42.62109536208446]
'''
# 坐标下降,就有迭代和收敛的问题,在sklearn中不推荐我们使用0这样的正则化系数。
#Lasso进行拟合
lasso_ = Lasso(alpha=0.01).fit(Xtrain,Ytrain)
(lasso_.coef_*100).tolist()
'''
[40.10568371834486,
1.0936292607860143,
-3.7423763610244563,
26.524037834897207,
0.00035253685115039395,
-0.3207129394887798,
-40.06483047344844,
-40.81754399163317]
'''
Lasso对参数 α \alpha α 更加敏感
#将系数进行绘图
plt.plot(range(1,9),(reg.coef_*100).tolist(),color="red",label="LR")
plt.plot(range(1,9),(Ridge_.coef_*100).tolist(),color="orange",label="Ridge")
plt.plot(range(1,9),(lasso_.coef_*100).tolist(),color="k",label="Lasso")
plt.plot(range(1,9),[0]*8,color="grey",linestyle="--")
plt.xlabel('w') #横坐标是每一个特征所对应的系数
plt.legend()
plt.show()
可见,比起岭回归,Lasso所带的L1正则项对于系数的惩罚要重得多,并且它会将系数压缩至0,因此可以被用来做特征选择。也因此,往往让Lasso的正则化系数 在很小的空间中变动,以此来寻找最佳的正则化系数。
4.3.3 选取最佳的正则化参数 α \alpha α 取值
sklearn.linear_model.LassoCV(
eps=0.001,
n_alphas=100,
alphas=None,
fit_intercept=True,
normalize=False,
precompute=’auto’,
max_iter=1000,
tol=0.0001,
copy_X=True,
cv=’warn’,
verbose=False,
n_jobs=None,
positive=False,
random_state=None,
selection=’cyclic’)
使用交叉验证的Lasso类的参数看起来与岭回归略有不同,这是由于Lasso对于alpha的取值更加敏感的性质决定的。由于Lasso对正则化系数的变动过于敏感,因此往往让 α \alpha α 在很小的空间中变动。
这个小空间小到超乎人们的想象(不是0.01到0.02之间这样的空间,这个空间对lasso而言还是太大了),因此设定了一个重要概念“正则化路径”,用来设定正则化系数的变动:
重要概念:正则化路径 regularization path
假设特征矩阵中有 n 个特征,则有特征向量
x
1
,
x
2
,
⋯
,
x
n
x_1,x_2,\cdots ,x_n
x1,x2,⋯,xn。对于每一个
α
\alpha
α 的取值,都可以得出一组对应这个特征向量的参数向量
β
\boldsymbol{\beta }
β ,其中包含了n+1个参数,分别是
β
0
,
β
1
,
⋯
,
β
n
\beta _0,\beta _1,\cdots ,\beta _n
β0,β1,⋯,βn 。
这些参数可以被看作是一个n+1维空间中的一个点。对于不同的 α \alpha α 取值,将得到许多个在n+1维空间中的点,所有的这些点形成的序列,就被称之为是正则化路径。
把形成这个正则化路径的 α \alpha α 的最小值除以最大值得到的量
α min α max \frac{\alpha _{\min}}{\alpha _{\max}} αmaxαmin
称为正则化路径的长度
在sklearn中,可以通过规定正则化路径的长度(即限制 的最小值和最大值之间的比例),以及路径中 α \alpha α 的个数,来让sklearn自动生成 α \alpha α 的取值,这就避免了我们需要自己生成非常非常小的 α \alpha α 的取值列表来让交叉验证类使用,类LassoCV自己就可以计算了。
LassoCV也会单独建立模型。它会先找出最佳的正则化参数,然后在这个参数下按照模型评估指标进行建模。
需要注意的是,LassoCV的模型评估指标选用的是均方误差,而岭回归的模型评估指标是可以自己设定的,并且默认是 R 2 R^2 R2。
from sklearn.linear_model import LassoCV
import numpy as np
import pandas as pd
from sklearn.linear_model import Ridge, LinearRegression, Lasso
from sklearn.model_selection import train_test_split as TTS
from sklearn.datasets import fetch_california_housing as fch
import matplotlib.pyplot as plt
# 加载数据集
housevalue = fch()
X = pd.DataFrame(housevalue.data)
y = housevalue.target
X.columns = ["住户收入中位数","房屋使用年代中位数","平均房间数目","平均卧室数目","街区人口","平均入住率","街区的纬度","街区的经度"]
# 划分数据集
Xtrain,Xtest,Ytrain,Ytest = TTS(X,y,test_size=0.3,random_state=420)
#恢复索引
for i in [Xtrain,Xtest]:
i.index = range(i.shape[0])
自己建立Lasso进行 α \alpha α 选择的范围
alpharange = np.logspace(-10, -2, 200,base=10)
#其实是形成10为底的指数函数
#10**(-10)到10**(-2)次方
alpharange.shape # (200,)
lasso_ = LassoCV(
alphas=alpharange #自行输入的alpha的取值范围
,cv=5 #交叉验证的折数
).fit(Xtrain, Ytrain)
#查看被选择出来的最佳正则化系数
lasso_.alpha_
# 0.0020729217795953697
#调用所有交叉验证的结果
lasso_.mse_path_
lasso_.mse_path_.shape #返回每个alpha下的五折交叉验证结果
# (200, 5)
lasso_.mse_path_.mean(axis=1)
#注意到在岭回归中轴向是axis=0
#在岭回归当中是留一验证,因此交叉验证结果返回的是,每一个样本在每个alpha下的交叉验证结果
#因此要求每个alpha下的交叉验证均值,就是axis=0,跨行求均值
#而在这里,返回的是每一个alpha取值下,每一折交叉验证的结果
#因此要求每个alpha下的交叉验证均值,就是axis=1,跨列求均值
#最佳正则化系数下获得的模型的系数结果
lasso_.coef_
'''
array([ 4.29867301e-01, 1.03623683e-02, -9.32648616e-02, 5.51755252e-01,
1.14732262e-06, -3.31941716e-03, -4.10451223e-01, -4.22410330e-01])
'''
lasso_.score(Xtest,Ytest)
# 0.6038982670571436 拟合优度
与线性回归相比如何
reg = LinearRegression().fit(Xtrain,Ytrain)
reg.score(Xtest,Ytest)
# 0.6043668160178817
使用 LassoCV 自带的正则化路径长度和路径中的 α \alpha α 个数来自动建立alpha选择的范围
ls_ = LassoCV(eps=0.00001,
n_alphas=300,
cv=5
).fit(Xtrain, Ytrain)
ls_.alpha_
# 0.0020954551690628535
ls_.alphas_ #查看所有自动生成的alpha取值
ls_.alphas_.shape
# (300,)
ls_.score(Xtest,Ytest)
# 0.6038915423819201 拟合优度
ls_.coef_
'''
array([ 4.29785372e-01, 1.03639989e-02, -9.31060823e-02, 5.50940621e-01,
1.15407943e-06, -3.31909776e-03, -4.10423420e-01, -4.22369926e-01])
'''
5 非线性问题:多项式回归
三角函数,高次函数等也因此被称为非线性函数
这些数据都不能由一条直线来进行拟合,也没有均匀分布在某一条线的周围,使用“是否线性可分”这个概念来划分分类数据集。当分类数据的分布上可以使用一条直线来将两类数据分开时,就说数据是线性可分的。反之,数据不是线性可分的。
总之,对于回归问题,数据若能分布为一条直线,则是线性的,否则是非线性。对于分类问题,数据分布若能使用一条直线来划分类别,则是线性可分的,否则数据则是线性不可分的。
5.1 线性模型与非线性模型
线性模型
y
=
β
0
+
β
1
x
1
+
β
2
x
2
+
⋯
+
β
n
x
n
y=\beta _0+\beta _1x_1+\beta _2x_2+\cdots +\beta _nx_n
y=β0+β1x1+β2x2+⋯+βnxn
其自变量都是一次项。
建立一个明显是非线性的数据集,并观察线性回归和决策树的而回归在拟合非线性数据集时的表现:
import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression
from sklearn.tree import DecisionTreeRegressor
# 创建需要拟合的数据集
# 构造 sin 函数 数据
rnd = np.random.RandomState(42) #设置随机数种子
X = rnd.uniform(-3, 3, size=100) #random.uniform,从输入的任意两个整数中取出size个随机数
#生成y的思路:先使用NumPy中的函数生成一个sin函数图像,然后再人为添加噪音
y = np.sin(X) + rnd.normal(size=len(X)) / 3 #random.normal,生成size个服从正态分布的随机数
#使用散点图观察建立的数据集是什么样子
plt.scatter(X, y,marker='o',c='k',s=20)
plt.show()
#为后续建模做准备:sklearn只接受二维以上数组作为特征矩阵的输入
X.shape # (100,)
# 转换为二维
X = X.reshape(-1, 1) # (100, 1)
建模
#使用原始数据进行建模
# 线性回归
LinearR = LinearRegression().fit(X, y)
# 决策树
TreeR = DecisionTreeRegressor(random_state=0).fit(X, y)
#放置画布
fig, ax1 = plt.subplots(1)
#创建测试数据:一系列分布在横坐标上的点
line = np.linspace(-3, 3, 1000, endpoint=False).reshape(-1, 1)
#将测试数据带入predict接口,获得模型的拟合效果并进行绘制
ax1.plot(line, LinearR.predict(line), linewidth=2, color='green',
label="linear regression")
ax1.plot(line, TreeR.predict(line), linewidth=2, color='red',
label="decision tree")
#将原数据上的拟合绘制在图像上
ax1.plot(X[:, 0], y, 'o', c='k')
#其他图形选项
ax1.legend(loc="best")
ax1.set_ylabel("Regression output")
ax1.set_xlabel("Input feature")
ax1.set_title("Result before discretization")
plt.tight_layout()
plt.show()
从图像上可以看出,线性回归无法拟合出这条带噪音的正弦曲线的真实面貌,只能够模拟出大概的趋势,而决策树却通过建立复杂的模型将几乎每个点都拟合出来了。可见,使用线性回归模型来拟合非线性数据的效果并不好,而决策树这样的模型却拟合得太细致,但是相比之下,还是决策树的拟合效果更好一些。
决策树是一个典型的非线性模型,当它被用于拟合非线性数据,可以发挥奇效。
其他典型的非线性模型还包括使用高斯核的支持向量机,树的集成算法,以及一切通过三角函数,指数函数等非线性方程来建立的模型。
通常线性模型用于拟合线性数据,非线性模型用于拟合非线性数据。但事实上机器学习远远比我们想象的灵活得多,线性模型可以用来拟合非线性数据,而非线性模型也可以用来拟合线性数据,更神奇的是,有的算法没有模型也可以处理各类数据,而有的模型可以既可以是线性,也可以是非线性模型!
-
非线性模型拟合线性数据
非线性模型们几乎都可以在线性可分数据上有不逊于线性模型的表现。
线性数据对于非线性模型来说太过简单,很容易过拟合
-
线性模型拟合非线性数据
线性模型若用来拟合非线性数据或者对非线性可分的数据进行分类,那通常都会表现糟糕。
线性模型的决策边界都是一条条平行的直线,而非线性模型们的决策边界是交互的直线(格子),曲线,环形等等。
对于分类模型来说,判断模型是线性还是非线性的重要评判因素:
线性模型的决策边界是平行的直线,
非线性模型的决策边界是曲线或者交叉的直线。
之前我们提到,模型上如果自变量上的最高次方为1,则模型是线性的,但这种方式只适用于回归问题。分类模型中,很少讨论模型是否线性,因为我们很少使用线性模型来执行分类任务(逻辑回归是一个特例)。
对分类问题而言,如果一个分类模型的决策边界上自变量的最高次方为1,则我们称这个模型是线性模型。
- 既是线性,也是非线性的模型
对于有一些模型来说,既可以处理线性模型又可以处理非线性模型,比如说强大的支持向量机。
支持向量机本身也是处理线性可分数据的,但却可以通过对数据进行升维(将数据 转移到高维空间中),将非线性可分数据变成高维空间中的线性可分数据,然后使用相应的“核函数”来求解。
当选用线性核函数"linear"的时候,数据没有进行变换,支持向量机中就是线性模型,此时它的决策边界是直线。
当选用非线性核函数比如高斯径向基核函数的时候,数据进行了升维变化,此时支持向量机就是非线性模型,此时它的决策边界在二维空间中是曲线。
所以这个模型可以在线性和非线性之间自由切换,一切取决于它的核函数。
当我们获取数据时,往往希望使用线性模型来对数据进行最初的拟合(线性回归用于回归,逻辑回归用于分类),如果线性模型表现良好,则说明数据本身很可能是线性的或者线性可分的,如果线性模型表现糟糕,这时可以使用非线性模型(如:决策树,随机森林)
5.2 使用分箱处理非线性问题
5.2.1 分箱及分箱的相关问题
让线性回归在非线性数据上表现提升的核心方法之一是对数据进行分箱,也就是离散化。与线性回归相比,常用的一种回归是决策树的回归。
之前拟合过一条带有噪音的正弦曲线以展示多元线性回归与决策树的效用差异,现在来分析一下这张图,然后再使用采取措施帮助我们的线性回归。
import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression
from sklearn.tree import DecisionTreeRegressor
# 创建需要拟合的数据集
# 构造 sin 函数 数据
rnd = np.random.RandomState(42) #设置随机数种子
X = rnd.uniform(-3, 3, size=100) #random.uniform,从输入的任意两个整数中取出size个随机数
#生成y的思路:先使用NumPy中的函数生成一个sin函数图像,然后再人为添加噪音
y = np.sin(X) + rnd.normal(size=len(X)) / 3 #random.normal,生成size个服从正态分布的随机数
#使用散点图观察建立的数据集是什么样子
plt.scatter(X, y,marker='o',c='k',s=20)
plt.show()
#为后续建模做准备:sklearn只接受二维以上数组作为特征矩阵的输入
X.shape # (100,)
# 转换为二维
X = X.reshape(-1, 1) # (100, 1)
#使用原始数据进行建模
# 线性回归
LinearR = LinearRegression().fit(X, y)
# 随机森林
TreeR = DecisionTreeRegressor(random_state=0).fit(X, y)
#放置画布
fig, ax1 = plt.subplots(1)
#创建测试数据:一系列分布在横坐标上的点
line = np.linspace(-3, 3, 1000, endpoint=False).reshape(-1, 1)
#将测试数据带入predict接口,获得模型的拟合效果并进行绘制
ax1.plot(line, LinearR.predict(line), linewidth=2, color='green',
label="linear regression")
ax1.plot(line, TreeR.predict(line), linewidth=2, color='red',
label="decision tree")
#将原数据上的拟合绘制在图像上
ax1.plot(X[:, 0], y, 'o', c='k')
#其他图形选项
ax1.legend(loc="best")
ax1.set_ylabel("Regression output")
ax1.set_xlabel("Input feature")
ax1.set_title("Result before discretization")
plt.tight_layout()
plt.show()
从图像上可以看出,线性回归无法拟合出这条带噪音的正弦曲线的真实面貌,只能够模拟出大概的趋势,而决策树却通过建立复杂的模型将几乎每个点都拟合出来了。
此时此刻,决策树正处于过拟合的状态,对数据的学习过于细致,而线性回归处于拟合不足的状态,这是由于模型本身只能够在线性关系间进行拟合的性质决定的。
为了让线性回归在类似的数据上变得更加强大,可以使用分箱,也就是离散化连续型变量的方法来处理原始数据,以此来提升线性回归的表现。
来看看如何实现:
from sklearn.preprocessing import KBinsDiscretizer
#将数据分箱
enc = KBinsDiscretizer(
n_bins=10 #分几类?
,encode="onehot") #ordinal
#encode模式"onehot":使用做哑变量方式做离散化
#之后返回一个稀疏矩阵(m,n_bins),每一列是一个分好的类别
#对每一个样本而言,它包含的分类(箱子)中它表示为1,其余分类中它表示为0
X_binned = enc.fit_transform(X)
X
'''
array([[-0.75275929],
[ 2.70428584],
[ 1.39196365],
...
'''
X.shape # (100, 1)
X_binned
# 返回一个稀疏矩阵(m,n_bins),每一列是一个分好的类别
'''
<100x10 sparse matrix of type '<class 'numpy.float64'>'
with 100 stored elements in Compressed Sparse Row format>
'''
#使用pandas打开稀疏矩阵
import pandas as pd
pd.DataFrame(X_binned.toarray()).head()
'''
0 1 2 3 4 5 6 7 8 9
0 0.0 0.0 0.0 0.0 [1.0] 0.0 0.0 0.0 0.0 0.0
1 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 [1.0]
2 0.0 0.0 0.0 0.0 0.0 0.0 0.0 [1.0] 0.0 0.0
3 0.0 0.0 0.0 0.0 0.0 0.0 [1.0] 0.0 0.0 0.0
4 0.0 0.0 [1.0] 0.0 0.0 0.0 0.0 0.0 0.0 0.0
'''
# 每一个样本(行),只能占一个箱子(列)
# 第一个样本(0),被分到箱子4
# 第二个样本(1),被分到箱子9
#将使用分箱后的数据来训练模型,
LinearR_ = LinearRegression().fit(X_binned, y)
#创建测试数据:一系列分布在横坐标上的点
line = np.linspace(-3, 3, 1000, endpoint=False).reshape(-1, 1)
LinearR_.predict(line) #line作为测试集
'''
报错
ValueError: matmul: Input operand 1 has a mismatch in its core dimension 0, with gufunc signature (n?,k),(k,m?)->(n?,m?) (size 10 is different from 1)
'''
# 在sklearn中,测试集和训练集的结构必须保持一致,否则报错
line.shape #测试 (1000, 1)
X_binned.shape #训练 (100, 10)
# 结构不一样
#因此需要创建分箱后的测试集:按照已经建好的分箱模型将line分箱
line_binned = enc.transform(line)
line_binned.shape #分箱后的数据是无法进行绘图的 (1000, 10)
line_binned
'''
<1000x10 sparse matrix of type '<class 'numpy.float64'>'
with 1000 stored elements in Compressed Sparse Row format>
'''
pd.DataFrame(line_binned.toarray()).head()
'''
0 1 2 3 4 5 6 7 8 9
0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
1 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
2 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
'''
LinearR_.predict(line_binned).shape # (1000,)
既要对训练集分箱,也要对测试集分箱
最终达到的 是连续型数据
5.2.2 使用分箱数据进行建模和绘图
#准备数据
enc = KBinsDiscretizer(n_bins=10,encode="onehot")
X_binned = enc.fit_transform(X)
line_binned = enc.transform(line)
#将两张图像绘制在一起,布置画布
fig, (ax1, ax2) = plt.subplots(ncols=2
, sharey=True #让两张图共享y轴上的刻度
, figsize=(10, 4))
#在图1中布置在 原始数据 上建模的结果
# 线性回归
ax1.plot(line, LinearR.predict(line), linewidth=2, color='green',label="linear regression")
# 树回归
ax1.plot(line, TreeR.predict(line), linewidth=2, color='red',label="decision tree")
ax1.plot(X[:, 0], y, 'o', c='k')
ax1.legend(loc="best")
ax1.set_ylabel("Regression output")
ax1.set_xlabel("Input feature") #
ax1.set_title("Result before discretization") # 标题
使用分箱数据
#在图2中 使用 分箱数据 进行建模
# 线性回归
LinearR_ = LinearRegression().fit(X_binned, y)
# 树回归
TreeR_ = DecisionTreeRegressor(random_state=0).fit(X_binned, y)
#进行预测,在图2中布置在分箱数据上进行预测的结果
ax2.plot(line #横坐标
, LinearR_.predict(line_binned) #分箱后的特征矩阵的结果
, linewidth=2
, color='green'
, linestyle='-'
, label='linear regression')
ax2.plot(line, TreeR_.predict(line_binned), linewidth=2, color='red',linestyle=':', label='decision tree')
#绘制和箱宽一致的竖线
ax2.vlines(enc.bin_edges_[0] #x轴
, *plt.gca().get_ylim() #y轴的上限和下限
, linewidth=1
, alpha=.2)
#将原始数据分布放置在图像上
ax2.plot(X[:, 0], y, 'o', c='k')
#其他绘图设定
ax2.legend(loc="best")
ax2.set_xlabel("Input feature")
ax2.set_title("Result after discretization")
plt.tight_layout()
plt.show()
从图像上可以看出,离散化后线性回归和决策树上的预测结果完全相同了——线性回归比较成功地拟合了数据的分布,而决策树的过拟合效应也减轻了。
由于特征矩阵被分箱,因此特征矩阵在每个区域内获得的值是恒定的,因此所有模型对同一个箱中所有的样本都会获得相同的预测值。
与分箱前的结果相比,线性回归明显变得更加灵活,而决策树的过拟合问题也得到了改善。但注意,一般来说我们是不使用分箱来改善决策树的过拟合问题的,因为树模型带有丰富而有效的剪枝功能来防止过拟合。
5.2.3 箱子数如何影响模型的结果
# 分 5 箱
enc = KBinsDiscretizer(n_bins=5,encode="onehot")
X_binned = enc.fit_transform(X)
line_binned = enc.transform(line)
fig, ax2 = plt.subplots(1,figsize=(5,4))
LinearR_ = LinearRegression().fit(X_binned, y)
print(LinearR_.score(line_binned,np.sin(line)))
TreeR_ = DecisionTreeRegressor(random_state=0).fit(X_binned, y)
ax2.plot(line #横坐标
, LinearR_.predict(line_binned) #分箱后的特征矩阵的结果
, linewidth=2
, color='green'
, linestyle='-'
, label='linear regression')
ax2.plot(line, TreeR_.predict(line_binned), linewidth=2, color='red',linestyle=':', label='decision tree')
ax2.vlines(enc.bin_edges_[0], *plt.gca().get_ylim(), linewidth=1, alpha=.2)
ax2.plot(X[:, 0], y, 'o', c='k')
ax2.legend(loc="best")
ax2.set_xlabel("Input feature")
ax2.set_title("Result after discretization")
plt.tight_layout()
plt.show()
5.2.4 如何选取最优的箱数
绘制学习曲线
from sklearn.model_selection import cross_val_score as CVS
import numpy as np
pred,score,var = [], [], []
binsrange = [2,5,10,15,20,30] # 箱数
for i in binsrange:
#实例化分箱类
enc = KBinsDiscretizer(n_bins=i,encode="onehot")
#转换数据
X_binned = enc.fit_transform(X)
line_binned = enc.transform(line)
#建立线性回归模型
LinearR_ = LinearRegression()
#全数据集上的交叉验证
cvresult = CVS(LinearR_,X_binned,y,cv=5) # 5折
score.append(cvresult.mean())
var.append(cvresult.var())
#测试数据集上的打分结果
# X_binned 训练集
# line_binned 测试集
# np.sin(line) 真实的正弦值
pred.append(LinearR_.fit(X_binned,y).score(line_binned,np.sin(line)))
#绘制图像
plt.figure(figsize=(6,5))
# 第一条线 测试数据
plt.plot(binsrange,pred,c="orange",label="test")
# 第二条线 全数据下交叉验证的结果均值
plt.plot(binsrange,score,c="k",label="full data")
# 围绕在均值两边的方差
plt.plot(binsrange,score+np.array(var)*0.5,c="red",linestyle="--",label = "var")
plt.plot(binsrange,score-np.array(var)*0.5,c="red",linestyle="--")
plt.legend()
plt.show()
在箱数为 20 时,模型在全数据下方差最小,模型稳定,且
R
2
R^2
R2 也是最高的
当箱数为 20 时
enc = KBinsDiscretizer(n_bins=i,encode="onehot")
#转换数据
X_binned = enc.fit_transform(X)
#创建测试数据:一系列分布在横坐标上的点
line = np.linspace(-3, 3, 1000, endpoint=False).reshape(-1, 1)
line_binned = enc.transform(line)
#建立线性回归模型
LinearR_ = LinearRegression().fit(X_binned, y)
# line_binned 测试集分箱
LinearR_.score(line_binned, np.sin(line))
# R2 0.94389623914709
5.3 多项式回归PolynomialFeatures
除了分箱之外,另一种更普遍的用于解决”线性回归只能处理线性数据“问题的手段,就是使用多项式回归对线性回归进行改进。
可以让线性回归使用类似于升维的转换,将数据由非线性转换为线性,从而为线性回归赋予处理非线性数据的能力
线性模型中的升维工具:多项式变化。
这是一种通过增加自变量上的次数,而将数据映射到高维空间的方法,只要设定一个自变量上的次数(大于1),就可以相应地获得数据投影在高次方的空间中的结果。
这种方法可以非常容易地通过sklearn中的类PolynomialFeatures来实现。
sklearn.preprocessing.PolynomialFeatures (
degree=2,
interaction_only=False,
include_bias=True)
from sklearn.preprocessing import PolynomialFeatures
import numpy as np
#如果原始数据是一维的 reshape转换为二维
X = np.arange(1,4).reshape(-1,1)
X
'''
array([[1],
[2],
[3]])
'''
#二次多项式,参数degree控制多项式的次方
poly = PolynomialFeatures(degree=2) #实例化 最高次方 2
#接口transform直接调用
X_ = poly.fit_transform(X) # 升维
X_
'''
array([[1., 1., 1.],
[1., 2., 4.],
[1., 3., 9.]])
'''
#三次多项式
PolynomialFeatures(degree=3).fit_transform(X) # 升维
'''
array([[ 1., 1., 1., 1.],
[ 1., 2., 4., 8.],
[ 1., 3., 9., 27.]])
'''
维度的增加是有一定的规律的
假设多项式转化的次数是n,则数据会被转化成形如:
[
1
,
x
,
x
2
,
x
3
,
⋯
,
x
n
]
\left[ 1,x,x^2,x^3,\cdots ,x^n \right]
[1,x,x2,x3,⋯,xn]
而拟合出的方程也可以被改写成:
y
=
β
0
+
β
1
x
+
β
2
x
2
+
⋯
+
β
n
x
n
y=\beta _0+\beta _1x+\beta _2x^2+\cdots +\beta _nx^n
y=β0+β1x+β2x2+⋯+βnxn
大多数时候我们的原始特征矩阵不可能会是一维的,至少也是二维以上,很多时候还
可能存在上千个特征或者维度。现在我们来看看原始特征矩阵是二维的状况:
X = np.arange(6).reshape(3, 2)
X
'''
array([[0, 1],
[2, 3],
[4, 5]])
'''
#尝试二次多项式
PolynomialFeatures(degree=2).fit_transform(X)
'''
array([[ 1., 0., 1., 0., 0., 1.],
[ 1., 2., 3., 4., 6., 9.],
[ 1., 4., 5., 16., 20., 25.]])
'''
很明显,上面一维的转换公式已经不适用了,但如果我们仔细看,是可以看出这样的规律的:
当原始特征为二维的时候,多项式的二次变化突然将特征增加到了六维,其中一维是常量(也就是截距)。当我们继续适用线性回归去拟合的时候,我们会得到的方程如下:
继续来尝试三次多项式:
PolynomialFeatures(degree=3).fit_transform(X)
'''
array([[ 1., 0., 1., 0., 0., 1., 0., 0., 0., 1.],
[ 1., 2., 3., 4., 6., 9., 8., 12., 18., 27.],
[ 1., 4., 5., 16., 20., 25., 64., 80., 100., 125.]])
'''
可以发现,当进行多项式转换的时候,多项式会产出到最高次数为止的所有低高次项。
sklearn中存在着控制是否要生成平方和立方项的参数interaction_only,默认为
False,以减少共线性。
多项式回归没有固定的模型表达式
5.3.1 多项式回归处理非线性问题
from sklearn.preprocessing import PolynomialFeatures as PF
from sklearn.linear_model import LinearRegression
import numpy as np
# 准备数据
rnd = np.random.RandomState(42) #设置随机数种子
X = rnd.uniform(-3, 3, size=100)
y = np.sin(X) + rnd.normal(size=len(X)) / 3
#将X升维,准备好放入sklearn中
X = X.reshape(-1,1)
#创建测试数据,均匀分布在训练集X的取值范围内的一千个点
line = np.linspace(-3, 3, 1000, endpoint=False).reshape(-1, 1)
原始特征矩阵的拟合结果
LinearR = LinearRegression().fit(X, y)
#对训练数据的拟合
LinearR.score(X,y) # 0.5361526059318595
#对测试数据的拟合
LinearR.score(line,np.sin(line)) # 0.6800102369793312
多项式拟合,设定高次项
d=5
#进行高此项转换
poly = PF(degree=d)
X_ = poly.fit_transform(X) # 训练数据和测试数据均进行高次转换
line_ = PF(degree=d).fit_transform(line)
#训练数据的拟合
LinearR_ = LinearRegression().fit(X_, y)
LinearR_.score(X_,y) # 0.8561679370344799
#测试数据的拟合
LinearR_.score(line_,np.sin(line)) # 0.9868904451787954
可视化
import matplotlib.pyplot as plt
d=5
#和上面展示一致的建模流程
LinearR = LinearRegression().fit(X, y) # 线性回归
X_ = PF(degree=d).fit_transform(X)
LinearR_ = LinearRegression().fit(X_, y)
line = np.linspace(-3, 3, 1000, endpoint=False).reshape(-1, 1)
line_ = PF(degree=d).fit_transform(line)
#放置画布
fig, ax1 = plt.subplots(1)
#将测试数据带入predict接口,获得模型的拟合效果并进行绘制
ax1.plot(line, LinearR.predict(line), linewidth=2, color='green',label="linear regression")
ax1.plot(line, LinearR_.predict(line_), linewidth=2, color='red',label="Polynomial regression")
#将原数据上的拟合绘制在图像上
ax1.plot(X[:, 0], y, 'o', c='k')
#其他图形选项
ax1.legend(loc="best")
ax1.set_ylabel("Regression output")
ax1.set_xlabel("Input feature")
ax1.set_title("Linear Regression ordinary vs poly")
plt.tight_layout()
plt.show()
多项式回归能够较好地拟合非线性数据,还不容易发生过拟合,可以说是保留了线性回归作为线性模型所带的“不容易过拟合”和“计算快速”的性质,同时又实现了优秀地拟合非线性数据。
5.3.2 多项式回归的可解释性
线性回归是一个具有高解释性的模型,它能够对每个特征拟合出参数以帮助我们理解每个特征对于标签的作用。
当进行了多项式转换后,尽管形成形如线性回归的方程,但随着数据维度和多项式次数的上升,方程也变得异常复杂,可能无法一眼看出增维后的特征是由之前的什么特征组成的(之前我们都是肉眼看肉眼判断)。
不过,多项式回归的可解释性依然是存在的,可以使用接口get_feature_names来调用生成的新特征矩阵的各个特征上的名称,以便帮助我们解释模型。
import numpy as np
from sklearn.preprocessing import PolynomialFeatures
from sklearn.linear_model import LinearRegression
X = np.arange(9).reshape(3, 3)
X
'''
array([[0, 1, 2],
[3, 4, 5],
[6, 7, 8]])
'''
poly = PolynomialFeatures(degree=5).fit(X)
#重要接口get_feature_names
poly.get_feature_names() # 调用所有特征名字
例子
from sklearn.datasets import fetch_california_housing as fch
import pandas as pd
housevalue = fch()
X = pd.DataFrame(housevalue.data)
y = housevalue.target
housevalue.feature_names
'''
['MedInc',
'HouseAge',
'AveRooms',
'AveBedrms',
'Population',
'AveOccup',
'Latitude',
'Longitude']
'''
X.columns = ["住户收入中位数","房屋使用年代中位数","平均房间数目","平均卧室数目","街区人口","平均入住率","街区的纬度","街区的经度"]
poly = PolynomialFeatures(degree=2).fit(X,y) # 实例化+拟合 升到2次
poly.get_feature_names(X.columns)
'''
['1',
'住户收入中位数',
'房屋使用年代中位数',
'平均房间数目',
'平均卧室数目',
'街区人口',
'平均入住率',
'街区的纬度',
'街区的经度',
'住户收入中位数^2',
'住户收入中位数 房屋使用年代中位数',
'住户收入中位数 平均房间数目',
'住户收入中位数 平均卧室数目',
'住户收入中位数 街区人口',
'住户收入中位数 平均入住率',
'住户收入中位数 街区的纬度',
'住户收入中位数 街区的经度',
'房屋使用年代中位数^2',
'房屋使用年代中位数 平均房间数目',
'房屋使用年代中位数 平均卧室数目',
'房屋使用年代中位数 街区人口',
'房屋使用年代中位数 平均入住率',
'房屋使用年代中位数 街区的纬度',
'房屋使用年代中位数 街区的经度',
'平均房间数目^2',
'平均房间数目 平均卧室数目',
'平均房间数目 街区人口',
'平均房间数目 平均入住率',
'平均房间数目 街区的纬度',
'平均房间数目 街区的经度',
'平均卧室数目^2',
'平均卧室数目 街区人口',
'平均卧室数目 平均入住率',
'平均卧室数目 街区的纬度',
'平均卧室数目 街区的经度',
'街区人口^2',
'街区人口 平均入住率',
'街区人口 街区的纬度',
'街区人口 街区的经度',
'平均入住率^2',
'平均入住率 街区的纬度',
'平均入住率 街区的经度',
'街区的纬度^2',
'街区的纬度 街区的经度',
'街区的经度^2']
'''
X_ = poly.transform(X) # 转换
#在这之后,我们依然可以直接建立模型,然后使用线性回归的coef_属性来查看什么特征对标签的影响最大
reg = LinearRegression().fit(X_,y)
[*zip(poly.get_feature_names(X.columns),reg.coef_)]
'''
[('1', 5.919541435538823e-08),
('住户收入中位数', -11.243025626342437),
('房屋使用年代中位数', -0.8488985558284562),
('平均房间数目', 6.4410593651213635),
('平均卧室数目', -31.591330651295365),
('街区人口', 0.00040609067929028186),
('平均入住率', 1.003862327021211),
('街区的纬度', 8.705681910190394),
('街区的经度', 5.8806327457107965),
('住户收入中位数^2', -0.0313081213151669),
('住户收入中位数 房屋使用年代中位数', 0.0018599480067008538),
('住户收入中位数 平均房间数目', 0.043302039980649264),
('住户收入中位数 平均卧室数目', -0.18614230762148926),
('住户收入中位数 街区人口', 5.728314525856295e-05),
('住户收入中位数 平均入住率', -0.0025901945279693736),
('住户收入中位数 街区的纬度', -0.15250571773355712),
('住户收入中位数 街区的经度', -0.1442429442847879),
('房屋使用年代中位数^2', 0.00021172534934901734),
('房屋使用年代中位数 平均房间数目', -0.001262189925797411),
('房屋使用年代中位数 平均卧室数目', 0.010611504463207522),
('房屋使用年代中位数 街区人口', 2.818852232910943e-06),
('房屋使用年代中位数 平均入住率', -0.0018171694880040896),
('房屋使用年代中位数 街区的纬度', -0.010069037386845982),
('房屋使用年代中位数 街区的经度', -0.009999501833041565),
('平均房间数目^2', 0.00726947766112956),
('平均房间数目 平均卧室数目', -0.0689064376034401),
('平均房间数目 街区人口', -6.823655292556474e-05),
('平均房间数目 平均入住率', 0.026887883884752058),
('平均房间数目 街区的纬度', 0.08750899458558387),
('平均房间数目 街区的经度', 0.08228903941858158),
('平均卧室数目^2', 0.1601809557433484),
('平均卧室数目 街区人口', 0.0005142639945011854),
('平均卧室数目 平均入住率', -0.08719113878663737),
('平均卧室数目 街区的纬度', -0.43704303291687147),
('平均卧室数目 街区的经度', -0.40415060876773945),
('街区人口^2', 2.7377934284324407e-09),
('街区人口 平均入住率', 1.9142675405873987e-05),
('街区人口 街区的纬度', 2.2952985444747162e-05),
('街区人口 街区的经度', 1.4656776567978964e-05),
('平均入住率^2', 8.715610325353607e-05),
('平均入住率 街区的纬度', 0.021334459217548014),
('平均入住率 街区的经度', 0.01624129382159661),
('街区的纬度^2', 0.06188673573416359),
('街区的纬度 街区的经度', 0.1081071733101677),
('街区的经度^2', 0.03990773509436183)]
'''
#放到dataframe中进行排序
coeff = pd.DataFrame([poly.get_feature_names(X.columns),reg.coef_.tolist()]).T
coeff.columns = ["feature","coef"]
coeff.sort_values(by="coef") # 按系数进行排序
'''
feature coef
4 平均卧室数目 -31.5913
1 住户收入中位数 -11.243
2 房屋使用年代中位数 -0.848899
33 平均卧室数目 街区的纬度 -0.437043
34 平均卧室数目 街区的经度 -0.404151
12 住户收入中位数 平均卧室数目 -0.186142
15 住户收入中位数 街区的纬度 -0.152506
16 住户收入中位数 街区的经度 -0.144243
32 平均卧室数目 平均入住率 -0.0871911
25 平均房间数目 平均卧室数目 -0.0689064
9 住户收入中位数^2 -0.0313081
22 房屋使用年代中位数 街区的纬度 -0.010069
23 房屋使用年代中位数 街区的经度 -0.0099995
14 住户收入中位数 平均入住率 -0.00259019
21 房屋使用年代中位数 平均入住率 -0.00181717
18 房屋使用年代中位数 平均房间数目 -0.00126219
26 平均房间数目 街区人口 -6.82366e-05
35 街区人口^2 2.73779e-09
0 1 5.91954e-08
20 房屋使用年代中位数 街区人口 2.81885e-06
38 街区人口 街区的经度 1.46568e-05
36 街区人口 平均入住率 1.91427e-05
37 街区人口 街区的纬度 2.2953e-05
13 住户收入中位数 街区人口 5.72831e-05
39 平均入住率^2 8.71561e-05
17 房屋使用年代中位数^2 0.000211725
5 街区人口 0.000406091
31 平均卧室数目 街区人口 0.000514264
10 住户收入中位数 房屋使用年代中位数 0.00185995
24 平均房间数目^2 0.00726948
19 房屋使用年代中位数 平均卧室数目 0.0106115
41 平均入住率 街区的经度 0.0162413
40 平均入住率 街区的纬度 0.0213345
27 平均房间数目 平均入住率 0.0268879
44 街区的经度^2 0.0399077
11 住户收入中位数 平均房间数目 0.043302
42 街区的纬度^2 0.0618867
29 平均房间数目 街区的经度 0.082289
28 平均房间数目 街区的纬度 0.087509
43 街区的纬度 街区的经度 0.108107
30 平均卧室数目^2 0.160181
6 平均入住率 1.00386
8 街区的经度 5.88063
3 平均房间数目 6.44106
7 街区的纬度 8.70568
'''
可以发现,不仅数据的可解释性还存在,还可以通过这样的手段做特征工程——特征创造。多项式帮助我们进行了一系列特征之间相乘的组合,若能够找出组合起来后对标签贡献巨大的特征,那就是创造了新的有效特征,对于任何学科而言发现新特征都是非常有价值的。
5.3.3 多项式回归是线性还是非线性模型?
多项式回归通常被认为是非线性模型,但广义上它是一种特殊的线性模型,它能够帮助我们处理非线性数据,是线性回归的一种进化。
在现实中,多项式变化疯狂增加数据维度的同时,也增加了过拟合的可能性,因此多项式变化多与能够处理过拟合的线性模型如岭回归,Lasso等来连用,与在线性回归上使用的效果是一致的
最后,岭回归和Lasso是为了解决多元线性回归中使用最小二乘法的各种限制,主要用途是消除多重共线性带来的影响并且做特征选择,而多项式回归解决了线性回归无法拟合非线性数据的明显缺点,核心作用是提升模型的表现。