查看全课大纲(第 24 / 35 节)
- 1.特征工程简介
- 2.特征工程的处理流程
- 3.数据与特征概述
- 4.数据统计、相似度与数据探索
- 5.数据探索工具 Pandas_profiling
- 6.工具应用:《泰坦尼克》分析报告
- 7.构造一个微型链家租房数据集用于演示
- 8.数据预处理综述
- 9.缺失值处理实操
- 10.异常值处理实操
- 11.连续值特征构建
- 12.类别型数据特征构建
- 13.特征幅度缩放
- 14.特征离散化
- 15.特征编码:让类别数据参与建模
- 16.特征变换
- 17.时间特征
- 18.特征衍生
- 19.自动化特征工具
- 20.特征选择综述
- 21.特征选择方法
- 22.过滤式特征选择方法实操
- 23.包裹式特征选择方法
- 24.嵌入式特征选择
- 25.混序法与混合法特征选择
- 26.特征降维综述
- 27.案例实操:特征降维
- 28.特征监控概述与指标
- 29.Iris 数据集特征漂移检测案例
- 30.特征监控实战:用分布对比检测数据漂移
- 31.实时特征监控
- 32.电商销量预测项目背景介绍
- 33.数据预处理和特征工程(一)
- 34.数据预处理和特征工程(二)
- 35.数据建模
嵌入式特征选择
约 3 分钟
小象实战讲义 · 特征工程精讲与实践
在学习了过滤法和包裹法之后,本节我们将深入讲解特征选择的第三种核心方法——嵌入法(Embedded Method)。嵌入法将特征选择过程与模型训练过程融为一体,利用模型自身的特性(如正则化、特征重要度)自动完成特征筛选。这种方法既考虑了特征与目标的关系,也考虑了特征之间的相互作用,是工业实践中非常高效且常用的特征选择手段。
💡 核心导读
- 嵌入法的核心思想:将特征选择过程嵌入到模型训练过程中,利用模型训练的结果(如系数、重要度)来评估特征的重要性。
- 基于正则化的特征选择:通过给模型(如逻辑回归)添加 L1 正则化(Lasso),可以使不重要的特征系数收缩为零,从而实现特征选择。
- 基于树模型的特征重要度:随机森林、GBDT 等集成树模型能够输出每个特征的重要度评分,基于此可以筛选重要特征。
- 实践流程与注意事项:使用嵌入法时,需要注意数据预处理(如幅度缩放对线性模型的影响)以及阈值设定的技巧。
嵌入法原理与流程
嵌入法是一种将特征选择器与机器学习模型结合训练的方法。其核心在于,模型在训练过程中会“学习”到每个特征对预测目标的贡献程度,我们可以利用这个贡献度(如线性模型的系数绝对值、树模型的特征重要度)来对特征进行排序和筛选。
与过滤法(独立评估每个特征)和包裹法(迭代评估特征子集)相比,嵌入法有两大优势:
- 计算效率更高:特征选择作为模型训练的一部分完成,无需像包裹法那样进行多次迭代训练。
- 考虑特征交互:模型在训练时能捕捉特征之间的协同或抵消效应,因此筛选出的特征子集通常更具代表性。
在 scikit-learn 中,SelectFromModel 元变换器是实现嵌入法的核心工具。它可以包装任何一个在训练后能提供特征重要性评估的模型(如 coef_ 或 feature_importances_ 属性),并根据设定的阈值来选择特征。
基于 L1 正则化的特征选择
线性模型(如逻辑回归、线性回归)结合 L1 正则化(又称 Lasso)是嵌入式特征选择的经典方法。L1 正则化会在模型的损失函数中增加一项权重的绝对值之和作为惩罚项,这会导致一部分不重要的特征对应的权重(系数)被精确地压缩为零。系数为零即意味着该特征对模型预测没有任何贡献,可以被安全移除。
重要前提:对于逻辑回归等线性模型,使用 L1 正则化进行特征选择前,必须对特征进行幅度缩放。因为正则化惩罚项作用于系数的绝对值,如果特征尺度差异巨大,小尺度的特征往往需要更大的系数才能产生同等影响,从而承受更重的惩罚、更容易被误清零,导致选择偏差。常用的缩放方法包括 RobustScaler(抗异常值)、StandardScaler(标准化)或 MinMaxScaler(归一化)。
下面我们通过一个完整的示例来演示如何使用逻辑回归+L1正则化进行特征选择。请注意,每个代码块都是完全独立、可运行的。
# 代码块1:准备数据与幅度缩放
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import RobustScaler
# 1. 构造一个微型可复现的示例数据集(模拟分类问题)
# 特征:f0-f4 为有效特征(与标签强相关),f5-f9 为噪声特征
X_data = np.array([
[0.2, 1.8, -0.9, -0.5, 0.10, 0.30, -0.10, 0.05, 0.40, -0.20],
[0.4, 1.6, -1.1, -0.7, 0.20, -0.20, 0.20, -0.15, -0.40, 0.30],
[0.3, 1.7, -0.8, -0.4, 0.15, 0.10, -0.30, 0.20, 0.30, -0.10],
[0.5, 1.5, -1.2, -0.8, 0.25, -0.30, 0.10, -0.05, -0.30, 0.40],
[0.25, 1.75, -1.0, -0.6, 0.05, 0.20, 0.00, 0.10, 0.35, 0.00],
[1.6, 0.3, 0.9, 0.7, 0.90, -0.10, 0.30, -0.20, -0.35, 0.10],
[1.8, 0.5, 1.1, 0.9, 1.00, 0.25, -0.20, 0.05, 0.45, -0.30],
[1.7, 0.2, 0.8, 0.6, 0.85, -0.25, 0.10, 0.15, -0.45, 0.20],
[1.9, 0.4, 1.2, 1.0, 1.10, 0.15, -0.15, -0.10, 0.25, 0.35],
[1.75, 0.35, 1.0, 0.8, 0.95, 0.05, 0.20, 0.00, -0.25, -0.05],
])
# 将小样本复制三次,形成 30 行数据用于演示
X_data = np.vstack([X_data]*3)
# 标签:前 5 行为 0、后 5 行为 1(由 f0-f4 的取值模式决定,与 f5-f9 无关),硬编码保证可复现
y_data = np.tile(np.array([0, 0, 0, 0, 0, 1, 1, 1, 1, 1]), 3)
feature_names = [f'f{i}' for i in range(10)]
X_df = pd.DataFrame(X_data, columns=feature_names)
y_series = pd.Series(y_data, name='target')
# 2. 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X_df, y_series, test_size=0.33, random_state=42)
print(f"训练集形状: {X_train.shape}, 测试集形状: {X_test.shape}")
# 3. 幅度缩放:使用RobustScaler(对线性模型+正则化非常重要)
scaler = RobustScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)
print("\n缩放后的训练集前2行:")
print(X_train_scaled[:2])训练集形状: (20, 10), 测试集形状: (10, 10)
缩放后的训练集前2行:
[[-0.17241379 0.19047619 -0.09876543 -0.14035088 -0.25 0.3030303
-0.13793103 0.42857143 0.49122807 0. ]
[ 0.89655172 -0.76190476 0.9382716 0.9122807 0.9375 0.42424242
-0.68965517 0.14285714 0.63157895 -0.70588235]]from sklearn.preprocessing import RobustScaler
from sklearn.model_selection import train_test_split
import pandas as pd
import numpy as np
# 代码块2:使用逻辑回归+L1正则化进行特征选择
from sklearn.linear_model import LogisticRegression
from sklearn.feature_selection import SelectFromModel
# 重新构造与代码块1完全相同的训练数据(确保本块独立)
# 特征:f0-f4 为有效特征,f5-f9 为噪声特征
X_data = np.array([
[0.2, 1.8, -0.9, -0.5, 0.10, 0.30, -0.10, 0.05, 0.40, -0.20],
[0.4, 1.6, -1.1, -0.7, 0.20, -0.20, 0.20, -0.15, -0.40, 0.30],
[0.3, 1.7, -0.8, -0.4, 0.15, 0.10, -0.30, 0.20, 0.30, -0.10],
[0.5, 1.5, -1.2, -0.8, 0.25, -0.30, 0.10, -0.05, -0.30, 0.40],
[0.25, 1.75, -1.0, -0.6, 0.05, 0.20, 0.00, 0.10, 0.35, 0.00],
[1.6, 0.3, 0.9, 0.7, 0.90, -0.10, 0.30, -0.20, -0.35, 0.10],
[1.8, 0.5, 1.1, 0.9, 1.00, 0.25, -0.20, 0.05, 0.45, -0.30],
[1.7, 0.2, 0.8, 0.6, 0.85, -0.25, 0.10, 0.15, -0.45, 0.20],
[1.9, 0.4, 1.2, 1.0, 1.10, 0.15, -0.15, -0.10, 0.25, 0.35],
[1.75, 0.35, 1.0, 0.8, 0.95, 0.05, 0.20, 0.00, -0.25, -0.05],
])
X_data = np.vstack([X_data]*3)
y_data = np.tile(np.array([0, 0, 0, 0, 0, 1, 1, 1, 1, 1]), 3)
feature_names = [f'f{i}' for i in range(10)]
X_df = pd.DataFrame(X_data, columns=feature_names)
y_series = pd.Series(y_data, name='target')
X_train, X_test, y_train, y_test = train_test_split(X_df, y_series, test_size=0.33, random_state=42)
scaler = RobustScaler()
X_train_scaled = scaler.fit_transform(X_train)
# 使用逻辑回归 + L1正则化 作为选择器的基础模型
# 注意:指定 penalty='l1' 和 solver='liblinear' 以支持L1正则化
lr_l1 = LogisticRegression(penalty='l1', solver='liblinear', C=1.0, random_state=42, max_iter=1000)
# 创建SelectFromModel对象,使用上面定义的逻辑回归模型
# threshold=None 表示使用模型内置的阈值(对于L1正则化,即保留系数非零的特征)
selector = SelectFromModel(estimator=lr_l1, threshold=None)
# 在缩放后的训练数据上拟合选择器(即训练逻辑回归模型并确定要保留的特征)
selector.fit(X_train_scaled, y_train)
# 查看哪些特征被选中(系数非零)
selected_mask = selector.get_support()
selected_feat_names = X_train.columns[selected_mask]
removed_feat_names = X_train.columns[~selected_mask]
print(f"原始特征数量: {X_train.shape[1]}")
print(f"L1正则化选中的特征数量: {len(selected_feat_names)}")
print(f"选中的特征: {list(selected_feat_names)}")
print(f"被剔除的特征(系数为0): {list(removed_feat_names)}")
print("\n逻辑回归模型的系数:")
for feat, coef in zip(X_train.columns, selector.estimator_.coef_[0]):
print(f" {feat}: {coef:.4f}")原始特征数量: 10
L1正则化选中的特征数量: 3
选中的特征: ['f1', 'f3', 'f4']
被剔除的特征(系数为0): ['f0', 'f2', 'f5', 'f6', 'f7', 'f8', 'f9']
逻辑回归模型的系数:
f0: 0.0000
f1: -1.2102
f2: 0.0000
f3: 2.1728
f4: 0.4566
f5: 0.0000
f6: 0.0000
f7: 0.0000
f8: 0.0000
f9: 0.0000从结果可以看到两个关键事实:
- 5 个噪声特征(
f5-f9)的系数全部被压缩为零——它们对预测没有贡献,被 L1 正则化自动剔除; - 有效的 5 个特征中,
f1、f3、f4拿到了非零系数(f1 为负方向,与数据设计一致),而f0、f2虽然携带信号,但它们的信息与已选特征高度重叠,L1 的稀疏性偏好会用最少的特征完成判别,因此被舍弃。
这正是嵌入式特征选择的威力所在:模型在训练过程中自动完成了特征筛选。在原课程的乳腺癌数据集(30 维特征)上,同样的方法挑选出了 14 个有用特征,其余特征的重要度基本缩减为 0。
我们可以使用训练好的选择器来转换数据,得到只包含选中特征的新数据集:
from sklearn.feature_selection import SelectFromModel
from sklearn.linear_model import LogisticRegression
from sklearn.preprocessing import RobustScaler
from sklearn.model_selection import train_test_split
import pandas as pd
import numpy as np
# 代码块3:转换数据并验证
# 使用代码块2中训练好的选择器转换数据(此处为演示,实际应接续代码块2的逻辑)
# 为保持独立性,我们重新训练一个选择器并转换
X_data = np.array([
[0.2, 1.8, -0.9, -0.5, 0.10, 0.30, -0.10, 0.05, 0.40, -0.20],
[0.4, 1.6, -1.1, -0.7, 0.20, -0.20, 0.20, -0.15, -0.40, 0.30],
[0.3, 1.7, -0.8, -0.4, 0.15, 0.10, -0.30, 0.20, 0.30, -0.10],
[0.5, 1.5, -1.2, -0.8, 0.25, -0.30, 0.10, -0.05, -0.30, 0.40],
[0.25, 1.75, -1.0, -0.6, 0.05, 0.20, 0.00, 0.10, 0.35, 0.00],
[1.6, 0.3, 0.9, 0.7, 0.90, -0.10, 0.30, -0.20, -0.35, 0.10],
[1.8, 0.5, 1.1, 0.9, 1.00, 0.25, -0.20, 0.05, 0.45, -0.30],
[1.7, 0.2, 0.8, 0.6, 0.85, -0.25, 0.10, 0.15, -0.45, 0.20],
[1.9, 0.4, 1.2, 1.0, 1.10, 0.15, -0.15, -0.10, 0.25, 0.35],
[1.75, 0.35, 1.0, 0.8, 0.95, 0.05, 0.20, 0.00, -0.25, -0.05],
])
X_data = np.vstack([X_data]*3)
y_data = np.tile(np.array([0, 0, 0, 0, 0, 1, 1, 1, 1, 1]), 3)
feature_names = [f'f{i}' for i in range(10)]
X_df = pd.DataFrame(X_data, columns=feature_names)
y_series = pd.Series(y_data, name='target')
X_train, X_test, y_train, y_test = train_test_split(X_df, y_series, test_size=0.33, random_state=42)
scaler = RobustScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)
lr_l1 = LogisticRegression(penalty='l1', solver='liblinear', C=1.0, random_state=42, max_iter=1000)
selector = SelectFromModel(estimator=lr_l1, threshold=None)
selector.fit(X_train_scaled, y_train)
# 使用选择器转换数据,得到筛选后的特征子集
X_train_selected = selector.transform(X_train_scaled)
X_test_selected = selector.transform(X_test_scaled)
print(f"原始训练集形状: {X_train_scaled.shape}")
print(f"筛选后训练集形状: {X_train_selected.shape}")
print(f"筛选后测试集形状: {X_test_selected.shape}")
# 查看被选中的特征名
selected_feat_names = X_train.columns[selector.get_support()]
print(f"\n筛选后保留的特征: {list(selected_feat_names)}")
print("\n筛选后训练集前2行 (对应上述特征):")
print(X_train_selected[:2])原始训练集形状: (20, 10)
筛选后训练集形状: (20, 3)
筛选后测试集形状: (10, 3)
筛选后保留的特征: ['f1', 'f3', 'f4']
筛选后训练集前2行 (对应上述特征):
[[ 0.19047619 -0.14035088 -0.25 ]
[-0.76190476 0.9122807 0.9375 ]]基于树模型特征重要度的选择
除了线性模型,另一大类常用于嵌入式特征选择的模型是树模型及其集成方法,如随机森林(Random Forest)、梯度提升树(GBDT,如 GradientBoostingClassifier)。这类模型在训练后能够提供每个特征的“重要度”评分,该评分通常基于特征在树的分裂节点上带来的不纯度减少量(如基尼指数、信息增益)的累计值。
基于特征重要度进行筛选的优点是:树模型本身对特征幅度不敏感,通常无需预先进行幅度缩放。我们可以直接使用 SelectFromModel 并指定一个阈值(如‘median’、‘mean’或一个具体数值)来筛选重要度高于该阈值的特征。
from sklearn.model_selection import train_test_split
import pandas as pd
import numpy as np
# 代码块4:基于随机森林和GBDT的特征重要度选择
from sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier
from sklearn.feature_selection import SelectFromModel
# 重新构造数据(保持独立)
X_data = np.array([
[0.2, 1.8, -0.9, -0.5, 0.10, 0.30, -0.10, 0.05, 0.40, -0.20],
[0.4, 1.6, -1.1, -0.7, 0.20, -0.20, 0.20, -0.15, -0.40, 0.30],
[0.3, 1.7, -0.8, -0.4, 0.15, 0.10, -0.30, 0.20, 0.30, -0.10],
[0.5, 1.5, -1.2, -0.8, 0.25, -0.30, 0.10, -0.05, -0.30, 0.40],
[0.25, 1.75, -1.0, -0.6, 0.05, 0.20, 0.00, 0.10, 0.35, 0.00],
[1.6, 0.3, 0.9, 0.7, 0.90, -0.10, 0.30, -0.20, -0.35, 0.10],
[1.8, 0.5, 1.1, 0.9, 1.00, 0.25, -0.20, 0.05, 0.45, -0.30],
[1.7, 0.2, 0.8, 0.6, 0.85, -0.25, 0.10, 0.15, -0.45, 0.20],
[1.9, 0.4, 1.2, 1.0, 1.10, 0.15, -0.15, -0.10, 0.25, 0.35],
[1.75, 0.35, 1.0, 0.8, 0.95, 0.05, 0.20, 0.00, -0.25, -0.05],
])
X_data = np.vstack([X_data]*3)
y_data = np.tile(np.array([0, 0, 0, 0, 0, 1, 1, 1, 1, 1]), 3)
feature_names = [f'f{i}' for i in range(10)]
X_df = pd.DataFrame(X_data, columns=feature_names)
y_series = pd.Series(y_data, name='target')
X_train, X_test, y_train, y_test = train_test_split(X_df, y_series, test_size=0.33, random_state=42)
print("=== 随机森林特征重要度 ===")
# 训练随机森林模型
rf_model = RandomForestClassifier(n_estimators=20, max_depth=4, random_state=42)
rf_model.fit(X_train, y_train)
# 查看特征重要度
feat_importance_rf = pd.DataFrame({
'feature': X_train.columns,
'importance': rf_model.feature_importances_
}).sort_values('importance', ascending=False)
print("特征重要度排序:")
print(feat_importance_rf.to_string(index=False))
# 使用“中位数”阈值筛选
selector_rf_median = SelectFromModel(rf_model, threshold='median', prefit=True)
selected_rf_median = X_train.columns[selector_rf_median.get_support()]
print(f"\n随机森林选中特征 (重要度>中位数): {list(selected_rf_median)}")
print("\n=== GBDT特征重要度 ===")
# 训练GBDT模型
gbt_model = GradientBoostingClassifier(n_estimators=20, max_depth=3, random_state=42)
gbt_model.fit(X_train, y_train)
# 使用“均值”阈值筛选
selector_gbt_mean = SelectFromModel(gbt_model, threshold='mean', prefit=True)
selected_gbt_mean = X_train.columns[selector_gbt_mean.get_support()]
print(f"GBDT选中特征 (重要度>均值): {list(selected_gbt_mean)}")
# 尝试不同的阈值策略
print("\n=== 不同阈值策略对比 (以随机森林为例) ===")
selector_1 = SelectFromModel(rf_model, threshold=0.05, prefit=True)
print(f"阈值0.05选中的特征: {list(X_train.columns[selector_1.get_support()])}")
selector_2 = SelectFromModel(rf_model, threshold='1.5*median', prefit=True)
print(f"阈值‘1.5*median’选中的特征: {list(X_train.columns[selector_2.get_support()])}")=== 随机森林特征重要度 ===
特征重要度排序:
feature importance
f0 0.25
f1 0.25
f2 0.20
f4 0.20
f3 0.10
f5 0.00
f6 0.00
f7 0.00
f8 0.00
f9 0.00
随机森林选中特征 (重要度>中位数): ['f0', 'f1', 'f2', 'f3', 'f4']
=== GBDT特征重要度 ===
GBDT选中特征 (重要度>均值): ['f1', 'f2', 'f3', 'f4']
=== 不同阈值策略对比 (以随机森林为例) ===
阈值0.05选中的特征: ['f0', 'f1', 'f2', 'f3', 'f4']
阈值‘1.5*median’选中的特征: ['f0', 'f1', 'f2', 'f3', 'f4']从结果可以看出,树模型将全部重要度都赋予了有效的特征 f0-f4(f0、f1 各 0.25,f2、f4 各 0.20,f3 0.10),5 个噪声特征的重要度全部为 0。与 L1 正则化只保留 3 个特征相比,随机森林对冗余信号更”宽容”——f0、f2 在这里也拿到了重要度。这说明不同的嵌入法对同一组特征的取舍可能不同,实践中值得交叉对比。通过调整 threshold 参数,我们可以控制筛选的严格程度:本例中 threshold='median' 与 threshold=0.05 都保留了全部 5 个有效特征,而 GBDT 配合 threshold='mean' 保留了 4 个。
原课程将这一流程封装为工具方法 random_forest_importances(可直接从课程工具库调用),在乳腺癌数据集上选出的最重要特征编号依次为 27、22、7、20;GBDT、XGBoost、LightGBM 等模型也都能输出特征重要度,接口一致,大家可以自行尝试。
📝 动手练一练
- 概念辨析:嵌入法与过滤法、包裹法的主要区别是什么?在什么场景下你会优先考虑使用嵌入法?
👉 点击查看参考答案
主要区别:
- 过滤法:独立评估每个特征与目标的相关性,速度快,但忽略了特征间的相互作用。
- 包裹法:将特征选择视为一个搜索问题,使用模型性能来评估特征子集,计算成本高,但能考虑特征交互。
- 嵌入法:将特征选择过程嵌入到模型训练中,利用模型训练得到的系数或重要度来筛选特征。它在计算效率和考虑特征交互之间取得了较好的平衡。
优先使用嵌入法的场景:
- 当特征数量较多,包裹法计算成本过高时。
- 当你计划使用的模型本身就能提供特征重要性评估(如线性模型+L1、树模型)时。
- 当你希望特征选择过程与最终的建模算法保持一致时。
- 动手实验:在代码块4的随机森林示例中,如果将
threshold参数改为'0.8*mean',预计会选中多少个特征?请修改代码验证你的推断。
👉 点击查看参考答案
首先,我们需要计算随机森林特征重要度的均值。从输出可知,重要度分别约为:f0 (0.25), f1 (0.25), f2 (0.20), f4 (0.20), f3 (0.10),其余 5 个噪声特征均为 0,均值 = 1.00 / 10 = 0.10。0.8*mean 约为 0.08。重要度高于此阈值的特征有:f0 (0.25), f1 (0.25), f2 (0.20), f4 (0.20), f3 (0.10)。因此预计会选中 5 个特征。
验证代码如下(自包含,与上一块数据完全一致):
import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.feature_selection import SelectFromModel
# 与上一块相同的构造过程,保证答案代码可独立运行
X_data = np.array([
[0.2, 1.8, -0.9, -0.5, 0.10, 0.30, -0.10, 0.05, 0.40, -0.20],
[0.4, 1.6, -1.1, -0.7, 0.20, -0.20, 0.20, -0.15, -0.40, 0.30],
[0.3, 1.7, -0.8, -0.4, 0.15, 0.10, -0.30, 0.20, 0.30, -0.10],
[0.5, 1.5, -1.2, -0.8, 0.25, -0.30, 0.10, -0.05, -0.30, 0.40],
[0.25, 1.75, -1.0, -0.6, 0.05, 0.20, 0.00, 0.10, 0.35, 0.00],
[1.6, 0.3, 0.9, 0.7, 0.90, -0.10, 0.30, -0.20, -0.35, 0.10],
[1.8, 0.5, 1.1, 0.9, 1.00, 0.25, -0.20, 0.05, 0.45, -0.30],
[1.7, 0.2, 0.8, 0.6, 0.85, -0.25, 0.10, 0.15, -0.45, 0.20],
[1.9, 0.4, 1.2, 1.0, 1.10, 0.15, -0.15, -0.10, 0.25, 0.35],
[1.75, 0.35, 1.0, 0.8, 0.95, 0.05, 0.20, 0.00, -0.25, -0.05],
])
X_data = np.vstack([X_data]*3)
y_data = np.tile(np.array([0, 0, 0, 0, 0, 1, 1, 1, 1, 1]), 3)
feature_names = [f'f{i}' for i in range(10)]
X_df = pd.DataFrame(X_data, columns=feature_names)
y_series = pd.Series(y_data, name='target')
X_train, X_test, y_train, y_test = train_test_split(X_df, y_series, test_size=0.33, random_state=42)
rf_model = RandomForestClassifier(n_estimators=20, max_depth=4, random_state=42)
rf_model.fit(X_train, y_train)
selector_test = SelectFromModel(rf_model, threshold='0.8*mean', prefit=True)
selected_test = X_train.columns[selector_test.get_support()]
print(f"阈值‘0.8*mean’选中的特征数量: {len(selected_test)}")
print(f"具体特征: {list(selected_test)}")阈值‘0.8*mean’选中的特征数量: 5
具体特征: ['f0', 'f1', 'f2', 'f3', 'f4']运行结果与推断一致:选中了 [‘f0’, ‘f1’, ‘f2’, ‘f3’, ‘f4’] 这 5 个特征——恰好是全部有效特征,噪声特征无一入选。
本章小结
本节我们深入学习了嵌入式特征选择方法。嵌入法通过将特征选择过程与模型训练相结合,提供了一种高效且实用的特征筛选途径。我们重点掌握了两类实现:
- 基于正则化的方法:以逻辑回归+L1正则化为代表,通过惩罚项使不重要特征的系数归零。切记:使用前必须对特征进行幅度缩放。
- 基于特征重要度的方法:以随机森林、GBDT为代表,利用模型提供的特征重要度评分进行筛选。树模型对特征尺度不敏感,使用更为便捷。
scikit-learn 提供的 SelectFromModel 元变换器统一了这些方法的调用接口,通过灵活设置 threshold 参数,我们可以轻松控制筛选的严格程度。
📋 行动清单
- 在你的项目中尝试使用
SelectFromModel结合逻辑回归(L1)或随机森林进行特征选择。 - 对比特征选择前后模型的性能(如准确率、AUC)和训练速度,体会特征选择的价值。
- 练习使用不同的阈值策略(‘median’, ‘mean’, ‘1.5*median’, 具体数值)并观察筛选结果的差异。
—— 小象教研组