Lasso 和 Ridge回归中的超参数调整技巧

article/2025/9/16 23:20:45

在这篇文章中,我们将首先看看Lasso和Ridge回归中一些常见的错误,然后我将描述我通常采取的步骤来优化超参数。代码是用Python编写的,我们主要依赖scikit-learn。本文章主要关注Lasso的例子,但其基本理论与Ridge非常相似。

起初,我并没有真正意识到需要另一个关于这个主题的指南——毕竟这是一个非常基本的概念。然而,当我最近想要确认一些事情时,我意识到,市面上的很多文章要么太学术化,要么太简单,要么就是完全错误。一个很常见的混淆来源是,在sklearn中总是有十多种不同的方法来计算同一件事情。

所以,废话少说,下面是我对这个话题的两点看法。

快速的理论背景回顾

Lasso和Ridge都是正则化方法,他们的目标是通过引入惩罚因子来正则化复杂的模型。它们在减少过拟合、处理多重共线性或自动特征工程方面非常出色。这听i来似乎有点神奇,但通过训练使模型更努力地拟合数据,我们得到一个更好的对底层结构的了解,从而对测试数据有了更好的泛化和更好的拟合。

LinearRegression

根据sklearn的公式,这是线性回归模型中最小的表达式,即所谓的普通最小二乘:

其中X矩阵为自变量,w为权重即系数,y为因变量。

Ridge

Ridge回归采用这个表达式,并在平方系数的最后添加一个惩罚因子:

这里α是正则化参数,这是我们要优化的。该模型惩罚较大的系数,并试图更平均地分配权重。用外行人的话来说,这就是Ridge模型所做的:

X1,我们看到你做得很好,如果不是因为惩罚的因素,我们会很重视你。但是X2只比你们差一点点,如果我们在你们俩之间均分权重,我们会得到更低的惩罚,从而得到更好的总分。

Lasso

Lasso做了类似的事情,但使用绝对值之和(l1范数)的权重作为惩罚。

注: sklearn提供公式中还有一个n_samples,这是观察的数量,并且应该改变X和y。我发现没有解释这是为什么,也许是为了比较不同模型。

Lasso将开始降低不那么重要的变量的系数,也有可能将系数降低到0。通俗的说:

X1,你对总分数的最小贡献会被注意到。但是,根据最新的罚分,我们将不得不将你从回归中移除。

Elastic Net

值得注意的是,您还可以将同一模型中的两个惩罚与Elastic Net结合起来。 您需要在那里优化两个超参数。 在本指南中,我们将不讨论此选项。

所需要的类库

以下是需要的所有库的列表:

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.metrics import \r2_score, get_scorer
from sklearn.linear_model import \Lasso, Ridge, LassoCV,LinearRegression
from sklearn.preprocessing import \StandardScaler, PolynomialFeatures
from sklearn.model_selection import \KFold, RepeatedKFold, GridSearchCV, \cross_validate, train_test_split

三个秘诀

在本节中,我们将讨论一些常规技巧和常见错误,以避免涉及正则化回归。 这些示例使用的是波士顿住房数据,您可以从Kaggle下载数据。

column_names = \['CRIM', 'ZN', 'INDUS', 'CHAS', 'NOX', 'RM', 'AGE',\'DIS', 'RAD', 'TAX', 'PTRATIO', 'B', 'LSTAT', 'MEDV']
data = pd.read_csv("../datasets/housing.csv", \header=None, delimiter=r"\s+", names=column_names)
y = data['MEDV']
X = data.drop(['MEDV'], axis = 1)

秘诀一:缩放自变量

如标题所示:需要缩放变量以进行正则回归。 (我们知道,像缩放这样的线性变换不会对原始线性回归的预测产生影响。)很明显,如果您仔细查看一下公式,为什么必须对正则回归进行缩放: 变量恰好在很小的范围内,其系数会很大,因此,由于惩罚会受到更大的惩罚。 反之亦然,大规模变量将获得较小的系数,并且受惩罚的影响较小。Lasso and Ridge都是如此。

假设您执行以下操作。

(同样,该示例没有缩放比例,将不会产生正确的结果,请不要这样做。此外,请注意,除了缩放比例以外,还有其他问题,我们将在近期内再次讨论。)

# 错误,不要使用
cv = RepeatedKFold(n_splits=10, n_repeats=3, random_state=1)
lasso_alphas = np.linspace(0, 0.2, 21)lasso = Lasso()
grid = dict()
grid['alpha'] = lasso_alphas
gscv = GridSearchCV( \lasso, grid, scoring='neg_mean_absolute_error', \cv=cv, n_jobs=-1)
results = gscv.fit(X, y)print('MAE: %.5f' % results.best_score_)
print('Config: %s' % results.best_params_)

结果如下:

MAE: -3.37896
Config: {'alpha': 0.01}

但是,如果事先缩放X变量,通常会获得更好的分数。 要缩放,我们可以使用sklearn的StandardScaler。 此方法使变量以0为中心,并使标准偏差等于1。

sc = StandardScaler()X_scaled = sc.fit_transform(X)
X_scaled = pd.DataFrame(data = X_scaled, columns = X.columns)

如果在上面的代码块中用X_scaled替换X,我们将得到:

MAE: -3.35080
Config: {'alpha': 0.08}

是的,没有太大的改进,但这是由于许多因素,我们将在后面看到。 最重要的是,波士顿的住房数据是一个很好的,经过量身定制的线性回归的示例,因此我们不能做太多改进。

总结:在进行正则化之前,使用StandardScaler缩放自变量。 无需调整因变量。

秘诀二:当Alpha等于零时…

如果在Lasso和Ridge中为alpha参数选择0,则基本上是在拟合线性回归,因为在公式的OLS部分没有任何惩罚。

由于计算复杂性,sklearn文档实际上不建议使用alpha = 0的参数运行这些模型。 因为他可能引起算问题,但我还没有遇到过这种情况,因为它总是给出与LinearRegression模型相同的结果。

总结:选择alpha = 0毫无意义,这只是线性回归。

秘诀三:多次尝试

在上面的示例中,我们浏览了一系列Alpha,对它们进行了全部尝试,然后选择了得分最高的Alpha。 但是,像往常一样,当您使用GridSearchCV时,建议进行多次尝试。 找到最高Alpha的区域,然后进行更详细的检查。

以我的经验,尤其是在使用Lasso时,选择最低的非零参数是一个常见的错误,而实际上,最佳参数要小得多。 请参阅下面的示例。

注意:当然,我们永远不会使用网格搜索方法找到实际的最佳数字,但是我们可以足够接近。

您还可以可视化结果。 这是未缩放版本的样子:

对于每个Alpha,GridSearchCV都适合模型,我们选择了Alpha,其中验证数据得分(例如,RepeatedKFold中测试折叠的平均得分)最高。 在此示例中,您可以看到0到0.01之间可能没有疯狂的峰值。 当然,这仍然是错误的,因为我们没有缩放。

这是缩放版本的图:

再次看起来不错,在0.07和0.09之间可能没有任何奇怪的事情发生。

总结:可视化是你的朋友,请观察alpha曲线。 确保您选择的Alpha位于漂亮的“弯曲”区域。

秘诀四:仔细考虑您的计分方法

您可能很想以其他方式进行计算以检查结果。 如前所述,sklearn通常有很多不同的方法来计算同一件事。 首先,有一个LassoCV方法将Lasso和GridSearchCV结合在一起。

您可以尝试执行以下操作以获得最佳Alpha(示例中不再使用未缩放的版本):

lasso = LassoCV(alphas=lasso_alphas, cv=cv, n_jobs=-1)
lasso.fit(X_scaled, y)
print('alpha: %.2f' % lasso.alpha_)

结果如下:

alpha: 0.03

等一下,难道不是上面的0.08的相同数据的Alpha吗? 是的。 差异的原因是什么? LassoCV使用R²得分,您无法更改它,而在更早的时候,我们在GridSearchCV对象中指定了MAE(正负MAE,但这只是为了使我们最大化并保持一致)。 这是为什么说上个代码错误的原因:

scoring='neg_mean_absolute_error'

问题是,sklearn有数十种计分方法,您也可以选择max_error来衡量模型的性能。 但是,该模型针对平方差进行了优化。但是我认为使用从平方差得出的任何东西都更加一致。,因为LassoCV使用R²,所以也许这是一个好的信号?

“在一个基础上进行优化,然后在另一个基础上进行性能比较”实际上在上面的图表中是很明显的。 注意绿线的评分高了很多。 那是因为这是训练的成绩。 在正常情况下,施加惩罚因素后,它的性能不应更好。

通常,这就是您将看到的曲线的形状。 训练数据得分立即下降,验证数据得分上升一段时间,然后下降:

总结:使用R²或另一个基于差异的平方模型作为回归的主要评分。

本文的方法

在这一节中,我将介绍我用来准备数据和拟合正则化回归的方法。

在得到X和y之前,我不会详细说明数据。我使用来自美国县级国家健康排名数据收集的一个版本来生成下面的结果,但对于这个示例来说,这真的无关紧要。

因此,假设您有一个漂亮而干净的X和y,下一步是使用方便的train_test_split留出一个测试数据集。如果想让结果重现,可以为my_random_state选择任何数字。

X_train , X_test, y_train, y_test = train_test_split(X, y, test_size=1000, random_state=my_random_state)

下一步是包含多项式特性。我们将结果保存在多边形对象中,这很重要,我们将在以后使用它。

poly = PolynomialFeatures(degree = 2, include_bias = False, interaction_only = False)

这将产生变量的所有二阶多项式组合。需要注意的是,我们将include_bias设置为False。这是因为我们不需要截距列,回归模型本身将包含一个截距列。

这是我们转换和重命名X的方法。它假设您将X保存在一个pandas DataFrame中,并且需要进行一些调整以保持列名可用。如果你不想要名字,你只需要第一行。

X_train_poly = poly.fit_transform(X_train)
polynomial_column_names = \poly.get_feature_names(input_features = X_train.columns)
X_train_poly = \pd.DataFrame(data = X_train_poly, columns = polynomial_column_names )X_train_poly.columns = X_train_poly.columns.str.replace(' ', '_')
X_train_poly.columns = X_train_poly.columns.str.replace('^', '_')

完成这一步后,下一步是扩展。在引入多项式之后,这就更加重要了,没有缩放,大小就会到处都是。

sc = StandardScaler()X_train_poly_scaled = sc.fit_transform(X_train_poly)
X_train_poly_scaled = pd.DataFrame( \data = X_train_poly_scaled, columns = X_train_poly.columns)

棘手的部分来了。如果我们想要使用测试数据集,我们需要应用相同的步骤。

但是,我们不需要再次适合这些对象。好吧,对于poly无所谓,但是对于sc,我们想要保留用于X_train_poly的方法。是的,这意味着测试数据不会完全标准化,这很好。我们用transform代替fit_transform。

X_test_poly = poly.transform(X_test)
X_test_poly_scaled = sc.transform(X_test_poly)

您可能想知道如何生成上面使用的图。我使用两个函数,构建在上面列出的库之上。第一个函数绘制一个图:

def regmodel_param_plot(validation_score, train_score, alphas_to_try, chosen_alpha,scoring, model_name, test_score = None, filename = None):plt.figure(figsize = (8,8))sns.lineplot(y = validation_score, x = alphas_to_try, label = 'validation_data')sns.lineplot(y = train_score, x = alphas_to_try, label = 'training_data')plt.axvline(x=chosen_alpha, linestyle='--')if test_score is not None:sns.lineplot(y = test_score, x = alphas_to_try, label = 'test_data')plt.xlabel('alpha_parameter')plt.ylabel(scoring)plt.title(model_name + ' Regularisation')plt.legend()if filename is not None:plt.savefig(str(filename) + ".png")plt.show()

第二个本质上是一个网格搜索,带有一些额外的东西:它也运行测试分数,当然还保存。

def regmodel_param_test(alphas_to_try, X, y, cv, scoring = 'r2', model_name = 'LASSO', X_test = None, y_test = None, draw_plot = False, filename = None):validation_scores = []train_scores = []results_list = []if X_test is not None:test_scores = []scorer = get_scorer(scoring)else:test_scores = Nonefor curr_alpha in alphas_to_try:if model_name == 'LASSO':regmodel = Lasso(alpha = curr_alpha)elif model_name == 'Ridge':regmodel = Ridge(alpha = curr_alpha)else:return Noneresults = cross_validate(regmodel, X, y, scoring=scoring, cv=cv, return_train_score = True)validation_scores.append(np.mean(results['test_score']))train_scores.append(np.mean(results['train_score']))results_list.append(results)if X_test is not None:regmodel.fit(X,y)y_pred = regmodel.predict(X_test)test_scores.append(scorer(regmodel, X_test, y_test))chosen_alpha_id = np.argmax(validation_scores)chosen_alpha = alphas_to_try[chosen_alpha_id]max_validation_score = np.max(validation_scores)if X_test is not None:test_score_at_chosen_alpha = test_scores[chosen_alpha_id]else:test_score_at_chosen_alpha = Noneif draw_plot:regmodel_param_plot(validation_scores, train_scores, alphas_to_try, chosen_alpha, scoring, model_name, test_scores, filename)return chosen_alpha, max_validation_score, test_score_at_chosen_alpha

我不想在这里讲得太详细,我认为这是不言自明的,稍后我们会看到如何调用它的例子。

有一件事,我认为非常酷:sklearn有一个get_scorer函数,它根据sklearn字符串代码返回一个scorer对象。例如:

scorer = get_scorer('r2')
scorer(model, X_test, y_test)

现在我们有另外一种方法来计算相同的东西。

一旦建立了这样的进程,我们所需要做的就是为不同的alpha数组运行函数。

这个过程的一个有趣之处在于,我们也在绘制测试分数:

  • 取训练数据集和alpha值;
  • 进行交叉验证,保存培训和验证分数;
  • 假设这是我们选择并拟合模型的alpha值,而不需要对整个训练数据进行交叉验证;
  • 计算该模型将对测试数据实现的分数,并保存测试分数。

这不是您在“现实生活”中会做的事情(除非您参加Kaggle竞赛),因为现在有了优化测试数据集的可能性。我们在这里仅仅是为了说明模型的性能。红线表示的是不同alpha的测试分数。

我们还需要一个交叉验证对象,这里没有一个好的答案,这是一个选项:

cv = KFold(n_splits=5, shuffle=True, random_state=my_random_state)

为了说明我关于多步参数搜索的重要性的观点,让我们假设我们想要检查这些alpha:

lasso_alphas = np.linspace(0, 0.02, 11)

运行函数后:

chosen_alpha, max_validation_score, test_score_at_chosen_alpha = \regmodel_param_test(lasso_alphas, X_train_poly_scaled, y_train, cv, scoring = 'r2', model_name = 'LASSO', X_test = X_test_poly_scaled, y_test = y_test, draw_plot = True, filename = 'lasso_wide_search')
print("Chosen alpha: %.5f" % \chosen_alpha)
print("Validation score: %.5f" % \max_validation_score)
print("Test score at chosen alpha: %.5f" % \test_score_at_chosen_alpha)

结果

Chosen alpha: 0.00200
Validation score: 0.82310
Test score at chosen alpha: 0.80673

这是否意味着我们找到了最优?你可以看一下图,看到一个漂亮的尖刺,但是它是否够高了。不完全是。如果我们在更细粒度的层面上运行它:

lasso_alphas = np.linspace(0, 0.002, 11)

这是结果,请注意0.02,最右边的点是我们在上一个图表中出现峰值的地方:

Chosen alpha: 0.00060
Validation score: 0.83483
Test score at chosen alpha: 0.82326

如果我们不进行详细的测试,我们就会选择一个能使整体检验R²降低2%的,我认为这很重要。

这篇文章的标题包括Ridge,除了理论介绍之外,我们还没有讨论过它。

原因很简单:它的工作方式与Lasso完全一样,您可能只是想选择不同的alpha参数,并在model_name参数中传递’ Ridge '。Ridge也存在同样的问题(我不包括搜索alpha范围的部分):

你会注意到,我们根据蓝线选择的点似乎不再是红线的最佳点。没错,但我觉得这对Lasso 模型来说是个巧合。

总结

这就是我为Lasso和Ridge做超参数调整的方法。希望对大家有所帮助,再次介绍一下要点:

  • 记住缩放变量;
  • alpha = 0是线性回归;
  • 多步搜索最佳参数;
  • 使用基于分数的平方差异来衡量表现。

作者:Mate Pocs

deephub翻译组


http://chatgpt.dhexx.cn/article/bL1EOQ4n.shtml

相关文章

Ridge和Lasso回归

上周看了看回归方面的知识,顺便复(xue)习一下Ridge(岭回归)和Lasso回归(套索回归)。瞅到了一篇英文博客讲得不错,翻译一下 本文翻译自 Ridge and Lasso Regression 本文是一篇Josh …

Ridge回归*

线性回归稍微总结一下: 常见有普通线性回归(没有正则项,目标是最小化均方误差)、LASSO(均方误差项l-1正则项)、Ridge回归(均方误差l-2正则项) 加上正则项可以降低过拟合风险。 Ridge…

【机器学习原理实战01】Ridge回归模型

Ridge回归模型 最近做项目用到岭回归模型,特地来记录一下整个岭回归模型的原理和代码。 以后会将用到的机器学习模型算法都记录下来。1、Ridge 回归原理 多元线性回归计算回归系数的时候回归系数的计算如下: 学过线性代数的都知道,以上式子…

岭回归(Ridge)和Lasso 回归(笔记)

最近在自学图灵教材《Python机器学习基础教程》,在csdn以博客的形式做些笔记。 对于回归问题,线性模型预测的一般公式如下: ŷ w[0] * x[0] w[1] * x[1] … w[p] * x[p] b 这里 x[0] 到 x[p] 表示单个数据点的特征(本例中特…

【机器学习】一文读懂正则化与LASSO回归,Ridge回归

该文已经收录到专题机器学习进阶之路当中,欢迎大家关注。 1.过拟合 当样本特征很多,样本数相对较少时,模型容易陷入过拟合。为了缓解过拟合问题,有两种方法: 方法一:减少特征数量(人工选择重要…

地址总线之寻址原理

译码器将地址总线的数据经过译码后锁定相应的位置,译码器有两种方结构,一种是单译码器,另外一种是双译码器。单译码器适用于存储容量小的应用环境中。双译码器结构将译码器分为两部分一部分是行译码器,另一部分是列译码器。行和列…

[组原]初识-地址总线,地址寄存器,存储单元,存储字长

文章目录 知识点机器字长指令寻址方式按字寻址按字节寻址 存储单元存储字存储字长 💟前记:由一道做错的题总结结果 ⏲ 05.09 知识点 机器字长 计算机进行一次整数运算所能处理的二进制数据的位数;通常也是CPU内部数据通路的宽度;…

地址总线/数据总线/控制总线的作用

数据总线 (1) 是CPU与内存或其他器件之间的数据传送的通道。 (2)数据总线的宽度决定了CPU和外界的数据传送速度。 (3)每条传输线一次只能传输1位二进制数据。eg: 8根数据线一次可传送一个8位二进制数据(…

内存结构、地址总线、数据总线详解

下图为内存的结构图 DB为一个bit,一行DB对应着一个存储单元。 数据总线个数对应着存储单元的位数 数据总线个数 存储单元位数 寄存器位数 如上图所示,内存的结构就是从上到下依次给存储单元编号。 按字节编址 就是每8个bit 对应着一个地址。 读取…

1.8地址总线

现在我们知道,CPU是通过地址总线来指定存储器单元的。可见地址总线上能传送多少个不同的信息,CPU就可以对多少个存储单元进行寻址。 现假设,一个CPU有10根地址总线,让我们来看一下它的寻址情况。我们知道,在电子计算机…

ARM数据/地址总线架构简析

ARM架构简析 1,ARM概述 现在大家讲的ARM的概念实际上是很模糊的,他可能指的是一类芯片,或者指的是ARM公司,亦或者是精简指令集,还是千万人手中的饭碗。下面引用一段关于百度百科关于ARM的准确描述 ARM架构&#xff0…

什么是前端总线,后端总线,内部总线、系统总线,外部总线,地址总线,数据总线,控制总线

文章 部分内容来自于 电子发烧友 内部总线、系统总线和外部总线汇总 部分内容来自于 知乎 前端总线,系统总线,内部总线,外部总线 本文是在两篇文章的基础上进行了二次加工,对两篇文章的精华内容进行了提炼,删掉了对理解…

地址总线是单向还是双向_「计算机组成原理」:总线、地址总线、数据总线和控制总线...

总线(Bus):是计算机各种功能部件之间传送信息的公共通信干线. 总线(Bus):是计算机各种功能部件之间传送信息的公共通信干线,它是由导线组成的传输线束, 按照计算机所传输的信息种类,计算机的总线可以划分为数据总线、地址总线和控制总线,分别用来传输数据、数据地址和控制…

计算机控制总线传输的是,总线,地址总线,数据总线和控制总线

总线(Bus): 这是一条通用的通信干线,可以在计算机的各种功能组件之间传输信息. 它是由电线组成的变速箱线束. 根据计算机传输的信息类型,计算机的总线可以分为数据总线和地址. 总线和控制总线分别用于传输数据,数据地址和控制信号. 总线是一种…

计算机64位地址总线,地址总线与数据总线的关系,功能和宽度

1. 地址总线与数据总线之间的关系 数据总线用于传输数据. 每条数据线传输一个二进制位64位处理器 数据总线,而8条线恰好传输一个字节. 但是,地址总线传输地址,每个地址也传输一个二进制位,而8条地址线传输8个二进制位. 这8个二进制位具有2 ^ 8个不同的变化,因此它们可用于…

地址总线

现在我们知道,CPU是通过地址总线来指定存储器单元的。可见地址总线上能传送多少个不同的信息,CPU就可以对多少个存储单元进行寻址。 现假设,一个CPU有10根地址总线,让我们来看一下它的寻址情况。我们知道,在电子计算机…

地址总线、物理地址、虚拟地址讲解

什么是地址总线? 地址总线(Address Bus ;又称位址总线)属于一种电脑总线(一部份),是由CPU或有DMA能力的单元,用来沟通这些单元想要存取(读取/写入)电脑内存元件/地方的实体位址。 换而言之,就…

计算机总线详解(数据总线、地址总线、控制总线)

文章目录 1 概述2 总线分类2.1 数据总线 Data Bus2.2 地址总线 Address Bus2.3 控制总线 Control Bus 3 扩展3.1 常考题3.2 百度百科 - 总线 Bus 1 概述 总线(Bus) 是计算机各种功能部件之间传送信息的 公共通信干线如果说 主板(Mother Boar…

总线之地址总线、数据总线、控制总线

https://www.cnblogs.com/codexlx/p/13252705.html 1.地址总线: CPU是通过地址总线来指定存储单元的,因此总线地址上能传送多少个不同的信息,CPU就可以对多少个存储单元进行寻址 一个CPU有N根地址线,那么可以说这个CPU的地址总线…

CentOS8 arm64架构 鲲鹏服务器上安装zabbix-agent

1、使用系统yum源中自带的资源安装 查看yum中自带的zabbix资源包 sudo yum list |grep zabbix安装 sudo install zabbix40-agent.aarch64 # 填写自己服务器上的资源2、下载资源包安装 获取官网下载地址 zabbix官网下载.aarch64.rpm的资源包,地址 搜索需要的包 …