上篇《线性回归中的多重共线性与岭回归》(点击跳转)详细介绍了线性回归中多重共线性,以及一种线性回归的缩减
(shrinkage)方法 ----岭回归
(Ridge Regression),除此之外另一种线性回归的缩减方法----Lasso
回归亦可解决多重共线性问题,但是不一样的是Lasso回归针对不同的自变量,会使其收敛的速度不一样。有的变量就很快趋于0了,有的却会很慢。因此一定程度上Lasso回归非常适合于做特征选择。
套索回归(Lasso Regression)
。Lasso
全称最小绝对收缩和选择算子(least absolute shrinkage and selection operator
),以L1
先验作为正则化器训练的线性模型。 它能够减少变化程度并提高线性回归模型的精度。在线性回归基础上增加L1
正则化项。
本文将从Lasso
回归分析讨论其损失函数、如何处理多重共线性、如何通过特征系数压缩以实现特征选择以及简单介绍了选择正则化系数类LassoCV
,并以在加利福利亚房屋数据集应用加以说明。
损失函数
与岭回归一样,在多元线性回归的损失函数上加上了正则项,不同的是表达为系数
的L1-范式
(即系数
的绝对值)乘以正则化系数
。
L1-范数和L2-范数
向量的L1-范数
向量内各元素绝对值之和
向量的L2-范数
向量内元素的平方和再开发
最小化目标函数:
假设我们的特征矩阵结构为(m,n)
,系数
的结构是(1,n)
,
是构为(n,n)
单位矩阵则
最终得到
在lasso
中,正则化系数
无法对矩阵
造成任何影响,即Lasso无法解决特征之间 "精确相关"的问题。
岭回归
可以解决特征间的精确相关关系导致的最小二乘法无法使用的问题,而Lasso
不行。
现实中,数据量大且很复杂,很难遇到 "精确相关的问题",因此假设矩阵
的逆存在,则
可以通过增大
而限制参数估计中的
的大小,而防止多重共线性引起的参数
被估计过大导致模型失准的问题。Lasso不是从根本上解决多重共线性问题,而是限制多重共线性带来的影响。
特征选择原理
L1正则化和L2正则化一个核心差异就是他们对系数
的影响。 两个正则化都会压缩系数
的大小,对标签贡献更少的特征的系数会更小,也会更容易被压缩。
不过,L2正则化
只会将系数压缩到尽量接近0,但L1正则化
主导稀疏性,因此会将系数压缩到0。
相比于岭回归,Lasso可以解决现在高维数据一个普遍问题——稀疏性
。高维数据即
的情况,现在随着数据采集能力的提高,特征数采集的多,但是其中可能有很多特征是不重要的,实际系数很小,如果用岭回归可能估计出相反的结果,即系数很大。而用Lasso可以把这些不重要变量的系数压缩为0,既实现了较为准确的参数估计,也实现了特征选择即降维。
Lasso特征选择原理
假设
是满秩矩阵(点击跳转),用最小二乘法估计出参数向量
,对于
亦可写成
,并以椭圆表示
从图中可以看出,L2-范数
约束相比于L1-范数
约束更不易在坐标轴上相交,因为圆"太凸,太光滑"了,所以相切很容易做到,而且很不容易做到使得回归系数
取0的时候相切到。但是棱形就不一样了,它的区域"很尖",所以"尖点"更加容易被第一次触碰相交,即使得特征值取0的点。Lasso方法中两图相交的点为
,则意味着系数
被压缩至0了。
这就是它的几何解释,也一定程度上说明了为什么Lasso回归在高维(自变量的个数大于样本个数的情形)的问题上有如此广泛的应用。
Lasso 最优解
Lasso因其损失函数不是连续可导的,因此常规的解法如梯度下降法、牛顿法就没法用了。接下来简单介绍下坐标轴下降法。
坐标轴下降法
坐标轴下降法是一种迭代算法,与梯度下降法利用目标函数的导数来确定搜索方向不同,坐标轴下降法是在当前坐标轴上搜索函数最小值,不需要求目标函数的导数。
凸函数
有
个参数向量
。令参数向量
为
维向量,即对于每个参数向量有
固定
个参数,计算剩下的那个参数使得凸函数
达到最小的点,
个参数都来一次,就得到了该次迭代的最小值点。
以第
个参数
为例 1, 确定初始位置点
2, 第1次迭代,从
开始计算后面
个参数,得到使得
达到最小的点参数
,然后依次计算,直至
为止,一共进行
次迭代
3, 第
次迭代:
4, 一直到第
次迭代,直到
相较与
次迭代都变化极小,说明结果已收敛,迭代结束,否则继续迭代。
以二维为例,设损失函数为凸函数
,在初始点固定
,找使得达
到最小的
,然后固定
,找使得
达到最小的
,这样一直迭代下去,因为
是凸函数,所以一定可以找到使得
达到最小的点
sklearn.linear_model.Lasso
语法
代码语言:javascript复制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')
重要参数
positive : bool, optional 当这个参数为
"True"
的时候,是我们要求Lasso
回归出的系数必须为正数,以此来保证
一定以增大来控制正则化的程度。
sklearn中Lasso使用的损失函数是
其中
的作用跟之前一样,只是作为一个系数,为了方便计算以及消除样本数量对建模结果的影响。
用法
代码语言:javascript复制>>> from sklearn import linear_model
>>> clf = linear_model.Lasso(alpha=0.1)
>>> clf.fit([[0,0], [1, 1], [2, 2]], [0, 1, 2])
Lasso(alpha=0.1)
>>> print(clf.coef_)
[0.85 0. ]
>>> print(clf.intercept_)
0.15...
要点
- 除常数项以外,这种回归的假设与最小二乘回归类似
- 它收缩系数接近零(等于零),确实有助于特征选择
- 如何获得 "稀疏" 解(如何进行特征选择)不带正则项等目标函数——平方误差项等值线与L1正则化等值线相交处取得正则化优化目标的解。其交点常出现在坐标轴上。
- 这是一个正则化方法,使用的是L1正则化
- L1正则化问题求解采用坐标轴梯度下降法
案例
加利福利亚房屋数据集
代码语言:javascript复制import numpy as np
import pandas as pd
from sklearn.linear_model import Ridge, LinearRegression, Lasso
from sklearn.model_selection import train_test_split
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 = LinearRegression().fit(Xtrain,Ytrain)
print((reg.coef_*100).tolist())
#岭回归进行拟合
Ridge_ = Ridge(alpha=0.01).fit(Xtrain,Ytrain)
print((Ridge_.coef_*100).tolist())
#Lasso进行拟合
lasso_ = Lasso(alpha=0.01).fit(Xtrain,Ytrain)
print((lasso_.coef_*100).tolist())
#加大正则项系数,观察模型的系数发生了什么变化
Ridge_ = Ridge(alpha=10**4).fit(Xtrain,Ytrain)
print((Ridge_.coef_*100).tolist())
lasso_ = Lasso(alpha=10**4).fit(Xtrain,Ytrain)
print((lasso_.coef_*100).tolist())
# 减小快正则项系数
lasso_ = Lasso(alpha=1).fit(Xtrain,Ytrain)
print((lasso_.coef_*100).tolist())
输出结果
代码语言:javascript复制# 线性回归进行拟合系数
[43.73589305968401, 1.0211268294493672, -10.780721617317635, 62.64338275363785,
5.2161253534123096e-05, -0.33485096463336794, -41.3095937894771, -42.62109536208483]
# 岭回归进行拟合系数
[43.735757206216086, 1.0211292318121772, -10.7804603362518, 62.64202320775749,
5.2170680732407906e-05, -0.33485065170676187, -41.30957143229145, -42.621053889324116]
# Lasso进行拟合系数
[40.10568371834486, 1.0936292607860143, -3.742376361024454, 26.524037834897207,
0.0003525368511503957, -0.3207129394887796, -40.06483047344844, -40.81754399163317]
# 加大正则项系数后的拟合系数
[34.62081517607697, 1.5196170869238694, 0.39686105292101204, 0.9151812510354913, 0.0021739238012248438, -0.34768660148100994, -14.736963474215276, -13.43557610252694]
# 经过拟合之后,系数均变为0,看来10**4对于Lasso来说是一个过于大的取值
[0.0, 0.0, 0.0, -0.0,-0.0, -0.0, -0.0, -0.0]
# Lasson会将系数压缩至0
[14.581141247629423, 0.6209347344423873, 0.0, -0.0, -0.00028065986329010016, -0.0, -0.0, -0.0]
比起岭回归,Lasso所带的L1正则项对于系数的惩罚要重得多,并且它会将系数压缩至0,由此选择那些系数不为0的那些特征,以供后续线性回归建模使用。Lasso的正则化系数
系数更加敏感,因此将其控制在很小的空间范围内变动,以此来寻找最佳的正则化系数。
将系数进行可视化绘
代码语言:javascript复制plt.figure(figsize=(10,6))
plt.plot(range(1,9),(reg.coef_*100).tolist(),color="green",label="LR")
plt.plot(range(1,9),(Ridge_.coef_*100).tolist(),color="blue",label="Ridge")
plt.plot(range(1,9),(lasso_.coef_*100).tolist(),color="red",label="Lasso")
plt.plot(range(1,9),[0]*8,color="grey",linestyle="--")
plt.ylabel("weight 'w'") #横坐标是每一个特征所对应的系数
plt.xlabel("feature 'x'")
plt.legend()
plt.show()
输出结果
最佳的正则化参数
LassoCV选取最佳的正则化参数取值
语法
代码语言:javascript复制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’)
重要参数
eps : float, optional 正则化路径的长度,默认0.001。 n_alphas : int, optional 正则化路径中 的个数,默认100。 alphas : numpy array, optional 需要测试的正则化参数的取值的元祖,默认None。当不输入的时候,自动使用eps和n_alphas 来自动生成带入交叉验证的正则化参数。 cv : int, cross-validation generator or an iterable, optional 交叉验证的次数,默认5折交叉验证。
重要属性
alpha_ : float 调用交叉验证选出来的最佳正则化参数。 alphas_ : numpy array, shape (n_alphas,) 使用正则化路径的长度和路径中 的个数来自动生成的,用来进行交叉验证的正则化参数。 coef_ : array, shape (n_features,) | (n_targets, n_features) 调用最佳正则化参数下建立的模型的系数。 mse_path_ : array, shape (n_alphas, n_folds) 返回所以交叉验证的结果细节。
使用交叉验证的LassoCV
参数与RidgeCV
略有不同,这是因为Lasso
对于
的取值更加敏感,因此LassoCV
对
的取值范围的处理更加细腻,可以通过规定正则化路径(参数eps
)以及路径中
的个数(参数n_alphas
),sklearn
自动计算并生成
的取值以供交叉验证类使用。
另外值得注意的是LassoCV_.mse_path_.mean(axis=1))
。在岭回归中我们的轴向是axis=0
,因其是留一验证,交叉验证结果返回每一个样本在每个
下的交叉验证结果,因此求每个均值需跨行求均值。而在这里返回每一个alpha
取值下,每一折交叉验证的结果,同样要求每个
下的交叉验证均值需跨列求均值,即axis=1
。
正则化路径 假设特征矩阵中有
个特征,其特征向量
。对于每一个
的取值,会有一组参数向量
与其对应,分别是
。这些参数可以被看作是一个
维空间中的一个点。对于不同的
取值即对应多个在
维空间中的点,所有的这些点形成的序列称为正则化路径 regularization path
。
和岭回归的交叉验证类相似,除了进行交叉验证之外,LassoCV也会单独建立模型
。它会先找出最佳的正则化参数, 然后在这个参数下按照模型评估指标进行建模。需要注意的是,LassoCV的模型评估指标选用的是均方误差,而岭回归的模型评估指标是可以自己设定的,并且默认是
。
弹性网络回归
弹性网络(ElasticNet
)是一种使用 L1-范数, L2-范数
作为先验正则项训练的线性回归模型。这种组合允许拟合到一个只有少量参数是非零稀疏的模型,就像 Lasso一样,但是它仍然保持了一些类似于Ridge的正则性质。我们可利用 l1_ratio
参数控制 L1-范数, L2-范数
的凸组合。
最小化目标函数:
例:
代码语言:javascript复制>>> from sklearn.linear_model import ElasticNet
>>> from sklearn.datasets import make_regression
>>> X, y = make_regression(n_features=2, random_state=0)
>>> regr = ElasticNet(random_state=0)
>>> regr.fit(X, y)
ElasticNet(random_state=0)
>>> print(regr.coef_)
[18.83816048 64.55968825]
>>> print(regr.intercept_)
1.451...
>>> print(regr.predict([[0, 0]]))
[1.451...]
ElasticNet是Lasso和Ridge回归技术的混合体。当有多个相关的特征时比较有用。
要点
- 在高度相关变量的情况下,它会产生群体效应
- 选择变量的数目没有限制
- 它可以承受双重收缩
同样ElasticNetCV
类可以通过交叉验证来设置参数 alpha
(
) 和 l1_ratio
(
)