注意
转到末尾下载完整示例代码,或通过JupyterLite或Binder在浏览器中运行此示例
稀疏信号的L1模型#
本示例比较了三个基于l1的回归模型,这些模型在由稀疏且相关特征生成的合成信号上进行,这些特征还受到附加高斯噪声的污染
一个Lasso;
一个Elastic-Net。
众所周知,当数据维度增加时,Lasso估计值会接近模型选择估计值,前提是无关变量与相关变量没有太强的相关性。在存在相关特征的情况下,Lasso本身无法选择正确的稀疏模式[1]。
在此,我们比较了这三个模型在\(R^2\)分数、拟合时间以及与真实值相比的估计系数稀疏性方面的性能。
# Authors: The scikit-learn developers
# SPDX-License-Identifier: BSD-3-Clause
生成合成数据集#
我们生成了一个样本数量少于总特征数量的数据集。这导致了一个欠定系统,即解不唯一,因此我们无法单独应用普通最小二乘法。正则化向目标函数引入了一个惩罚项,这改变了优化问题,并有助于缓解系统的欠定性质。
目标y
是正弦信号的线性组合,符号交替。 X
中100个频率中只有最低的10个用于生成y
,其余特征不提供信息。这导致了一个高维稀疏特征空间,其中需要一定程度的l1惩罚。
import numpy as np
rng = np.random.RandomState(0)
n_samples, n_features, n_informative = 50, 100, 10
time_step = np.linspace(-2, 2, n_samples)
freqs = 2 * np.pi * np.sort(rng.rand(n_features)) / 0.01
X = np.zeros((n_samples, n_features))
for i in range(n_features):
X[:, i] = np.sin(freqs[i] * time_step)
idx = np.arange(n_features)
true_coef = (-1) ** idx * np.exp(-idx / 10)
true_coef[n_informative:] = 0 # sparsify coef
y = np.dot(X, true_coef)
一些信息性特征具有接近的频率,以诱导(反)相关性。
freqs[:n_informative]
array([ 2.9502547 , 11.8059798 , 12.63394388, 12.70359377, 24.62241605,
37.84077985, 40.30506066, 44.63327171, 54.74495357, 59.02456369])
使用numpy.random.random_sample
引入随机相位,并向特征和目标添加一些高斯噪声(由numpy.random.normal
实现)。
for i in range(n_features):
X[:, i] = np.sin(freqs[i] * time_step + 2 * (rng.random_sample() - 0.5))
X[:, i] += 0.2 * rng.normal(0, 1, n_samples)
y += 0.2 * rng.normal(0, 1, n_samples)
这种稀疏、有噪声和相关的特征可以从例如监测某些环境变量的传感器节点中获得,因为它们通常根据其位置记录相似的值(空间相关性)。我们可以可视化目标。
import matplotlib.pyplot as plt
plt.plot(time_step, y)
plt.ylabel("target signal")
plt.xlabel("time")
_ = plt.title("Superposition of sinusoidal signals")

为简单起见,我们将数据分成训练集和测试集。实际上,在处理具有时间关系的数据时,应使用TimeSeriesSplit
交叉验证来估计测试分数的方差。这里我们将shuffle="False"
,因为我们不能使用在测试数据之后出现的训练数据。
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.5, shuffle=False)
接下来,我们将计算三个基于l1的模型在拟合优度\(R^2\)分数和拟合时间方面的性能。然后,我们将绘制一个图来比较估计系数与真实系数的稀疏性,最后分析之前的结果。
Lasso#
在此示例中,我们演示了一个Lasso
模型,其正则化参数alpha
具有固定值。实际上,应通过将TimeSeriesSplit
交叉验证策略传递给LassoCV
来选择最佳参数alpha
。为了使示例简单且执行速度快,我们在此直接设置alpha
的最优值。
from time import time
from sklearn.linear_model import Lasso
from sklearn.metrics import r2_score
t0 = time()
lasso = Lasso(alpha=0.14).fit(X_train, y_train)
print(f"Lasso fit done in {(time() - t0):.3f}s")
y_pred_lasso = lasso.predict(X_test)
r2_score_lasso = r2_score(y_test, y_pred_lasso)
print(f"Lasso r^2 on test data : {r2_score_lasso:.3f}")
Lasso fit done in 0.001s
Lasso r^2 on test data : 0.480
自动相关性确定 (ARD)#
ARD 回归是 Lasso 的贝叶斯版本。如果需要,它可以为所有参数(包括误差方差)生成区间估计。当信号存在高斯噪声时,它是一个合适的选择。有关ARDRegression
和BayesianRidge
回归器的比较,请参见示例比较线性贝叶斯回归器。
from sklearn.linear_model import ARDRegression
t0 = time()
ard = ARDRegression().fit(X_train, y_train)
print(f"ARD fit done in {(time() - t0):.3f}s")
y_pred_ard = ard.predict(X_test)
r2_score_ard = r2_score(y_test, y_pred_ard)
print(f"ARD r^2 on test data : {r2_score_ard:.3f}")
ARD fit done in 0.045s
ARD r^2 on test data : 0.543
ElasticNet#
ElasticNet
是Lasso和Ridge之间的一种折衷,因为它结合了L1和L2惩罚。正则化量由两个超参数l1_ratio
和alpha
控制。当l1_ratio = 0
时,惩罚是纯L2,模型等同于Ridge
。类似地,当l1_ratio = 1
时,惩罚是纯L1,模型等同于Lasso
。当0 < l1_ratio < 1
时,惩罚是L1和L2的组合。
如前所述,我们使用alpha
和l1_ratio
的固定值训练模型。为了选择它们的最佳值,我们使用了ElasticNetCV
,此处未显示以保持示例的简洁性。
from sklearn.linear_model import ElasticNet
t0 = time()
enet = ElasticNet(alpha=0.08, l1_ratio=0.5).fit(X_train, y_train)
print(f"ElasticNet fit done in {(time() - t0):.3f}s")
y_pred_enet = enet.predict(X_test)
r2_score_enet = r2_score(y_test, y_pred_enet)
print(f"ElasticNet r^2 on test data : {r2_score_enet:.3f}")
ElasticNet fit done in 0.001s
ElasticNet r^2 on test data : 0.636
结果的绘图和分析#
在本节中,我们使用热图可视化相应线性模型的真实系数和估计系数的稀疏性。
import matplotlib.pyplot as plt
import pandas as pd
import seaborn as sns
from matplotlib.colors import SymLogNorm
df = pd.DataFrame(
{
"True coefficients": true_coef,
"Lasso": lasso.coef_,
"ARDRegression": ard.coef_,
"ElasticNet": enet.coef_,
}
)
plt.figure(figsize=(10, 6))
ax = sns.heatmap(
df.T,
norm=SymLogNorm(linthresh=10e-4, vmin=-1, vmax=1),
cbar_kws={"label": "coefficients' values"},
cmap="seismic_r",
)
plt.ylabel("linear model")
plt.xlabel("coefficients")
plt.title(
f"Models' coefficients\nLasso $R^2$: {r2_score_lasso:.3f}, "
f"ARD $R^2$: {r2_score_ard:.3f}, "
f"ElasticNet $R^2$: {r2_score_enet:.3f}"
)
plt.tight_layout()

在本示例中,ElasticNet
产生了最佳分数并捕获了大部分预测性特征,但仍未能找到所有真实组件。请注意,ElasticNet
和ARDRegression
都产生了比Lasso
更不稀疏的模型。
结论#
Lasso
以有效恢复稀疏数据而闻名,但对于高度相关特征的表现不佳。实际上,如果几个相关特征对目标有贡献,Lasso
最终只会选择其中一个。在稀疏但非相关特征的情况下,Lasso
模型会更适用。
ElasticNet
对系数引入了一些稀疏性并将其值收缩到零。因此,在存在对目标有贡献的相关特征的情况下,模型仍然能够减少它们的权重而不会将其精确地设置为零。这导致一个比纯Lasso
更不稀疏的模型,并且也可能捕获非预测性特征。
ARDRegression
在处理高斯噪声时表现更好,但仍然无法处理相关特征,并且由于拟合先验需要更多时间。
参考文献#
脚本总运行时间:(0分钟 0.446秒)
相关示例