文章目录
前言
上一小节主要介绍了线性回归算法,并且在文章的末尾提到了它所存在的一些缺陷,事实上在线性回归的基础上稍作改进就可以很好的客服这些缺陷。因此本小节主要介绍线性回归的几个拓展模型:Lasso、Ridge以及ElasticNet(弹性网)
一、算法推导
李航老师的《统计学习方法》中提到,统计学习方法都是由模型
、策略
和算法
构成的,因此本文在算法推导也主要从这三部分进行展开讨论。
1.1 Lasso模型
模型通俗来说就是最后要得到一个什么样的方程。Lasso是线性回归的一个拓展,其模型与线性回归模型一样,最终都是得到一个线性方程:
f
(
x
)
=
w
0
T
x
+
b
=
w
T
x
f(x) = w_{0}^Tx + b=w^Tx
f(x)=w0Tx+b=wTx
其中在训练阶段
f
(
x
)
和
x
f(x)和x
f(x)和x都是已知的,
w
和
b
w和b
w和b是需要估计的参数。
1.2 Lasso策略
有了模型,接下来就需要考虑按照什么样准则学习或选择最优的模型。选择最优模型的原则一般是最小化损失函数,所以策略其实是给出损失函数。Lasso是在线性回归的损失函数基础上,加入了L1惩罚项,可以实现变量筛选(降维),缓解过拟合问题
。
Lasso损失函数:
J
(
w
)
=
1
m
∑
i
=
1
m
(
w
T
x
i
−
y
i
)
2
+
λ
∣
∣
w
∣
∣
1
J(w)=\frac{1}{m} \sum_{i=1}^{m}\left(w^Tx_{i}-y_{i}\right)^{2} + \lambda||w||_{1}
J(w)=m1i=1∑m(wTxi−yi)2+λ∣∣w∣∣1
其中
f
(
x
i
)
f(x_{i})
f(xi)是模型的预测值,
y
i
y_{i}
yi是真实值。
w
w
w的L1范数是
w
w
w每个向量的绝对值之和,从直观上理解,Lasso的损失函数比普通的线性回归的损失函数多了一项约束,而
1
m
∑
i
=
1
m
(
f
(
x
i
)
−
y
i
)
2
\frac{1}{m} \sum_{i=1}^{m}\left(f\left(\boldsymbol{x}_{i}\right)-y_{i}\right)^{2}
m1∑i=1m(f(xi)−yi)2可以看成是一个椭圆,最小化loss就等价于在满足约束下,求
1
m
∑
i
=
1
m
(
f
(
x
i
)
−
y
i
)
2
\frac{1}{m} \sum_{i=1}^{m}\left(f\left(\boldsymbol{x}_{i}\right)-y_{i}\right)^{2}
m1∑i=1m(f(xi)−yi)2的最小值。如下图所示:
红色菱形就是L1范数的约束,最终最优解会落在菱形的顶点处。在上图中,最优解如果落在上面的顶点,会导致w2=0,此时
x
2
x_{2}
x2将对最终的模型不产生作用,从而达到了变量筛选(降维)的效果。
1.3 Lasso算法
算法部分就是通过最小化Loss来求出参数 w w w,Lasso的损失函数中包含有绝对值,无法直接求导,从而经典的梯度下降就不可用了。替代方法有坐标下降法和最小角回归,这里简单介绍一下前一个。坐标下降法其实也是一个迭代算法,它跟梯度下降最大的区别是,梯度下降是沿着负梯度的方向去更新参数,而坐标下降是沿着坐标轴的方向去更新参数,所以坐标下降法每一次都是固定其他k-1个维度,然后更新剩下的一个维度,如此反复,直到所有的坐标都收敛,其实这跟支持向量机中使用的序列最小优化算法(SMO)是一样的想法。由于是迭代法,所以不存在像线性回归那样的显式解。
2.1 Ridge模型
Ridge也是线性回归的一个拓展,其模型与线性回归模型一样,最终都是得到一个线性方程:
f
(
x
)
=
w
0
T
x
+
b
=
w
T
x
f(x) = w_{0}^Tx + b=w^Tx
f(x)=w0Tx+b=wTx
其中在训练阶段
f
(
x
)
和
x
f(x)和x
f(x)和x都是已知的,
w
和
b
w和b
w和b是需要估计的参数。
2.2 Ridge策略
Rigde是在线性回归的损失函数的基础上,加入了L2惩罚项,可以解决共线性问题
。
Ridge损失函数:
J
(
w
)
=
1
m
∑
i
=
1
m
(
w
T
x
i
−
y
i
)
2
+
λ
∣
∣
w
∣
∣
2
2
J(w)=\frac{1}{m} \sum_{i=1}^{m}\left(w^Tx_{i}-y_{i}\right)^{2} + \lambda||w||^{2}_{2}
J(w)=m1i=1∑m(wTxi−yi)2+λ∣∣w∣∣22
其中
f
(
x
i
)
f(x_{i})
f(xi)是模型的预测值,
y
i
y_{i}
yi是真实值。
w
w
w的L2范数是
w
w
w每个向量的平方和之和,从直观上理解,Ridge的损失函数比普通的线性回归的损失函数多了一项约束,而
1
m
∑
i
=
1
m
(
f
(
x
i
)
−
y
i
)
2
\frac{1}{m} \sum_{i=1}^{m}\left(f\left(\boldsymbol{x}_{i}\right)-y_{i}\right)^{2}
m1∑i=1m(f(xi)−yi)2可以看成是一个椭圆,最小化loss就等价于在满足约束下,求
1
m
∑
i
=
1
m
(
f
(
x
i
)
−
y
i
)
2
\frac{1}{m} \sum_{i=1}^{m}\left(f\left(\boldsymbol{x}_{i}\right)-y_{i}\right)^{2}
m1∑i=1m(f(xi)−yi)2的最小值。如下图所示:
红色椭圆形就是L2范数的约束,最终最优解不一定落在顶点处。所以L2不具备变量筛选的功能,但是为什么可以解决共线性问题呢?
2.3 Ridge算法
上一小节我们推导出线性回归参数的最优解
w
∗
=
(
X
T
X
)
−
1
X
T
y
w^{*}=\left(X^{T} X\right)^{-1} X^{T} y
w∗=(XTX)−1XTy
当变量之间存在共线性的时候,
X
T
X
X^TX
XTX就不是满秩的,换言之
X
T
X
X^TX
XTX不可逆。但是我们通过对Ridge的
J
(
w
)
J(w)
J(w)求导,并令其等于0,可以发现,通过加入L2惩罚项
w
∗
=
(
X
T
X
+
λ
I
)
−
1
X
T
y
w^{*}=\left(X^{T} X+\lambda I\right)^{-1} X^{T} y
w∗=(XTX+λI)−1XTy
其中
I
I
I是一个单位矩阵,可以看到Ridge跟普通线性回归的区别主要在于求逆的时候多加了一个
λ
I
\lambda I
λI矩阵,加入这个之后会使得
(
X
T
X
+
λ
I
)
(X^{T}X+\lambda I)
(XTX+λI)变得可逆,从而解决了共线性带来矩阵不可逆的问题。另一方面,
λ
I
\lambda I
λI是一个单位矩阵乘以一个实数,所以它看起来像一条脊,因此叫做Ridge回归(Rigde的翻译叫岭、脊)。
**ElasticNet(弹性网)**是Lasso和Ridge的结合,它在线性回归的基础上同时加入了L1和L2惩罚项,同时具备变量筛选和解决共线性的功能,由于它的模型、策略和算法与Lasso以及Ridge差不多,这里就不再花篇幅进行赘述了。
二、应用场景
Lasso作为线性回归的拓展,并且具备特征选择的能力,在现阶段经济学中应用的比较多。
Ridge主要用于解决变量之间的共线性问题,因此当数据之间的相关性不叫明显的时候,可以尝试使用Ridge回归。
三、代码实现
这里主要使用sklearn这个库来实现机器学习算法。
1.导入相关库
from sklearn.linear_model import Lasso
from sklearn.model_selection import train_test_split
from sklearn.model_selection import cross_val_score
from sklearn.datasets import fetch_california_housing
from sklearn.metrics import mean_squared_error
import pandas as pd
import numpy as np
2.读取样例数据
data = fetch_california_housing() # 获取样例数据,这里的数据是加利福利亚的放假数据
X = pd.DataFrame(data.data,columns=data.feature_names)
y = data.target
"""
MedInc:该街区住户的收入中位数
HouseAge:该街区房屋使用年代的中位数
AveRooms:该街区平均的房间数目
AveBedrms:该街区平均的卧室数目
Population:街区人口
AveOccup:平均入住率
Latitude:街区的纬度
Longitude:街区的经度
"""
3.划分训练集和测试集
X_train, X_test, y_train, y_test =train_test_split(X,y,test_size=0.3,random_state=0)
4.建立模型
lasso_ = Lasso(alpha=0.05).fit(X_train,y_train)
ridge_ = Ridge(alpha=0.05).fit(X_train,y_train)
elast_ = ElasticNet(alpha=0.05,l1_ratio=0.5).fit(X_train,y_train)
las_pred = lasso_.predict(X_test)
rid_pred = ridge_.predict(X_test)
ela_pred = elast_.predict(X_test)
最终模型参数
为了对比普通线性回归和Lasso的结果,我这里把上一小结的结果粘贴了过来,可以看到相比于普通的线性回归,Lasso得到的系数中会有部分的系数等于0,这说明了Lasso起到变量筛选的效果。
普通线性回归得到的系数
Lasso得到的系数
Ridge得到的系数
ElasticNet得到的系数
5.评估模型
计算均方误差MSE
print('Lasso MSE:%.3f'%(mean_squared_error(y_test,las_pred)))
print('Rigde MSE:%.3f'%(mean_squared_error(y_test,rid_pred)))
print('ElasticNet MSE:%.3f'%(mean_squared_error(y_test,ela_pred)))
Lasso MSE:0.574
Rigde MSE:0.543
ElasticNet MSE:0.565
其实上面用的方法是留出法,我们也可以使用交叉验证法来计算模型误差。这样就把划分训练集和测试集、建立模型以及评估模型这几步合并在一起。
las_mse = -np.mean(cross_val_score(Lasso(alpha=0.01),X,y,cv=10,scoring='neg_mean_squared_error'))
print('Lass MSE:%.3f'%(las_mse))
rid_mse = -np.mean(cross_val_score(Ridge(alpha=0.01),X,y,cv=10,scoring='neg_mean_squared_error'))
print('Lass MSE:%.3f'%(rid_mse))
ela_mse = -np.mean(cross_val_score(ElasticNet(alpha=0.05,l1_ratio=0.5),X,y,cv=10,scoring='neg_mean_squared_error'))
print('Lass MSE:%.3f'%(ela_mse))
Lass MSE:0.557
Lass MSE:0.551
Lass MSE:0.575
但值得注意的是,Lasso和ElasticNet模型的测试误差并没有比普通的线性回归低,这主要是因为这里的特征个数较少,并且每个变量都对模型起作用,所以Lasso强行把系数压缩至0并不是一种很合适的做法。Lasso更适合与那种特征数量很多的应用场景。同时也说明了模型并不是越复杂越好,没有最好的模型,只有最合适的模型,有时候拿神经网络硬train一发的效果并不就见得比简单的线性回归好。
四、优缺点
1.优点
Lasso
(1)可解释性强,可以清晰的知道每个特征对模型的影响
(2)简单快速
Ridge
(1)可解释性强,可以解决共线性问题
(2)加入L2惩罚项,一定程度上缓解了过拟合的问题
2.缺点
Lasso
(1)只适用于数据之间的线性关系,但现实生活中大多数特征之间的关系是非线性的。
(2)需要满足一些假设,特征之间不能存在共线性
Ridge
(1)不具备变量筛选功能
(2)本质上仍然是一个线性回归,无法解决非线性问题