用于稀疏信号的基于 L1 的模型#

本示例比较了三种基于 l1 的回归模型在从稀疏且相关的特征(进一步被高斯加性噪声破坏)获得的合成信号上的性能。

众所周知,当数据维度增长时,如果无关变量与相关变量的相关性不大,则 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")
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 的贝叶斯版本。如果需要,它可以为所有参数(包括误差方差)生成区间估计。当信号具有高斯噪声时,它是一个合适的选项。请参阅示例比较线性贝叶斯回归器,了解ARDRegressionBayesianRidge回归器的比较。

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.020s
ARD r^2 on test data : 0.543

ElasticNet#

ElasticNetLassoRidge 的折中方案,因为它结合了 L1 和 L2 正则化项。正则化的程度由两个超参数 l1_ratioalpha 控制。对于 l1_ratio = 0,惩罚项为纯 L2 正则化,模型等效于 Ridge。类似地,l1_ratio = 1 为纯 L1 正则化,模型等效于 Lasso。对于 0 < l1_ratio < 1,惩罚项是 L1 和 L2 的组合。

如前所述,我们使用 alphal1_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()
Models' coefficients Lasso $R^2$: 0.480, ARD $R^2$: 0.543, ElasticNet $R^2$: 0.636

在本例中,ElasticNet 获得了最佳分数,并捕获了大部分预测特征,但仍然未能找到所有真实成分。请注意,ElasticNetARDRegression 都导致比 Lasso 稀疏性更低的模型。

结论#

Lasso 已知能够有效地恢复稀疏数据,但在高度相关的特征上表现不佳。事实上,如果几个相关的特征对目标有贡献,Lasso 最终只会选择其中一个。对于稀疏但非相关的特征,Lasso 模型更合适。

ElasticNet 在系数上引入了一些稀疏性,并将它们的值缩小到零。因此,在对目标有贡献的相关特征存在的情况下,该模型仍然能够减少它们的权重,而不会将它们精确地设置为零。这导致比纯 Lasso 模型稀疏性更低的模型,并且也可能捕获非预测特征。

ARDRegression 在处理高斯噪声时更好,但仍然无法处理相关特征,并且由于拟合先验而需要更多时间。

参考文献#

脚本总运行时间:(0 分钟 0.506 秒)

相关示例

稠密和稀疏数据上的 Lasso

稠密和稀疏数据上的 Lasso

Lasso、Lasso-LARS 和弹性网路径

Lasso、Lasso-LARS 和弹性网路径

使用多任务 Lasso 进行联合特征选择

使用多任务 Lasso 进行联合特征选择

模型正则化对训练误差和测试误差的影响

模型正则化对训练误差和测试误差的影响

由 Sphinx-Gallery 生成的图库