目录
1.Lasso与多重共线性
2. Lasso的核心作用:特征选择
3. 选取最佳的正则化参数取值
Lasso全称最小绝对收缩和选择算子(Least absolute shrinkage and selection operator),由于这个名称过于复杂所以简称为Lasso,和岭回归一样,Lasso是用来作用于多重共线性的算法,不过Lasso使用的是系数的L1范式(L1范式是系数的绝对值)乘系数,所以Lasso的损失函数表达式为:
Lasso的推导过程:
在岭回归中,通过正则化系数能够向方阵加上一个单位矩阵,以此来防止方阵的行列式为0,而在L1范式所带的正则项在求导之后并不带有这个项,因此无法对造成任何影响,也就是说Lasso无法解决特征之间“精确相关”的问题,当我们使用最小二乘法求解线性回归时,如果线性回归无解或报除零错误,换Lasso不能解决任何问题。
岭回归 VS Lasso
岭回归可以解决特征间的精确相关关系导致最小二乘法无法使用的问题,而Lasso不行。
而在现实中,其实会很少遇到“精确相关关系”的多重共线性问题,大部分多重共线性应该是“高度相关关系”,而如果假设方阵的逆是一定存在的,那么可以有:
通过增大,我们可以为的计算增加增加一个负项,从而限制参数估计中的大小,而防止多重共线性引起的参数被估计过大导致模型失准的问题,Lasso不是从根本上解决多重共线性的问题,而是限制多重共线性带来的影响。何况这还是在假设所有系数都为正的前提下,假设系数无法为正,则很有可能需要将正则化参数设定为负,因此可以去负数,并且负数越大,对共线性的限制也越大。
L1和L2正则化一个核心差异就是他们对系数的影响:两个正则化都会压缩的大小,对标签贡献更少的特征的系数会更小,也会更容易被压缩。不过L2正则化只会将系数压缩到尽量接近0,但L1正则化主导稀疏性,因此会将系数压缩到0.这个性质,让Lasso成为了线性模型中的特征选择工具首选。
class sklearn.linear_model.Lasso(alpha=1.0,fit_intercept=True,normalize=False,precompute=False,copy_X=True,max_iter=1000,tol=0.001,warm_start=False,random_state=None)
sklearn中使用类Lasso来调用Lasso回归,众多参数中比较重要的是参数alpha,正则化系数设置使用参数positive,当这个参数为True,表示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.735893059684, 1.0211268294493827, -10.780721617317681, 62.643382753637766, 5.2161253534695196e-05, -0.3348509646333501, -41.3095937894772, -42.62109536208474]
# 岭回归进行拟合
Ridge_=Ridge(alpha=0).fit(xtrain,ytrain)
(Ridge_.coef_*100).tolist()
[43.73589305968356, 1.0211268294493694, -10.780721617316962, 62.6433827536353, 5.2161253532548055e-05, -0.3348509646333529, -41.30959378947995, -42.62109536208777]
# Lasso进行拟合
lasso_=Lasso(alpha=0).fit(xtrain,ytrain)
(lasso_.coef_*100).tolist()
:2: UserWarning: With alpha=0, this algorithm does not converge well. You are advised to use the LinearRegression estimator lasso_=Lasso(alpha=0).fit(xtrain,ytrain) D:\Anaconda\Anaconda\lib\site-packages\sklearn\linear_model\_coordinate_descent.py:648: UserWarning: Coordinate descent with no regularization may lead to unexpected results and is discouraged. model = cd_fast.enet_coordinate_descent( D:\Anaconda\Anaconda\lib\site-packages\sklearn\linear_model\_coordinate_descent.py:648: ConvergenceWarning: Objective did not converge. You might want to increase the number of iterations, check the scale of the features or consider increasing regularisation. Duality gap: 3.770e+03, tolerance: 1.917e+00 Linear regression models with null weight for the l1 regularization term are more efficiently fitted using one of the solvers implemented in sklearn.linear_model.Ridge/RidgeCV instead. model = cd_fast.enet_coordinate_descent( [43.73589305968403, 1.0211268294494058, -10.780721617317653, 62.643382753637724, 5.2161253532678864e-05, -0.33485096463335745, -41.30959378947717, -42.62109536208475]
报错内容分别为:
1.正则化系数为0,这样算法不可收敛!如果想让正则化系数为0,则是用线性回归
2.没有正则项的坐标下降法可能会导致意外的结果,不鼓励这样做!
3.目标函数没有收敛,你也许想要增加迭代次数,使用一个非常小的alpha来拟合模型可能会造成精确度问题。
将alpha设置为0.01
# 岭回归进行拟合
Ridge_=Ridge(alpha=0.01).fit(xtrain,ytrain)
(Ridge_.coef_*100).tolist()
[43.73575720621553, 1.0211292318121377, -10.78046033625102, 62.64202320775469, 5.217068073227091e-05, -0.3348506517067568, -41.309571432294405, -42.621053889327314]
# Lasso进行拟合
lasso_=Lasso(alpha=0.01).fit(xtrain,ytrain)
(lasso_.coef_*100).tolist()
[40.10568371834486, 1.093629260786014, -3.7423763610244563, 26.524037834897197, 0.00035253685115039417, -0.32071293948878005, -40.064830473448424, -40.81754399163315]
加大正则项系数
# 岭回归进行拟合
Ridge_=Ridge(alpha=10**4).fit(xtrain,ytrain)
(Ridge_.coef_*100).tolist()
[34.62081517607707, 1.5196170869238759, 0.3968610529209999, 0.915181251035547, 0.0021739238012248533, -0.34768660148101127, -14.73696347421548, -13.435576102527182]
# Lasso进行拟合
lasso_=Lasso(alpha=10**4).fit(xtrain,ytrain)
(lasso_.coef_*100).tolist()
[0.0, 0.0, 0.0, -0.0, -0.0, -0.0, -0.0, -0.0]
# Lasso进行拟合
lasso_=Lasso(alpha=1).fit(xtrain,ytrain)
(lasso_.coef_*100).tolist()
[14.581141247629423, 0.6209347344423876, 0.0, -0.0, -0.00028065986329009983, -0.0, -0.0, -0.0]
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的正则化系数在很小的空间中变动,以此来寻找最佳正则化系数。
class 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对正则化系数的变动过于敏感,因此我们往往让在很小的空间中变动。这个小空间很小(不是0.01到0.02之间这样的空间,这样的空间对Lasso而言还是太大了),因此设定了一个重要概念“正则化路径”,用来设定正则化系数的变动:
正则化路径regularization path |
---|
假设特征矩阵中有n个特征,则有特征向量…。对于每一个的取值,我们都可以得出一组对应这个特征向量的参数向量,其中包含了n+1个参数,分别是……。这些参数可以被看做是一个n维空间中的一个点。对于不同的取值,我们将得到许多个在n维空间中的点,所有的这些点形成的序列,就被称之为正则化路径。 |
我们把形成这个正则化路径的的最小值除以的最大值得到的量称为正则化路径的长度(length of the path)。在sklearn中,我们可以通过规定正则化路径的长度(即限制的最小值和最大值之间的比例),以及路径中的个数,来让sklearn为我们自动生成的取值,这就避免了我们需要自己生成非常非常小的的取值列表来让交叉验证类使用,类LassoCV就可以自己计算。
和岭回归的交叉验证相似,除了进行交叉验证之外,LassoCV也会单独建立模型,它会先找出最佳的正则化参数,然后在这个参数下按照模型评估指标进行建模,需要注意的是,LassoCV的模型评估指标选用的是均方误差,而岭回归的模型评估指标是可以自己设定的,并且默认是。
参数 | 含义 |
---|---|
eps | 正则化路径的长度,默认为0.001 |
n_alphas | 正则化路径中的个数,默认100 |
alphas | 需要测试的正则化参数取值的元组,默认为None,当不输入的时候,自动使用eps和n_alphas来自动生成代入交叉验证的正则化参数 |
cv | 交叉验证的次数,默认3折交叉验证 |
属性 | 含义 |
---|---|
alpha_ | 调用交叉验证选出来的最佳正则化参数 |
alphas_ | 使用正则化路径的长度和路径中的个数来自动生成的,用来进行交叉验证的正则化参数 |
mse_path | 返回所有交叉验证的结果细节 |
coef_ | 调用最佳正则化参数下建立的模型的系数 |
实践代码:
from sklearn.linear_model import LassoCV
import numpy as np
# 自己建立Lasso进行alpha选择的范围
alpharange=np.logspace(-10,-2,200,base=10)
# 形成以10为底的指数函数 10**(-10)到10**(-2)次方
# 建模
lasso_=LassoCV(alphas=alpharange
,cv=5
).fit(xtrain,ytrain)
# 查看被选择出来的最佳正则化系数
lasso_.alpha_
0.0020729217795953697
lasso_.mse_path_.mean(axis=1) #返回均方误差 在岭回归中轴向axis=0
在岭回归当中使用的是留意验证,因此交叉验证的结果是每一个样本在每个alpha下的交叉验证结果,因此求每个alpha下交叉验证的均值,就是axis=0,跨行求均值 列为alpha
在Lasso中返回的是,每一个alpha取值下,每一折交叉验证的结果,因此求每个alpha下交叉验证均值,就是axis=1,跨列求均值 行为alpha
# 最佳正则化系数下获得的模型的系数结果
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.6038982670571434
# 与线性回归相比如何
reg=LinearRegression().fit(xtrain,ytrain)
reg.score(xtest,ytest)
0.6084558760596188
# 使用LassoCV自带的正则化路径长度和路径中的alpha个数来自动建立alpha选择的范围
ls_=LassoCV(eps=0.00001
,n_alphas=300
,cv=5
).fit(xtrain,ytrain)
ls_.alpha_
0.0020954551690628557
ls_.score(xtest,ytest)
0.60389154238192
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])