查看全课大纲(第 23 / 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.数据建模
包裹式特征选择方法
约 4 分钟
小象实战讲义 · 特征工程精讲与实践
在上一节我们学习了过滤式特征选择方法,它基于统计指标评估单个特征的重要性。但过滤法有一个明显的局限性:没有考虑特征之间的相互作用。本节我们将介绍包裹式特征选择方法,它会将特征选择看作一个特征子集搜索问题,直接使用机器学习模型来评估不同特征子集的效果,从而找到最优的特征组合。
💡 核心导读
- 包裹式特征选择的核心思想:将特征选择过程”包裹”在机器学习模型训练中,通过模型性能来评估特征子集的好坏
- 三种经典搜索策略:前向选择(逐步添加特征)、后向删除(逐步删除特征)、穷举搜索(遍历所有可能组合)
- 包裹法的优缺点:考虑特征交互但计算成本较高,适合特征数适中的场景
包裹式特征选择的基本原理
包裹式特征选择(Wrapper Methods)的核心思想是将特征选择过程”包裹”在机器学习模型的训练过程中。与过滤法不同,包裹法不是单独评估每个特征,而是评估不同特征子集组合的整体效果。
工作流程:
- 定义一个搜索策略(如前向选择、后向删除等)
- 生成一个候选特征子集
- 使用该特征子集训练模型
- 评估模型性能(如准确率、AUC等)
- 根据性能决定是否接受该特征子集
- 重复步骤2-5,直到满足停止条件
这种方法的优势在于它考虑了特征之间的相互作用,因为模型是在特征子集上整体训练的。但缺点也很明显:计算成本高,因为需要反复训练模型。
经典包裹式方法实现
我们将使用乳腺癌数据集来演示三种经典的包裹式特征选择方法。首先准备数据:
import pandas as pd
import numpy as np
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
# 加载乳腺癌数据集
data = load_breast_cancer()
X = pd.DataFrame(data.data, columns=data.feature_names)
y = pd.Series(data.target)
# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)
print(f"原始特征数: {X_train.shape[1]}")
print(f"训练集样本数: {X_train.shape[0]}, 测试集样本数: {X_test.shape[0]}")原始特征数: 30
训练集样本数: 455, 测试集样本数: 1141. 前向选择(Forward Selection)
前向选择是一种贪心算法,从空特征集开始,逐步添加对模型性能提升最大的特征。原课程使用 mlxtend 库的 SequentialFeatureSelector 实现,下面我们手写它的核心逻辑——这恰恰是把机制看得最清楚的方式:
import pandas as pd
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score
data = load_breast_cancer()
X = pd.DataFrame(data.data, columns=data.feature_names)
y = pd.Series(data.target)
X_train, X_val, y_train, y_val = train_test_split(X, y, test_size=0.2, random_state=42)
# 手写前向选择(演示用单次划分评估,工程实践建议交叉验证)
def forward_selection(X_train, y_train, X_val, y_val, k):
selected = []
remaining = list(X_train.columns)
while len(selected) < k:
best_feat, best_acc = None, -1.0
for feat in remaining:
cols = selected + [feat]
model = RandomForestClassifier(n_estimators=10, random_state=42)
model.fit(X_train[cols], y_train)
acc = accuracy_score(y_val, model.predict(X_val[cols]))
if acc > best_acc:
best_acc, best_feat = acc, feat
selected.append(best_feat)
remaining.remove(best_feat)
return selected
selected_forward = forward_selection(X_train, y_train, X_val, y_val, k=5)
print("前向选择选中的特征:")
for i, feat in enumerate(selected_forward, 1):
print(f"{i}. {feat}")前向选择选中的特征:
1. worst area
2. mean smoothness
3. mean concavity
4. mean compactness
5. area error前向选择的工作原理:
- 从空特征集开始
- 依次尝试添加每个未选中的特征
- 评估添加该特征后的模型性能
- 选择性能提升最大的特征加入特征集
- 重复步骤2-4,直到达到预设的特征数量或性能不再提升
2. 后向删除(Backward Elimination)
后向删除与前向选择相反,从全量特征开始,逐步删除对模型性能影响最小的特征:
import pandas as pd
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score
data = load_breast_cancer()
X = pd.DataFrame(data.data, columns=data.feature_names)
y = pd.Series(data.target)
X_train, X_val, y_train, y_val = train_test_split(X, y, test_size=0.2, random_state=42)
# 手写后向删除(每轮尝试删除一个特征,保留使验证性能最高的组合)
def backward_elimination(X_train, y_train, X_val, y_val, k):
remaining = list(X_train.columns)
while len(remaining) > k:
worst_feat, worst_acc = None, -1.0
for feat in remaining:
cols = [c for c in remaining if c != feat]
model = RandomForestClassifier(n_estimators=10, random_state=42)
model.fit(X_train[cols], y_train)
acc = accuracy_score(y_val, model.predict(X_val[cols]))
if acc > worst_acc:
worst_acc, worst_feat = acc, feat
remaining.remove(worst_feat)
return remaining
selected_backward = backward_elimination(X_train, y_train, X_val, y_val, k=10)
print(f"后向删除保留的{len(selected_backward)}个特征:")
for i, feat in enumerate(selected_backward, 1):
print(f"{i}. {feat}")后向删除保留的10个特征:
1. mean radius
2. mean symmetry
3. radius error
4. texture error
5. perimeter error
6. smoothness error
7. concave points error
8. symmetry error
9. worst area
10. worst concavity后向删除的工作原理:
- 从所有特征开始
- 依次尝试删除每个特征
- 评估删除该特征后的模型性能
- 删除对性能影响最小的特征
- 重复步骤2-4,直到达到预设的特征数量或性能开始显著下降
3. 穷举搜索(Exhaustive Search)
穷举搜索会尝试所有可能的特征组合,找到性能最优的特征子集。这种方法计算成本极高,只适用于特征数很少的情况。原课程使用 mlxtend 的 ExhaustiveFeatureSelector,用标准库 itertools.combinations 就能等价实现:
import pandas as pd
from itertools import combinations
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score
data = load_breast_cancer()
X = pd.DataFrame(data.data, columns=data.feature_names)
y = pd.Series(data.target)
X_train, X_val, y_train, y_val = train_test_split(X, y, test_size=0.2, random_state=42)
# 穷举搜索:只取前 6 个特征、组合规模 2~4,控制计算量
X_small = X_train.iloc[:, :6]
cols_small = list(X_small.columns)
best_combo, best_acc = None, -1.0
for r in (2, 3, 4):
for combo in combinations(cols_small, r):
model = RandomForestClassifier(n_estimators=10, random_state=42)
model.fit(X_train[list(combo)], y_train)
acc = accuracy_score(y_val, model.predict(X_val[list(combo)]))
if acc > best_acc:
best_acc, best_combo = acc, list(combo)
print(f"穷举搜索找到的最佳特征组合(从{len(cols_small)}个特征中):")
for i, feat in enumerate(best_combo, 1):
print(f"{i}. {feat}")
print(f"最佳准确率: {best_acc:.4f}")穷举搜索找到的最佳特征组合(从6个特征中):
1. mean radius
2. mean texture
3. mean perimeter
4. mean compactness
最佳准确率: 0.9649除了上述三种经典的搜索策略,包裹式家族中还有一种非常直观的实验性方法——通过打乱单个特征的值来观察模型性能的下降幅度(特征乱序法,也即课程后文的「混序法」),我们将在本章最后一节结合代码详细学习它。
包裹式方法的优缺点与适用场景
优点
- 考虑特征交互:直接使用模型评估,能捕捉特征之间的相互作用
- 找到最优组合:通过搜索策略找到性能最优的特征子集
- 与模型强相关:选择出的特征对特定模型是最优的
缺点
- 计算成本高:需要反复训练模型,特征数多时计算量巨大
- 容易过拟合:在训练集上搜索最优特征子集,可能在测试集上表现不佳
- 随机性影响:模型训练本身的随机性会影响特征选择结果
适用场景
- 特征数量适中(通常少于50个)
- 计算资源充足
- 对模型性能要求极高,愿意为小幅提升付出计算代价
- 特征之间存在复杂的相互作用
📝 动手练一练
- 概念辨析:过滤式特征选择和包裹式特征选择的核心区别是什么?在什么情况下你会选择使用包裹式方法?
👉 点击查看参考答案
核心区别:
- 过滤式:基于统计指标(如方差、相关系数、卡方检验等)单独评估每个特征,不考虑特征之间的相互作用,计算效率高
- 包裹式:使用机器学习模型评估特征子集的整体性能,考虑特征交互,但计算成本高
选择包裹式的情况:
- 特征数量适中(少于50个),计算资源充足
- 特征之间存在复杂的相互作用,过滤法可能漏掉重要组合
- 对模型性能有极致要求,愿意为小幅性能提升付出计算代价
- 需要为特定模型(如随机森林、神经网络)找到最优特征子集
- 代码实践:修改前向选择的代码,尝试选择不同数量的特征(如3个、8个、15个),观察选中的特征有何变化,并思考如何确定最优的特征数量?
👉 点击查看参考答案
import pandas as pd
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score
data = load_breast_cancer()
X = pd.DataFrame(data.data, columns=data.feature_names)
y = pd.Series(data.target)
X_train, X_val, y_train, y_val = train_test_split(X, y, test_size=0.2, random_state=42)
def forward_selection(X_train, y_train, X_val, y_val, k):
selected = []
remaining = list(X_train.columns)
while len(selected) < k:
best_feat, best_acc = None, -1.0
for feat in remaining:
cols = selected + [feat]
model = RandomForestClassifier(n_estimators=10, random_state=42)
model.fit(X_train[cols], y_train)
acc = accuracy_score(y_val, model.predict(X_val[cols]))
if acc > best_acc:
best_acc, best_feat = acc, feat
selected.append(best_feat)
remaining.remove(best_feat)
return selected
# 以 k=3 为例(读者可自行把 k 改成 8、15 观察选中的特征有何变化)
selected_3 = forward_selection(X_train, y_train, X_val, y_val, k=3)
print("选择3个特征时的结果:")
for i, feat in enumerate(selected_3, 1):
print(f" {i}. {feat}")
# 确定最优特征数量的方法:
# 1. 绘制特征数量 vs 模型性能曲线,找到性能拐点
# 2. 使用验证集或交叉验证评估不同特征数量的泛化性能
# 3. 考虑业务约束(如计算成本、可解释性要求)选择3个特征时的结果:
1. worst area
2. mean smoothness
3. mean concavity通过实验可以发现:
- 选择较少的特征(如3个)时,选中的通常是最重要的核心特征
- 随着特征数量增加,会逐步加入次重要的特征
- 存在一个”拐点”,超过该点后增加特征带来的性能提升很小
- 最优特征数量需要平衡性能、计算成本和模型复杂度
本章小结
本节我们深入学习了包裹式特征选择方法,这是特征工程中考虑特征相互作用的重要技术。包裹法通过将特征选择过程”包裹”在模型训练中,能够找到对特定模型最优的特征子集。
📋 行动清单
- 在本地运行包裹式特征选择的代码,理解前向选择、后向删除和穷举搜索的工作原理
- 思考在你们当前的项目中,哪些场景适合使用包裹式特征选择,并设计实验验证
- 记录不同特征选择方法(过滤式、包裹式)的计算时间和性能差异,建立选择方法论
包裹式特征选择虽然计算成本较高,但在特征数量适中、特征交互复杂、对性能要求极高的场景下,它往往能带来显著的性能提升。下一节我们将学习嵌入式特征选择方法,它通过模型正则化来自动完成特征选择。
—— 小象教研组