多重共线性是在使用线性回归算法时经常遇到的一个问题。与其他算法如决策树和贝叶斯相比,决策树在每次拆分时只考虑一个变量,具备抵抗多重共线性的功能;而贝叶斯假设变量间相互独立,因此在表面上没有多重共线性的问题。然而,对于回归算法,无论是普通回归、逻辑回归还是生存分析,都需要同时考虑多个预测因素,因此多重共线性是一个不可避免的问题,而且在许多情况下,多重共线性是一个普遍现象。
在构建预测模型时,如何处理多重共线性是一个需要技巧的问题。既不能完全忽视它,也不能一概而论地认为多重共线性应该被消除。
假设有一个包含k个自变量的多元线性回归模型:
$$ y = beta0 + beta1 x1 + beta2 x2 + ... + betak x_k + epsilon $$
其中,误差项是一个期望值为0且服从正态分布的随机变量。利用最小二乘法可以得到参数的估计值:
$$ hat{beta} = (X^T X)^{-1} X^T y $$
这个求解公式唯一的条件是矩阵X是列满秩的,否则将会有无穷多解。当自变量之间存在共线性问题,即部分变量之间存在线性相关时,矩阵X几乎接近不满秩状态,$X^T X$ 几乎接近奇异矩阵,X的最小奇异值会非常小。那么这种情况下,它对解的影响有多大呢?我们从矩阵计算的角度来看这个问题。
在矩阵计算中,扰动分析指的是当输入有较大扰动时,输出也会有相应较大的扰动。对于一个方程或系统,如果输入变化很大且无法控制,那么这个系统的预测结果就会变得不准确。在矩阵计算中,这被称为扰动分析。
【扰动分析定理】设非奇异方阵A满足方程:
$$ Ax = b $$
其准确解为$x^*$,当A存在一个小扰动时,假设$hat{x}$是新方程的解:
$$ (A+Delta A)hat{x} = b $$
可以证明$x^*$的扰动满足:
$$ |x^-hat{x}| leq kappa(A) |Delta A| |x^| $$
可以看到矩阵的条件数越大,扰动就越大,即解的准确性会大大降低。在回归问题中,当存在共线性时,$X^T X$的最小奇异值非常小,因此条件数会变得非常大。这意味着机器学习中的共线性问题实际上就是矩阵计算中的条件数问题。
从实际应用的角度来看,通常如果条件数大于某个阈值(例如1000),则认为存在多重共线性问题。
为了说明多重共线性的影响,我们使用三组不同的数据集进行线性回归实验。
```python import numpy as np from sklearn.linearmodel import LinearRegression from sklearn.modelselection import crossvalscore
X1 = ... # 数据集1 X2 = ... # 数据集2 X3 = ... # 数据集3 y = ... # 标签
print(np.linalg.cond(X1)) print(np.linalg.cond(X2)) print(np.linalg.cond(X3))
lr2 = LinearRegression() lr2.fit(X2[training], y[training])
mse = ((lr2.coef_[:8] - coef0) ** 2).sum() / 8
r2test = lr2.score(X2[~training], y[~training]) cvr2 = crossvalscore(lr2, X2, y, cv=5).mean()
print(mse) print(r2test) print(cvr2)
lr3 = LinearRegression() lr3.fit(X3[training], y[training])
mse = ((lr3.coef_[:8] - coef0) ** 2).sum() / 8
r2test = lr3.score(X3[~training], y[~training]) cvr2 = crossvalscore(lr3, X3, y, cv=5).mean()
print(mse) print(r2test) print(cvr2) ```
结果显示,对于具有多重共线性的数据集X2,其平均测试集准确率为0.932070,拟合的参数MSE为7.697837。相比之下,无多重共线性的数据集X1和X3表现更好。
接下来,我们使用方差膨胀因子(VIF)来进一步评估共线性问题。
```python import matplotlib.pyplot as plt
def calculate_vif(X): vif = np.zeros((X.shape[1], 1)) for i in range(X.shape[1]): tmp = [k for k in range(X.shape[1]) if k != i] clf.fit(X[:, tmp], X[:, i]) vif[i] = 1 / (1 - clf.score(X[:, tmp], X[:, i])) return vif
vif2 = calculatevif(X2) vif3 = calculatevif(X3)
plt.figure() ax = plt.gca() ax.plot(vif2) ax.plot(vif3) plt.xlabel('特征') plt.ylabel('VIF') plt.title('特征的VIF系数') plt.axis('tight') plt.show() ```
从图中可以看出,第0、1、2、3、8、9个特征的VIF值较高,表明它们之间存在较强的共线性关系。
最后,我们使用岭回归来检测共线性问题。
```python import matplotlib.pyplot as plt from sklearn.linear_model import Ridge
plt.figure() nalphas = 20 alphas = np.logspace(-1, 4, num=nalphas) coefs = []
for alpha in alphas: ridge = Ridge(alpha=alpha, fitintercept=False) ridge.fit(X2, y) coefs.append(ridge.coef)
ax = plt.gca() ax.plot(alphas, coefs) ax.setxscale('log') handles, labels = ax.getlegendhandleslabels() plt.legend(labels=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) plt.xlabel('alpha') plt.ylabel('权重') plt.title('岭回归系数作为正则化的函数') plt.axis('tight') plt.show()
print(coefs[0]) ```
结果显示,当alpha取0.1时,岭回归估计的系数中第0、1、2、3、8、9个变量出现了波动,表明它们之间存在一定的共线性。通过观察岭迹,可以考虑剔除其中波动较大的第1、8、9个变量。
此外,Lasso回归也可以用来检测共线性问题,但这里不再展示具体代码。
通过上述方法,我们可以有效地检测和处理多重共线性问题,从而提高模型的准确性和稳定性。