Lasso 模型选择:AIC-BIC / 交叉验证#

本示例重点介绍 Lasso 模型的模型选择,该模型是具有 L1 正则化的线性模型,用于回归问题。

实际上,可以使用多种策略来选择正则化参数的值:通过交叉验证或使用信息准则,即 AIC 或 BIC。

在下文中,我们将详细讨论不同的策略。

# Author: Olivier Grisel
#         Gael Varoquaux
#         Alexandre Gramfort
#         Guillaume Lemaitre
# License: BSD 3 clause

数据集#

在本例中,我们将使用糖尿病数据集。

from sklearn.datasets import load_diabetes

X, y = load_diabetes(return_X_y=True, as_frame=True)
X.head()
年龄 性别 bmi 血压 s1 s2 s3 s4 s5 s6
0 0.038076 0.050680 0.061696 0.021872 -0.044223 -0.034821 -0.043401 -0.002592 0.019907 -0.017646
1 -0.001882 -0.044642 -0.051474 -0.026328 -0.008449 -0.019163 0.074412 -0.039493 -0.068332 -0.092204
2 0.085299 0.050680 0.044451 -0.005670 -0.045599 -0.034194 -0.032356 -0.002592 0.002861 -0.025930
3 -0.089063 -0.044642 -0.011595 -0.036656 0.012191 0.024991 -0.036038 0.034309 0.022688 -0.009362
4 0.005383 -0.044642 -0.036385 0.021872 0.003935 0.015596 0.008142 -0.002592 -0.031988 -0.046641


此外,我们向原始数据中添加了一些随机特征,以更好地说明 Lasso 模型执行的特征选择。

import numpy as np
import pandas as pd

rng = np.random.RandomState(42)
n_random_features = 14
X_random = pd.DataFrame(
    rng.randn(X.shape[0], n_random_features),
    columns=[f"random_{i:02d}" for i in range(n_random_features)],
)
X = pd.concat([X, X_random], axis=1)
# Show only a subset of the columns
X[X.columns[::3]].head()
年龄 血压 s3 s6 随机_02 随机_05 随机_08 随机_11
0 0.038076 0.021872 -0.043401 -0.017646 0.647689 -0.234137 -0.469474 -0.465730
1 -0.001882 -0.026328 0.074412 -0.092204 -1.012831 -1.412304 0.067528 0.110923
2 0.085299 -0.005670 -0.032356 -0.025930 -0.601707 -1.057711 0.208864 0.196861
3 -0.089063 -0.036656 -0.036038 -0.009362 -1.478522 1.057122 0.324084 0.611676
4 0.005383 0.021872 0.008142 -0.046641 0.331263 -0.185659 0.812526 1.003533


通过信息准则选择 Lasso#

LassoLarsIC 提供了一个 Lasso 估计器,它使用 Akaike 信息准则 (AIC) 或贝叶斯信息准则 (BIC) 来选择正则化参数 alpha 的最佳值。

在拟合模型之前,我们将使用 StandardScaler 对数据进行标准化。此外,我们将测量拟合和调整超参数 alpha 的时间,以便与交叉验证策略进行比较。

我们将首先使用 AIC 准则拟合 Lasso 模型。

import time

from sklearn.linear_model import LassoLarsIC
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler

start_time = time.time()
lasso_lars_ic = make_pipeline(StandardScaler(), LassoLarsIC(criterion="aic")).fit(X, y)
fit_time = time.time() - start_time

我们存储在 fit 期间使用的每个 alpha 值的 AIC 指标。

results = pd.DataFrame(
    {
        "alphas": lasso_lars_ic[-1].alphas_,
        "AIC criterion": lasso_lars_ic[-1].criterion_,
    }
).set_index("alphas")
alpha_aic = lasso_lars_ic[-1].alpha_

现在,我们使用 BIC 准则执行相同的分析。

lasso_lars_ic.set_params(lassolarsic__criterion="bic").fit(X, y)
results["BIC criterion"] = lasso_lars_ic[-1].criterion_
alpha_bic = lasso_lars_ic[-1].alpha_

我们可以检查哪个 alpha 值导致最小的 AIC 和 BIC。

def highlight_min(x):
    x_min = x.min()
    return ["font-weight: bold" if v == x_min else "" for v in x]


results.style.apply(highlight_min)
  AIC 准则 BIC 准则
alphas    
45.160030 5244.764779 5244.764779
42.300343 5208.250639 5212.341949
21.542052 4928.018900 4936.201520
15.034077 4869.678359 4881.952289
6.189631 4815.437362 4831.802601
5.329616 4810.423641 4830.880191
4.306012 4803.573491 4828.121351
4.124225 4804.126502 4832.765671
3.820705 4803.621645 4836.352124
3.750389 4805.012521 4841.834310
3.570655 4805.290075 4846.203174
3.550213 4807.075887 4852.080295
3.358295 4806.878051 4855.973770
3.259297 4807.706026 4860.893055
3.237703 4809.440409 4866.718747
2.850031 4805.989341 4867.358990
2.384338 4801.702266 4867.163224
2.296575 4802.594754 4872.147022
2.031555 4801.236720 4874.880298
1.618263 4798.484109 4876.218997
1.526599 4799.543841 4881.370039
0.586798 4794.238744 4880.156252
0.445978 4795.589715 4885.598533
0.259031 4796.966981 4891.067109
0.032179 4796.662409 4894.853846
0.019069 4794.652739 4888.752867
0.000000 4796.626286 4894.817724


最后,我们可以绘制不同 alpha 值的 AIC 和 BIC 值。图中的垂直线对应于为每个准则选择的 alpha。选择的 alpha 对应于 AIC 或 BIC 准则的最小值。

ax = results.plot()
ax.vlines(
    alpha_aic,
    results["AIC criterion"].min(),
    results["AIC criterion"].max(),
    label="alpha: AIC estimate",
    linestyles="--",
    color="tab:blue",
)
ax.vlines(
    alpha_bic,
    results["BIC criterion"].min(),
    results["BIC criterion"].max(),
    label="alpha: BIC estimate",
    linestyle="--",
    color="tab:orange",
)
ax.set_xlabel(r"$\alpha$")
ax.set_ylabel("criterion")
ax.set_xscale("log")
ax.legend()
_ = ax.set_title(
    f"Information-criterion for model selection (training time {fit_time:.2f}s)"
)
Information-criterion for model selection (training time 0.01s)

使用信息准则进行模型选择非常快。它依赖于对提供给 fit 的样本内集计算准则。这两个准则都根据训练集误差估计模型泛化误差,并惩罚这种过于乐观的误差。但是,这种惩罚依赖于对自由度和噪声方差的正确估计。两者都是针对大样本(渐近结果)推导出来的,并假设模型是正确的,即数据实际上是由该模型生成的。

当问题条件不佳(特征多于样本)时,这些模型也往往会失效。然后需要提供噪声方差的估计值。

通过交叉验证选择 Lasso#

Lasso 估计器可以使用不同的求解器来实现:坐标下降和最小角回归。它们在执行速度和数值误差来源方面有所不同。

在 scikit-learn 中,可以使用两种不同的估计器进行集成交叉验证:LassoCVLassoLarsCV,它们分别使用坐标下降和最小角回归来解决问题。

在本节的其余部分,我们将介绍这两种方法。对于这两种算法,我们将使用 20 折交叉验证策略。

通过坐标下降实现 Lasso#

让我们首先使用 LassoCV 进行超参数调整。

from sklearn.linear_model import LassoCV

start_time = time.time()
model = make_pipeline(StandardScaler(), LassoCV(cv=20)).fit(X, y)
fit_time = time.time() - start_time
import matplotlib.pyplot as plt

ymin, ymax = 2300, 3800
lasso = model[-1]
plt.semilogx(lasso.alphas_, lasso.mse_path_, linestyle=":")
plt.plot(
    lasso.alphas_,
    lasso.mse_path_.mean(axis=-1),
    color="black",
    label="Average across the folds",
    linewidth=2,
)
plt.axvline(lasso.alpha_, linestyle="--", color="black", label="alpha: CV estimate")

plt.ylim(ymin, ymax)
plt.xlabel(r"$\alpha$")
plt.ylabel("Mean square error")
plt.legend()
_ = plt.title(
    f"Mean square error on each fold: coordinate descent (train time: {fit_time:.2f}s)"
)
Mean square error on each fold: coordinate descent (train time: 0.31s)

通过最小角回归实现 Lasso#

让我们首先使用 LassoLarsCV 进行超参数调整。

from sklearn.linear_model import LassoLarsCV

start_time = time.time()
model = make_pipeline(StandardScaler(), LassoLarsCV(cv=20)).fit(X, y)
fit_time = time.time() - start_time
lasso = model[-1]
plt.semilogx(lasso.cv_alphas_, lasso.mse_path_, ":")
plt.semilogx(
    lasso.cv_alphas_,
    lasso.mse_path_.mean(axis=-1),
    color="black",
    label="Average across the folds",
    linewidth=2,
)
plt.axvline(lasso.alpha_, linestyle="--", color="black", label="alpha CV")

plt.ylim(ymin, ymax)
plt.xlabel(r"$\alpha$")
plt.ylabel("Mean square error")
plt.legend()
_ = plt.title(f"Mean square error on each fold: Lars (train time: {fit_time:.2f}s)")
Mean square error on each fold: Lars (train time: 0.07s)

交叉验证方法总结#

两种算法给出的结果大致相同。

Lars 只为路径中的每个拐点计算解路径。因此,当拐点很少时,它非常有效,如果特征或样本很少,就会出现这种情况。此外,它能够在不设置任何超参数的情况下计算完整路径。相反,坐标下降在预先指定的网格上计算路径点(这里我们使用默认值)。因此,如果网格点的数量小于路径中拐点的数量,则它更有效。如果特征的数量非常大,并且在每个交叉验证折叠中都有足够的样本可供选择,那么这种策略可能会很有趣。在数值误差方面,对于高度相关的变量,Lars 会累积更多误差,而坐标下降算法只会在网格上对路径进行采样。

请注意 alpha 的最佳值如何随每个折叠而变化。这说明了为什么嵌套交叉验证在尝试评估通过交叉验证选择参数的方法的性能时是一个好的策略:这种参数选择对于仅在未见过的测试集上进行最终评估可能不是最佳的。

结论#

在本教程中,我们介绍了两种选择最佳超参数 alpha 的方法:一种策略是仅使用训练集和一些信息准则来找到 alpha 的最佳值,另一种策略是基于交叉验证。

在本例中,这两种方法的工作原理类似。样本内超参数选择甚至在计算性能方面也显示出其有效性。但是,只有当样本数量相对于特征数量足够大时,才能使用它。

这就是为什么通过交叉验证进行超参数优化是一种安全的策略:它适用于不同的设置。

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

相关示例

通过信息准则进行 Lasso 模型选择

通过信息准则进行 Lasso 模型选择

基于 L1 的稀疏信号模型

基于 L1 的稀疏信号模型

使用 LARS 的 Lasso 路径

使用 LARS 的 Lasso 路径

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

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

由 Sphinx-Gallery 生成的图库