想象你要做一道美食,面对琳琅满目的食材,优秀的厨师不会把所有原料都扔进锅里,而是会选择最适合的几种。
在机器学习中,特征选择就是这个挑选过程,从原始数据中选择对预测目标最有用的特征(列),就像挑选优质食材一样重要。
1. 什么是特征选择?
特征选择是机器学习中一个至关重要的步骤,它从原始数据的众多特征中挑选出对模型最有价值的子集。
简单来说,就是从一堆可能影响结果的因素中,找出那些真正重要的因素,把不重要的、重复的或者有干扰的特征去掉。
为什么要做特征选择呢?主要有以下几个原因:
- 提高模型性能:少而精的特征能帮助模型更专注于重要的信息,避免被无关特征干扰,从而提高准确性。
- 加快训练速度:特征少了,模型需要处理的数据量就小了,训练时间自然也就缩短了。
- 降低过拟合风险:过多的特征可能让模型记住噪声,而不是学习到真正的规律。特征选择能帮助模型更好地泛化。
- 提升可解释性:特征少了,模型的决策过程更容易理解,这对很多实际应用场景非常重要。
2. 三大特征选择方法
根据特征选择与模型训练过程的关系,主要可以分为以下三类方法:
2.1. 过滤式:先"筛"后"用"
过滤式方法就像用筛子筛沙子一样,先根据特征本身的统计特性对特征进行评估和筛选,然后再把选出来的特征交给模型使用。
这个过程完全独立于机器学习模型。
常见的过滤式方法包括:
- 方差阈值:去掉那些几乎不变的特征(方差低于某个阈值)
- 卡方检验:评估分类问题中特征与目标变量的独立性
- 相关系数:计算特征与目标变量之间的相关性
下面的代码演示如何使用过滤式方法来进行特征选择,使用卡方检验(SelectBest
):
from sklearn.datasets import load_iris
from sklearn.feature_selection import SelectKBest, f_classif
from sklearn.model_selection import train_test_split
# 加载数据集
iris = load_iris()
X, y = iris.data, iris.target
# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
# 创建过滤式选择器,选择2个最佳特征
selector = SelectKBest(score_func=f_classif, k=2)
X_train_selected = selector.fit_transform(X_train, y_train)
X_test_selected = selector.transform(X_test)
# 查看选中的特征
selected_features = selector.get_support(indices=True)
print(f"选中的特征索引: {selected_features}")
print(f"特征得分: {selector.scores_}")
# 输出特征选择后的数据维度
print(f"原始训练集特征数: {X_train.shape[1]}")
print(f"选择后训练集特征数: {X_train_selected.shape[1]}")
## 输出结果:
'''
选中的特征索引: [2 3]
特征得分: [ 74.7572012 33.41979913 713.45534904 526.54162416]
原始训练集特征数: 4
选择后训练集特征数: 2
'''
最后选择的特征是2
和3
,也就是后2个特征(特征索引是从0
开始的)。
从特征得分来看看,后2个特征也是得分最高的。
2.2. 包裹式:带着模型一起选
包裹式方法就像带着模型去"试衣"一样,把特征子集当作不同的"衣服",让模型试穿(训练)后看看效果如何。
根据模型的表现(比如准确性)来决定哪些特征组合最好。
常见的包裹式方法包括:
- 递归特征消除(
RFE
):不断移除最不重要的特征,直到达到指定数量 - 穷举搜索:尝试所有可能的特征组合(计算成本很高)
下面的代码示例使用递归特征消除(RFE
):
from sklearn.feature_selection import RFE
from sklearn.ensemble import RandomForestClassifier
# 创建随机森林分类器
model = RandomForestClassifier(random_state=42)
# 创建递归特征消除选择器
selector = RFE(model, n_features_to_select=2, step=1)
selector = selector.fit(X_train, y_train)
# 查看选中的特征
selected_features = selector.get_support(indices=True)
print(f"选中的特征: {selected_features}")
print(f"特征排名: {selector.ranking_}")
# 特征选择后的数据
X_train_selected = selector.transform(X_train)
X_test_selected = selector.transform(X_test)
# 输出特征选择后的数据维度
print(f"原始训练集特征数: {X_train.shape[1]}")
print(f"选择后训练集特征数: {X_train_selected.shape[1]}")
## 输出结果:
'''
选中的特征: [2 3]
特征排名: [2 3 1 1]
原始训练集特征数: 4
选择后训练集特征数: 2
'''
选择的特征也是2
和3
,从特征排名来看,前两个特征排名2
和3
,后两个特征并列排名第一
。
2.3. 嵌入式:在模型里自然选择
嵌入式方法就像在模型里内置了一个"挑食"机制,让模型在训练过程中自然地倾向于使用某些特征,而忽略其他特征。
这种方法通常通过正则化来实现。
常见的嵌入式方法包括:
- L1正则化(
Lasso
):会自动将不重要特征的系数缩放到零 - 树模型中的特征重要性:如随机森林、梯度提升树等模型自带的特征重要性评分
代码示例:
from sklearn.feature_selection import SelectFromModel
from sklearn.linear_model import LassoCV
# 创建带L1正则化的逻辑回归模型
model = LassoCV(random_state=42)
# 创建嵌入式选择器
selector = SelectFromModel(model, threshold='median')
selector = selector.fit(X_train, y_train)
# 查看选中的特征
selected_features = selector.get_support(indices=True)
print(f"选中的特征: {selected_features}")
# 特征选择后的数据
X_train_selected = selector.transform(X_train)
X_test_selected = selector.transform(X_test)
# 输出特征选择后的数据维度
print(f"原始训练集特征数: {X_train.shape[1]}")
print(f"选择后训练集特征数: {X_train_selected.shape[1]}")
## 输出结果:
'''
选中的特征: [2 3]
原始训练集特征数: 4
选择后训练集特征数: 2
'''
同样,最终选择的特征也是2
和3
。
3. 三种方法的对比
选择哪种特征选择方法,取决于你的具体需求和场景,下面是三种方法的比较:
方法类型 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
过滤式 | 计算效率高,不依赖模型 | 可能忽略特征与模型的关系 | 特征数量较少,对模型不敏感的情况 |
包裹式 | 直接考虑模型性能,效果通常较好 | 计算开销大,容易过拟合 | 特征数量适中,对性能要求高的情况 |
嵌入式 | 计算效率较高,考虑了特征与模型的关系 | 通常需要模型本身支持特征选择 | 需要模型具有稀疏性的情况 |
4. 总结
特征选择是机器学习流程中不可忽略的重要环节。
在实际应用中,我们可以尝试多种方法,观察它们对模型性能的影响,有时候,结合多种方法甚至能取得更好的效果。
记住,特征选择不是一成不变的规则,而是一门需要根据数据和模型不断调整的艺术。
原创作者: wang_yb 转载于: https://www.cnblogs.com/wang_yb/p/18908033