news 2026/10/14 12:45:12

嵌入法特征选择:从Lasso稀疏到树模型重要性原理与实操

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
嵌入法特征选择:从Lasso稀疏到树模型重要性原理与实操

1. 嵌入法到底在解决什么问题

做特征选择绕不开三条路:过滤式、包裹式、嵌入式。前几篇已经把过滤式和包裹式的实操讲得差不多了,今天专门拆嵌入法(Embedded)。

先记住一句话:嵌入法把“特征筛选”直接塞进模型训练过程里。模型一边拟合目标函数,一边给每个特征分配权重或打重要性分,训练一结束,特征的重要性排名也就出来了。整个过程不需要像包裹式那样反复训练特征子集,也不需要像过滤式那样把特征和模型割裂开单独算统计量。这也是嵌入法叫“嵌入”的原因——选择动作内嵌于训练本身。

适合看这篇的人有三类:一是手里特征动辄上百个,想快速找到对预测真正有用的那一小部分;二是被包裹式方法(比如递归特征消除)的计算量劝退,想找更省时的筛选方式;三是你已经在用线性模型或树模型,但对Lasso系数和feature_importances_到底能不能信、怎么用来选特征这件事缺乏系统理解。

1.1 过滤式、包裹式、嵌入式的本质区别

先放一张总表,方便理解嵌入法在整个特征选择体系中的位置。

方法评估粒度是否依赖具体模型计算成本典型代表
过滤式单个特征与标签的关系否极低方差、卡方、互信息、相关系数
包裹式特征子集的整体预测能力是,模型当黑盒反复试探高RFE、递归特征消除、前向选择
嵌入式特征在模型目标函数中的贡献是,参与训练过程中Lasso、树模型重要性

为什么嵌入式要放到过滤式和包裹式之后才讲?因为它对模型有硬性要求:你手上必须有一个能产出权重或重要性的模型。同时,前面做过的数据清洗、编码、分箱、缺失值处理这些前置工作一样也不能少。换句话说,嵌入法是站在前两种方法肩膀上的精细操作,前置工作不到位,嵌入式的输出就会失真。

用生活化类比来说明三者的差异:过滤式像相亲只看简历上的身高学历,不相处不知道合不合拍;包裹式像反复约会试错,成本高但最贴近真实;嵌入式像一边相处一边打分,合适就让对方留下,不合适直接淘汰,效率和效果都在线。

1.2 嵌入式“嵌入”的到底是什么

我习惯把嵌入法理解成:把“特征选择函数”做进了损失函数里。

以线性模型为例,普通最小二乘只优化误差项,所有特征都会分到或大或小的系数,没有一个真正的0。加了L1惩罚后,目标函数多了一项“所有系数绝对值之和”,优化器在误差和惩罚之间权衡,对预测贡献微弱的特征,系数会直接被压到0。这个“压到0”的动作,就是选择动作本身。

树模型那边同理。随机森林、XGBoost、LightGBM训练时不断寻找最优分裂特征,每次分裂都会让节点纯度提升,这种“提升量”被累计到对应特征头上,就成了我们常见的特征重要性。训练结束,重要性的排序就是筛选依据。

所以嵌入式的核心答案是:不需要额外设计一套筛选流程,模型的训练过程本身就包含了特征评估。这是它效率高的根本原因。

1.3 单特征视角和组合视角的差异

过滤式的主要问题是只看单特征与标签的关系,完全看不到特征之间的组合效应。包裹式虽然看组合,但搜索空间是指数级增长的,特征一多就完全跑不动。嵌入法则站在中间:它考虑的是“在当前模型假设下,某个特征对整体目标函数的最优解有多大贡献”。

它能看到特征在模型结构里的作用。树模型天然能捕捉特征交互,某个特征单独看与标签关系很弱,但和另一个特征组合起来价值很大,过滤式会漏掉,树模型的增益机制能抓住。Lasso则把相关系数高的特征视为冗余,自动挑一个留下。这种“组合视角”让嵌入式在精度和效率之间找到了一个平衡点。

2. 嵌入式的两大技术流派

嵌入式具体落地,基本分裂成两个流派:正则化流派和树模型流派。两者原理不同、产出物不同、适用场景也不同,搞清楚它们的差异,才能在不同数据集上选对工具。

2.1 正则化流派:L1为什么能把系数压成0

回归问题的优化目标可以写成误差项加惩罚项。L1惩罚是所有系数绝对值之和,L2惩罚是所有系数平方和。两者只有一个字母之差,行为却天差地别。

图解能看得很清楚:L1的约束区域是菱形,顶点正好落在坐标轴上;误差项的等值线是椭圆,优化时等值线碰到约束边界,极大概率先碰到菱形的顶点,交点处某个系数正好为0。L2的约束区域是圆形,等值线只会沿着圆周压缩,系数整体变小但不会归零。这就是为什么L1天然适合做特征选择,L2天然适合处理共线性。选择L1还是L2,本质上是选择“稀疏”还是“收缩”。

正则化强度由alpha(也叫lambda)控制。alpha越大,惩罚越狠,系数越稀疏,保留的特征越少;alpha越小,越接近普通最小二乘,特征几乎全留下。实操中千万不要手动试alpha,用LassoCV直接在交叉验证里搜最优值,又快又稳。

还有一个容易踩的点:当特征之间强相关时,L1会“随机”挑一个代表性特征,另一个直接被归零,而且挑谁不稳定。这时候可以用ElasticNet,也就是L1加L2混合惩罚。它在L1稀疏的基础上多了一层L2收缩,相关特征组的权重会被平摊,稳定性好很多。我的经验是,一旦发现特征相关度明显,直接上ElasticNet,别再纠结Lasso。

2.2 树模型流派:重要性分数怎么算出来的

树模型的特征重要性有三种常见口径,很多人混着用,但它们的内涵完全不同。

第一种是最常用也最容易出问题的“基于分裂增益”的重要性。每次分裂都会降低节点不纯度,分类用基尼或信息熵,回归用MSE。特征被选为分裂点的次数越多、带来的不纯度下降越大,累计重要性越高。XGBoost和LightGBM在此基础上还细分出weight(被选中分裂的次数)、gain(每次分裂的平均增益)、cover(覆盖的样本量)。默认输出的是weight,但这只能说明“这个特征经常被用到”,不能完全反映“贡献多大”。想评估真实贡献,看gain更有意义。

XGBoost和LightGBM里取gain型重要性的方式不太一样,以LightGBM为例:

import pandas as pd import lightgbm as lgb model = lgb.LGBMRegressor(n_estimators=200, random_state=42) model.fit(X, y) importance = pd.Series( model.booster_.feature_importance(importance_type='gain'), index=[f'feature_{i}' for i in range(X.shape[1])] ).sort_values(ascending=False) print(importance.head(20))

第二种是排列重要性(permutation importance),做法是训练完模型后,把某个特征列随机打乱,再看模型效果下降多少,下降越多说明该特征越重要。严格来说它不属于嵌入法,因为发生在训练之后,是后验评估。我在这里专门提它,是提醒你别把模型自带的重要性和排列重要性混为一谈,两者统计口径不同,排列重要性更接近“该特征对预测能力的直接影响”。

树模型重要性有个天生缺陷:数值型特征和高基数特征容易被高估。因为可切分点多,偶然获得的“伪增益”也多。所以比较重要性要在同类型特征内部比,不要拿一个连续变量和一个二分类变量的重要性绝对值直接对比,那是没有意义的。

2.3 根据业务场景选流派

两个流派谁更强,取决于数据和业务目标,我从经验出发给一个选型方向:

  • 线性关系为主、特征全是数值型、希望得到稀疏系数方便出业务规则,选L1或ElasticNet。
  • 特征之间存在交互、类型混杂、有大量类别变量,选树模型。
  • 特征维度上万甚至更高,L1迭代快,树模型的增益计算虽然也扛得住,但重要性解释成本更高。
  • 业务上要求可解释性,Lasso系数的正负号有直接含义;树模型只能给出相对排名。

实操里我喜欢“两步走”:先用Lasso做一次粗筛把维度降下来,比如500个特征压到50个,再把筛选后的特征喂给树模型做精细调参。这样既吃到Lasso的稀疏优点,又不浪费树模型对非线性关系的捕捉能力。这种“嵌入式加嵌入式”的组合,实际用下来比单用一种效果好。

3. 实操:把嵌入法落地

理论讲完,直接上代码。下面以scikit-learn为主,讲清楚LassoCV、SelectFromModel和Pipeline三个核心工具的用法和背后的原理。

3.1 LassoCV自动选alpha

第一步是标准化。L1惩罚项直接加在系数绝对值上,如果特征量纲不同,数值大的特征系数天然就小,惩罚力度完全不一致。标准化后所有特征在同一尺度上,Lasso的系数才是可比的。

第二步是用LassoCV自动搜alpha,以回归任务为例:

import numpy as np import pandas as pd from sklearn.datasets import make_regression from sklearn.preprocessing import StandardScaler from sklearn.linear_model import LassoCV X, y = make_regression(n_samples=500, n_features=80, n_informative=20, noise=0.5, random_state=42) scaler = StandardScaler() X_scaled = scaler.fit_transform(X) lasso = LassoCV(cv=5, random_state=42, max_iter=10000, n_alphas=200) lasso.fit(X_scaled, y) coef = pd.Series(lasso.coef_, index=[f'feature_{i}' for i in range(X.shape[1])]) selected = coef[coef != 0] print('保留特征数:', len(selected)) print('最优alpha:', lasso.alpha_)

这里有几个值得说透的细节。LassoCV内部会用交叉验证搜索最优alpha,n_alphas=200意味着alpha搜索路径划分得很细,找到的最优alpha更精确。代价是训练时间稍长,但Lasso本身是线性复杂度,完全可接受。

max_iter是我踩过坑的地方。LassoCV默认迭代上限是1000,特征维度稍高就报坐标下降不收敛警告。直接把max_iter拉到10000甚至50000,问题基本消失。

筛选出来的特征就是coef不为0的那些,不需要额外设定阈值。如果你想要更严格的筛选,也可以对非零系数再按绝对值排序,只取前N个,这适合业务上有特征数量上限的场景。

3.2 树模型加SelectFromModel

树模型做特征选择时,scikit-learn的SelectFromModel是官方推荐的高效封装。它的原理很简单:对模型fit一次,读取model.feature_importances_(或coef_),再按阈值决定保留哪些特征。

from sklearn.ensemble import RandomForestRegressor from sklearn.feature_selection import SelectFromModel rf = RandomForestRegressor(n_estimators=200, random_state=42, n_jobs=-1) sfm = SelectFromModel(rf, threshold="median", max_features=30) sfm.fit(X, y) selected_mask = sfm.get_support() print('保留特征数:', selected_mask.sum()) selected_features = [f'feature_{i}' for i in range(X.shape[1]) if selected_mask[i]] print(selected_features)

threshold参数控制保留标准。默认值是"mean",也就是保留重要性高于平均值的特征。但机器学习里的重要性分布通常呈长尾,少数特征占据大部分重要性,此时mean会把大部分特征过掉,筛选过于激进。threshold="median"保留重要性高于中位数的特征,约等于保留一半,更温和一些。

max_features参数用来设置保留数的上限。当你对特征数量有明确约束,比如只准100个特征入模,它在threshold之上再卡一道闸,非常实用。

实际使用前,我强烈建议先看一眼重要性的分布,打印排序后的重要性数值,再定threshold。很多教程直接告诉你用median,但它默认删掉一半特征,可能过于保守也可能过于激进,还是要看具体数据。

3.3 把选择器放进Pipeline的正确姿势

嵌入法最容易被忽视的坑:先在完整数据集上跑Lasso选特征,再对选出来的特征子集切训练测试集、训练下游模型。这样做的结果是模型评估分数虚高,因为特征选择环节已经看到了全量标签信息,属于典型的信息泄露。

解决办法是把特征选择器放进Pipeline,让交叉验证把“选择加建模”整条链路一起评估:

from sklearn.pipeline import Pipeline from sklearn.linear_model import Ridge from sklearn.model_selection import cross_val_score pipe = Pipeline([ ('scale', StandardScaler()), ('select', SelectFromModel(LassoCV(cv=3, random_state=42, max_iter=5000), threshold="median")), ('model', Ridge(alpha=1.0)) ]) scores = cross_val_score(pipe, X, y, cv=5, scoring='r2') print('CV R2:', scores.mean())

这段代码里,select环节在每一折交叉验证内部重新执行,选择依据只来自训练折,不会碰到验证折的信息。这个原则同样适用于过滤式特征选择——只要选择过程使用了标签信息,就必须放在CV内部。这个点我会反复强调,因为它直接影响整个模型评估体系的真实性。

4. 常见坑与排查实录

嵌入法用久了,会遇到一些重复出现的坑。下面这些场景基本覆盖了我在多个项目里踩过的典型问题,遇到相似情况可以先对号入座。

4.1 特征重要性不等于真实因果

嵌入法筛出来的特征,只能说明“在当前模型和数据下,该特征对预测有用”,不能说明“改变这个特征就能改变结果”。我在一个信贷评分场景里遇到过,模型里“年龄”的重要性很高,业务同学觉得年龄是关键因子,差点据此设计差异化策略。但事实上年龄反映的是其他社会经济变量的复合影响,直接拿去做业务归因,很容易得出误导性结论。

还有一类偏差来自样本分布。正负样本比例严重失衡时,少数类样本对应的特征往往被低估,因为树模型分裂主要被多数类主导。处理方式是训练时设置class_weight,或者对样本加权后再计算重要性。样本不均衡问题不处理,嵌入式选出来的特征集可能只对多数类有效。

4.2 高相关性特征会把重要性摊薄

两个特征强相关时,树模型每次分裂会随机选择其中一个,导致两者重要性都被压低。这不是模型坏了,而是模型在“节俭”——它只要一个特征就够用了。Lasso的表现更直接,L1会挑一个特征强制归零另一个。

我常用的规避手段有三个:

  • 先用相关性热图或聚类把强相关特征归成组,从每组里保留一个代表特征。
  • 树模型下把强相关特征的重要性直接相加,看“组重要性”而不是单特征重要性。
  • 用ElasticNet替代Lasso,让相关特征组的权重被L2平摊,选特征更稳定。

如果你发现重要性的排名在两次训练之间剧烈变动,第一件事就去查特征相关矩阵,大概率能找到一群高度相关的特征在那里“打架”。

4.3 特征数大于样本数时L1的表现

当特征维度远大于样本量,也就是常说的p远大于n场景,Lasso选出非零系数的特征数大约最多不超过样本量n,而且选谁相当不稳定。alpha稍微变一点,选出的特征集会剧烈变化。这不是代码写错了,是L1在超高维下的天然特性。

解决思路是分层处理:先用过滤式把明显没用的特征清掉,比如方差过滤和相关性分析,把维度降到样本量的可接受范围,再上嵌入法做精细筛选。如果必须处理超高维,用ElasticNet会比Lasso稳定得多,唯一代价是选出来的特征没那么“硬核稀疏”。

4.4 阈值到底怎么定

很多教程说threshold="median"简单方便,但它默认删掉一半特征,这个“一半”可能太保守也可能太激进。我的做法是画重要性排序曲线,看前多少特征贡献了80%以上的累计重要性,找肘部位置作为自然阈值点。操作不复杂,把importance排序后算累计占比画个折线图就行。

如果业务上对特征数量有硬性约束,比如上线流程只允许10个特征,那就没有讨论空间,阈值直接按数量倒推。如果没有约束,我建议在交叉验证框架下对threshold做网格搜索,对比不同阈值下模型效果,而不是拍脑袋定“一半”。

4.5 类别特征和缺失值的影响

独热编码后的类别特征喂给Lasso,会让特征数量暴涨,大部分虚拟变量系数为0,符合预期但特征空间很大、训练变慢。我一般先对类别做目标编码或频次编码,压缩维度后再上Lasso。树模型对类别特征的原生支持更好,但连续编码的排序方式会影响分裂逻辑,需要多留意。

缺失值方面,Lasso完全不能容忍缺失,必须先填补;部分梯度提升树框架内部可以处理缺失值,默认策略是把它分到增益最大的方向,所以保留缺失值本身也是一种选择,要不要用取决于业务含义。比如“收入缺失”可能本身就含有“群体标签”的信息,直接填补反而丢掉信息。

这里给一个常见问题速查表,遇到类似情况可以先对照排查:

现象可能原因排查思路
Lasso报警不收敛max_iter太小或特征未标准化标准化、max_iter拉到10000以上
选中特征全是数值型树模型对类别特征有偏同类型特征内比较,或调大min_samples_leaf
两次运行保留特征集差异大随机性加特征强相关固定random_state、用ElasticNet、多次跑取交集
CV分数诡异偏高特征选择在CV外面做了把选择器放进Pipeline再评估
重要性排名和业务认知相悖相关性不等于因果检查相关矩阵和样本分布,勿直接下结论

5. 关于嵌入法,我最后想说的话

前面把原理和代码都过了一遍,最后分享几个我在项目里沉淀下来的操作习惯,这些细节教科书里很少写,但对实际选特征帮助很大。

5.1 稳定性优先,三跑取交集

嵌入法选出来的特征不一定稳定。我自己的标准操作是:固定random_state,把LassoCV跑三遍,每次换不同的交叉验证折法,取三次非零系数的交集作为最终特征集。交集里的特征才是“无论如何都重要”的特征。这个做法偏保守,但换来的是后续建模时特征集不会反复变动,省掉大量返工时间。

树模型也一样。临时跑出来的重要性排名可能有运气成分,多跑几次取稳定出现的头部特征,比单跑一次的结果可信得多。

5.2 树模型要看gain而不是weight

用梯度提升树时,默认输出往往是weight(被选中分裂的次数)。一个特征如果经常被选中做分裂,很可能是因为它在某个局部数据子群里有效,对全局贡献有限。gain,也就是平均增益,更能反映“每次分裂带来的实际质量”。

我习惯把gain排名前20和weight排名前20拉出来对比,差异大的特征单独检查。曾经有一个特征在weight里排第三,但gain排到五十几名,最后确认它是被频繁使用却每分裂贡献很小的“工具人”特征,对整体预测帮助有限,去掉之后模型效果不降反升。

5.3 嵌入法不是一劳永逸

特征选择结果绑定在训练时的数据分布上。业务环境里数据分布会漂移,上个月重要度很高的特征,这个月可能完全不重要。我的习惯是给特征重要性设一个“保质期”,模型上线后定期重算重要性,一旦发现老特征集体失效、新特征大量浮现,就考虑重训加重选。这个动作听起来麻烦,但比模型悄悄变差再抢救要省事得多。

嵌入法是我做特征选择时最常用的工具箱,它不像过滤式那样单薄,也不像包裹式那样昂贵。但记住一点:它给出的是“当前模型视角下的重要性”,不是“客观世界里的重要性”。带着这个认知去用,嵌入法能帮你把特征工程做得又快又稳。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/14 12:45:02

栈的经典应用:判断形如“序列1序列2”的镜像逆序序列

如果你在准备数据结构考试或者刷算法题,大概率会遇到这样一道经典题:识别一个以 结尾的字符序列,是否形如 序列1&序列2 的形式,其中序列2是序列1的逆序列。我第一次看到这个题目时觉得特别绕,明明每个字都懂&…

作者头像 李华
网站建设 2026/10/14 12:44:42

Tomcat 8 集群 Session 共享:Redis Session Manager 配置与避坑指南

简介:针对Tomcat8与JDK1.8环境下的Java Web应用集群部署场景,下载包整合了tomcat-redis-session-manager所需的核心依赖与配置模板。默认Tomcat的Session保存在本地内存,难以在多个实例间共享;引入Redis存储后,用户会话…

作者头像 李华
网站建设 2026/10/14 12:44:25

RapidOcr-Onnxruntime离线OCR实战:依赖部署与推理优化

简介:这是一份面向开发者与算法工程师的离线文字识别依赖库资源,围绕RapidOcr与Onnxruntime组合方案,解决在无网络环境下进行高效OCR识别的需求,适用于文档处理、车牌识别、屏幕截图转文本等实时场景。压缩包共991个文件&#xff…

作者头像 李华
网站建设 2026/10/14 12:43:37

智慧电厂解决方案落地的关键:四层架构、六大模块与避坑指南

简介:智慧电厂解决方案是一份面向发电行业信息化与数字化转型的专题文档,适合电力企业管理者、信息化工程师及智慧能源领域从业者参考。内容系统梳理了发电企业信息化从生产过程自动化到大数据、人工智能应用的五个阶段,并针对传统电厂在检修…

作者头像 李华
网站建设 2026/10/14 12:43:28

多Agent系统面试通关:四种经典架构深度解析与实战避坑指南

1. 多 Agent 系统面试的底层逻辑拆解面试里被问到多 Agent 系统,很多人第一反应是背概念:什么 Agent 是能感知环境、做出决策、采取行动的东西,多 Agent 就是多个 Agent 协作。这种回答在初级面试里勉强能过,但一旦面试官追问“那…

作者头像 李华
网站建设 2026/10/14 12:43:10

多Agent系统面试核心:四种架构选型与工程落地实践

1. 多 Agent 系统面试的底层逻辑拆解 1.1 为什么面试官总爱问多 Agent 架构 面到多 Agent 系统这道题,基本可以判断你面的岗位跟 AI 应用落地强相关,要么是平台工程,要么是算法工程,要么是偏架构的应用开发。面试官问这个&#x…

作者头像 李华