上篇文章《简单而强大的线性回归详解》(点击跳转)详细介绍了线性回归分析方程、损失方程及求解、模型评估指标等内容,其中在推导多元线性回归使用最小二乘法的求解原理时,对损失函数求导得到参数向量 的方程式
上式中有解,即能够得到最后一步的前提条件是
存在逆矩阵,而逆矩阵存在的充分必要条件是特征矩阵不存在多重共线性。
本文将详细介绍线性回归中多重共线性问题,以及一种线性回归的缩减
(shrinkage)方法 ----岭回归
(Ridge Regression),并对其进行了Python实现
多重共线性
多重共线性是指线性回归模型中的解释变量之间由于存在精确相关关系或高度相关关系而使模型估计失真或难以估计准确。
从线性等式理解,对于
个变量,如果存在常数
使得如下公式,可近似表示为
那么通常称这
个变量存在多重共线性。
下面从特征矩阵的角度出发,深入探究多重共线性将究竟如何影响对损失函数求解,以便深刻理解改进原理。
逆矩阵存在的充要条件
逆矩阵计算公式
其中
是伴随矩阵,其存在没有限制条件,当然也不会影响逆矩阵的存在性。而矩阵的行列式
存在于分母上,其值不能为零。由此得到,逆矩阵存在的充分必要条件是:矩阵的行列式不能为0。
即
存在逆矩阵的充要条件为
不能为0。这是使用最小二乘法来求解线性回归的核心条件之一。
行列式与矩阵
矩阵
(matrix)是一组数按照一定方式排列的数表,它不能从整体上被看成一个数(只有一个数的1阶矩阵除外),记为
在线性代数中,行列式
(determinant)是一个函数,它将每个
矩阵
对应到一个纯量(scalar),简单讲即是行列式是这一组数按照某种运算法则计算出的一个数,记为
或
行列式不为零的充要条件
假设特征矩阵
的结构为
,则
一般行列式计算不会通过展开的方式,而是通过行列初等行变换/列变换将其整合成一个梯形的行列式
由于梯形行列式下半部分为0,整个矩阵的行列式其实就是梯形行列式对角线上的元素相乘。
由此可见,如果对角线上的任一个元素为0,则行列式结果即为0。反之,如果对角线上的任一元素均不为0,则行列式不为0。
矩阵满秩是矩阵的行列式不为0的充分必要条件。
满秩矩阵 一个结构为
的矩阵
,若
转换为梯形矩阵后,没有任何全为0的行或者全为0的列,则称
为满秩矩阵。简单来说,只要对角线上没有一个元素为0,则这个矩阵中绝对不可能存在全为0的行或列。
矩阵满秩的充要条件
精确相关关系
即完全相关,矩阵两行之间或两列之间存在完全线性关系,这种精确相关关系会使得矩阵的行列式为0,则矩阵的逆矩阵不存在。在最小二乘法中,如果矩阵
中存在这种精确相关关系,则逆矩阵不存在,线性回归无法使用最小二乘法求出结果
即当
则会发生除零错误 。
高度相关关系
即不完全相关,这种高度相关关系下,矩阵的行列式不为0,但是一个非常接近0的数,矩阵的逆是存在的,但接近无限大,直接影响参数向量求解。
这种情况下,结果参数向量
无穷大,直接影响模型结果,造成模型偏差大或不可用。
精确相关关系和高度相关关系并称"多重共线性"。
矩阵行与行或列于列之间相互独立,其矩阵的行列式经初等变换后的对角线上没有任何元素特别接近于0,因此矩阵求得的参数向量不会对模型产生影响,对拟合结果也是较理想的。
由此可见,一个矩阵如果要满秩,则要求矩阵中每个向量之间不能存在多重共线性,这也构成了线性回归算法对于特征矩阵的要求。
多重共线性与相关性 多重共线性(Multicollinearity)是一种统计现象,是指线性模型中的特征(解释变量)之间由于存在精确相关关系或高度相关关系, 多重共线性的存在会使模型无法建立,或者估计失真。多重共线性使用指标方差膨胀因子(variance inflation factor,VIF)来进行衡量(
from statsmodels.stats.outliers_influence import variance_inflation_factor
), 通常当我们提到"共线性",都特指多重共线性。 相关性(Correlation)是衡量两个或多个变量一起波动的程度的指标,它可以是正的,负的或者0。一般而言,变量之间具有相关性,通常是指线性相关性,线性相关一般由皮尔逊相关系数进行衡量,非线性相关可以使用斯皮尔曼相关系数或者互信息法进行衡量。此部分可参见数据探索分析(点击跳转)。
多重共线性对回归模型的影响
回归系数的估计值方差变大,回归系数的置信度变宽,估计的精确性大幅度降低,使得估计值稳定性变差。
会使得一些回归系数通不过显著性检验,回归系数的正负号也可能出现倒置,使得回归方程无法得到合理的解释,直接影响最小二乘法的计算结果。
多重共线性如果存在,则线性回归就无法使用最小二乘法来进行求解,或者求解就会出现偏差。不能存在多重共线性,不代表不能存在相关性——机器学习不要求特征之间必须独立,必须不相关,只要不是高度相关或者精确相关就好。
改进线性回归处理多重共线性
处理多重共线性方法有多种,其中最直接的方法是手动移除共线性的变量。具体做法是先对数据进行相关分析,若两个特征的相关系数大于某特定值(一般为0.7),则手动移除其中一个特征,再继续做回归分析。这种做法会导致估计结果产生偏差,会引起遗漏变量问题。而且有时数据特征本来就很少,或并不想直接删除特征,此时可考虑其他更加有效的方法。
改进线性回归即是当前解决多重共线性问题的最有效的方法。
岭回归
岭回归分析(Ridge Regression)是一种改良的最小二乘法,其通过放弃最小二乘法的无偏性,以损失部分信息为代价来寻找效果稍差但回归系数更符合实际情况的模型方程。该模型求解的回归模型的损失函数为线性最小二乘函数,正则化采用l2-范数
。也称为岭回归(Ridge Regression)或吉洪诺夫正则化(Tikhonov regularization)。岭回归与套索回归(Lasso Regression)两个算法不是为了提升模型表现,而是为了修复漏洞而设计的。(Lasso回归将在下一篇章介绍)
岭回归原理和逻辑是将求解
的过程转化为一个带条件的最优化问题,然后再用最小二乘法求解。岭回归在多元线性回归的损失函数上加上了正则项,表达为系数
的L2-范式
(即系数
的平方项)乘以正则化系数
。
最小化目标函数:
假设我们的特征矩阵结构为(m,n)
,系数
的结构是(1,n)
,
是构为(n,n)
单位矩阵则
最终得到
从而转化为只需
存在逆矩阵,就能求解出参数向量
。
假设原本的特征矩阵存在共线性,即非满秩矩阵
最后得到的这个行列式还是一个梯形行列式,但已经不存在全0行或者全0列了,除非以下两种情况,否则矩阵
永远都是满秩。
(1)
(2) 原本的矩阵
存在对角线上元素为
,且其他元素都为0的行或者列
以上两种情况,在sklearn
中都可以轻松应对:自由控制
;当出现无法求解时,更换
取值即可。则可认为
是存在的
其中
通过调整正则化系数
大小,来避免"精确相关关系"及"高度相关关系"带来的影响。如此,多重共线性就被控制住了:最小二乘法一定有解,并且这个解可以通过
来进行调节,以确保不会偏离太多。当然了,
挤占了
中由原始的特征矩阵贡献的空间,因此
如果太大,也会导致的估计出现较大的偏移,无法正确拟合数据的真实面貌。我们在使用中,需要找出
让模型效果变好的最佳取值。
语法:
代码语言:javascript复制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)
重要参数:
alpha : {float, ndarray of shape (n_targets,)}, default=1.0 正则化参数,必须是正浮点数。正则化改善了问题的条件,降低了估计的方差。值越大表示正则化惩罚越强。对应于其它线性模型中的 C−1,如LogisticRegression或LinearSVC。如果传递了数组,则惩罚特定目标。
例:
代码语言:javascript复制>>> from sklearn.linear_model import Ridge
>>> import numpy as np
>>> n_samples, n_features = 10, 5
>>> rng = np.random.RandomState(0)
>>> y = rng.randn(n_samples)
>>> X = rng.randn(n_samples, n_features)
>>> clf = Ridge(alpha=1.0)
>>> clf.fit(X, y)
Ridge()
虽然岭回归和Lasso不是设计来提升模型表现,而是专注于解决多重共线性问题的,但当
在一定范围内变动的时候,消除多重共线性也许能够一定程度上提高模型的泛化能力。
案例
波士顿房价数据集中看岭回归处理多重共线性。
代码语言:javascript复制from sklearn.datasets import load_boston
from sklearn.model_selection import cross_val_score
import matplotlib.pyplot as plt
from sklearn.linear_model import Ridge
import numpy as np
from sklearn.linear_model import LinearRegression # 线性回归模型
from sklearn.model_selection import train_test_split# 交叉验证
X = load_boston().data
y = load_boston().target
Xtrain, Xtest, Ytrain, Ytest = train_test_split(X, y, test_size=0.3,random_state=420)
#先查看方差的变化,再看R2的变化
alpha_range = np.arange(1,1001,100)
ridge_var, lr_var, ridge_mean, lr_mean = [], [], [], []
plt.figure(figsize=(16,10))
plt.style.use('seaborn')
for alpha in alpha_range:
ridge = Ridge(alpha=alpha)
linear = LinearRegression()
score_rig = cross_val_score(ridge,X,y,cv=5,scoring="r2")
score_linear = cross_val_score(linear,X,y,cv=5,scoring="r2")
ridge_var.append(score_rig.var())
lr_var.append(score_linear.var())
ridge_mean.append(score_rig.mean())
lr_mean.append(score_linear.mean())
name = ['variance', 'mean']
for i,j in enumerate([[ridge_var, lr_var], [ridge_mean, lr_mean]]):
plt.subplot(2,2,i 1)
plt.plot(alpha_range,j[0],color="green",label="Ridge")
plt.plot(alpha_range,j[1],color="blue",label="LR")
plt.title(f"cross_val_score {name[i]} for different alpha values")
plt.xlabel("alpha")
plt.ylabel(f"{name[i]}")
plt.legend()
#细化学习曲线
alpha_range = np.arange(100,300,10)
ridge_score, lr_score = [], []
for alpha in alpha_range:
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_score.append(regs)
lr_score.append(linears)
for i,j in enumerate([[ridge_score,"Ridge", "green"], [lr_score, "LR", 'blue']]):
plt.subplot(2,2,i 3)
plt.plot(alpha_range,j[0],color=j[2],label=j[1])
plt.title("cross_val_score mean for different alpha values")
plt.xlabel("alpha")
plt.ylabel("Mean")
plt.legend()
plt.suptitle("cross_val_score of the LR and Ridge for different alpha values", y=0.95, fontsize=18)
plt.show()
输出结果
可以发现,比起加利佛尼亚房屋价值数据集,波士顿房价数据集的方差降低明显,偏差也降低明显,可见使用岭回归还是起到了一定的作用,模型的泛化能力是有可能会上升的。
选择最佳正则化系数
使用交叉验证类 RidgeCV
来选择最佳的正则化系数。
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)
重要参数
alphas : numpy array of shape [n_alphas] 需要测试的正则化参数的取值的元祖 scoring : string, callable or None, optional, default: None 用来进行交叉验证的模型评估指标,默认是
,可自行调整
store_cv_values : boolean, default=False
是否保存每次交叉验证的结果,默认False
cv : int, cross-validation generator or an iterable, optional
交叉验证的模式,默认是None
,表示默认进行留一交叉验证可以输入Kfold
对象和StratifiedKFold
对象来进行交叉验证 注意,仅仅当为None
时,每次交叉验证的结果才可以被保存下来当cv
有值存在(不是None
)时,store_cv_values
无法被设定为True
重要属性
alpha_ : float 查看交叉验证选中的
alpha
cv_values_ : array, shape = [n_samples, n_alphas] or shape = [n_samples, n_targets, n_alphas], optional 调用所有交叉验证的结果,只有当store_cv_values=True
的时候才能够调用,因此返回的结构是(n_samples, n_alphas)
重要接口
score 调用
Ridge
类不进行交叉验证的情况下返回的
例
代码语言:javascript复制>>> import numpy as np
>>> from sklearn import linear_model
>>> reg = linear_model.RidgeCV(alphas=np.logspace(-6, 6, 13))
>>> reg.fit([[0, 0], [0, 0], [1, 1]], [0, .1, 1])
RidgeCV(alphas=array([1.e-06, 1.e-05, 1.e-04, 1.e-03, 1.e-02, 1.e-01, 1.e 00, 1.e 01,
1.e 02, 1.e 03, 1.e 04, 1.e 05, 1.e 06]))
>>> reg.alpha_
0.01
另一种方式是使用岭迹图来判断正则项参数的最佳取值,了解即可,实际工作中不建议使用。
代码语言:javascript复制import numpy as np
import matplotlib.pyplot as plt
from sklearn import linear_model
#创造10*10的希尔伯特矩阵
X = 1. / (np.arange(1, 11) np.arange(0, 10)[:, np.newaxis])
y = np.ones(10)
#计算横坐标
n_alphas = 200
alphas = np.logspace(-10, -2, n_alphas)
#建模,获取每一个正则化取值下的系数组合
coefs = []
for a in alphas:
ridge = linear_model.Ridge(alpha=a, fit_intercept=False)
ridge.fit(X, y)
coefs.append(ridge.coef_)
#绘图展示结果
ax = plt.gca()
ax.plot(alphas, coefs)
ax.set_xscale('log')
ax.set_xlim(ax.get_xlim()[::-1]) #将横坐标逆转
plt.xlabel('正则化参数alpha')
plt.ylabel('系数w')
plt.title('岭回归下的岭迹图')
plt.axis('tight')
plt.show()
岭迹图 以正则化参数即岭参数
为横坐标,线性模型求解的系数即岭系数
为纵坐标的图像,其中每一条彩色的线都是一个岭系数
。其目标是建立岭参数
与岭系数
之间的直接关系,以此来观察岭参数的变化如何影响了岭系数
的拟合。
岭迹图认为,线条交叉越多,则说明特征之间的多重共线性越高。我们应该选择系数较为平稳的喇叭口所对应的
取值作为最佳的正则化参数的取值。不存在奇异性时,岭迹图应稳定的逐渐趋向于0。
希伯尔特矩阵
岭回归分析是一种用于存在多重共线性(自变量高度相关)数据的技术。在线性回归基础上增加L2
正则化项 。
除常数项以外,这种回归的假设与最小二乘回归类似;它收缩了相关系数的值,但没有达到零,这表明它没有特征选择功能,这是一个正则化方法,并且使用的是L2
正则化。
转载请联系笔者
点赞关注转发在看
支持笔者,关注持续获取最新数据分析相关干货。