1. 为什么你的模型需要特征选择?

做过机器学习项目的朋友应该都遇到过这种情况:数据集里塞满了各种特征,有些是业务专家精心设计的,有些是拍脑袋随便加的,还有些是数据预处理时自动生成的。我去年做一个电商用户流失预测项目时,原始数据经过特征工程后膨胀到187个特征,结果模型训练不仅慢得像蜗牛,预测效果还特别不稳定。

这就是典型的"特征冗余"问题。多余的特征就像噪音,会干扰模型找到真正的规律。更糟糕的是,某些高度相关的特征还会导致多重共线性,让模型参数变得不可靠。这时候就需要特征选择出场了——它就像个精明的采购员,帮我们从杂货铺般的特征堆里挑出真正有用的"好货"。

在sklearn的工具箱里,递归特征消除法(RFE)是我最常用的"采购员"之一。它采用"逐步淘汰"的策略,先让所有特征参与建模,然后反复剔除最不重要的特征,直到剩下最优组合。这种方法特别适合特征间存在复杂交互的场景,比如我用随机森林做金融风控模型时,RFE总能找出那些意想不到却又至关重要的特征组合。

2. RFE的工作原理:像剥洋葱一样选特征

2.1 递归消除的核心逻辑

想象你在玩一个"猜价格"游戏:面前摆着20件商品,每次可以去掉一个最不可能影响总价的物品。RFE的工作方式就很类似:

  1. 先用所有特征训练模型(比如随机森林)
  2. 计算每个特征的重要性得分
  3. 淘汰得分最低的那个特征
  4. 用剩下的特征重复上述过程

这个递归过程会一直持续,直到达到预设的特征数量。我在实践中发现,配合交叉验证的RFECV效果更好,它能自动确定最优特征数量,避免人工设定的主观性。

2.2 与过滤式方法的区别

常见的特征选择方法还有卡方检验、互信息法等过滤式方法。它们就像是用筛子过一遍特征,简单快速但有个致命缺点:不考虑特征组合效应。而RFE这类封装式方法会把特征选择过程融入模型训练,考虑特征间的协同作用。有次我做医疗诊断模型时,单看各个血液指标都不显著,但RFE选出的组合却能准确预测病情。

3. 手把手实现RFE特征选择

3.1 数据准备与标准化

我们先加载示例数据集WFs1.csv(你可以替换成自己的数据)。注意第一步永远是标准化,这对基于距离的模型尤为重要:

import pandas as pd
from sklearn.preprocessing import StandardScaler

# 读取数据
data = pd.read_csv("WFs1.csv")
X = data.iloc[:, 1:]  # 特征
y = data.iloc[:, 0]   # 标签

# 标准化处理
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)

3.2 构建基模型

RFE需要依赖一个能输出特征重要性的基模型。随机森林是我的首选,它对异常值不敏感且能自然输出特征重要性:

from sklearn.ensemble import RandomForestClassifier

# 初始化随机森林
rfc = RandomForestClassifier(n_estimators=100, random_state=42)

3.3 执行RFECV交叉验证

这是最省心的方式,自动确定最优特征数量:

from sklearn.feature_selection import RFECV

# 设置5折交叉验证
selector = RFECV(estimator=rfc, step=1, cv=5, scoring='accuracy')
selector.fit(X_scaled, y)

print("最优特征数量:", selector.n_features_)
print("特征排名:", selector.ranking_)

3.4 可视化选择过程

为了更直观,我们可以绘制特征数量与模型得分的关系曲线:

import matplotlib.pyplot as plt

plt.figure(figsize=(10, 6))
plt.plot(range(1, len(selector.grid_scores_) + 1), selector.grid_scores_)
plt.xlabel("特征数量")
plt.ylabel("交叉验证得分")
plt.show()

4. 实战中的经验与陷阱

4.1 基模型的选择技巧

虽然随机森林很常用,但不同场景需要灵活调整:

  • 线性问题:用Lasso回归作为基模型
  • 高维稀疏数据:考虑线性SVM
  • 时间序列:尝试LightGBM

我曾在一个NLP项目里犯过错——用随机森林处理文本TF-IDF特征,结果完全失效。后来换成线性模型作为RFE基模型,效果立竿见影。

4.2 特征数量的动态调整

RFECV默认会保留所有特征直到得分下降,这可能导致选择过多特征。我通常设置最小特征数约束:

selector = RFECV(rfc, step=1, cv=5, min_features_to_select=5)

4.3 处理特征相关性

当特征高度相关时,RFE可能随机保留其中一个。这时可以:

  1. 先做聚类去除冗余特征
  2. 使用稳定性选择方法
  3. 人工干预选择业务可解释的特征

5. 进阶技巧:RFE与其他方法的组合

5.1 预过滤+递归消除

对于超多特征(比如>1000)的数据集,我会先用方差阈值或互信息做初步筛选:

from sklearn.feature_selection import SelectKBest, mutual_info_classif

# 先用互信息选出前200个特征
pre_select = SelectKBest(mutual_info_classif, k=200)
X_filtered = pre_select.fit_transform(X_scaled, y)

# 再对筛选后的特征做RFE
selector.fit(X_filtered, y)

5.2 集成特征重要性

不同模型对特征重要性的评估可能不同。我的解决方案是:

  1. 用多种基模型分别做RFE
  2. 统计各特征被选中的频率
  3. 选择高频特征作为最终集合

5.3 时间序列特征选择

处理时间数据时,我会引入时间交叉验证:

from sklearn.model_selection import TimeSeriesSplit

tscv = TimeSeriesSplit(n_splits=5)
selector = RFECV(rfc, step=1, cv=tscv)

6. 性能优化与加速技巧

当特征量很大时,RFE可能运行缓慢。这几个方法帮我节省过大量时间:

  1. 并行化处理:设置n_jobs参数
rfc = RandomForestClassifier(n_jobs=-1)
  1. 早期停止:当连续N轮分数不再提升时停止
from sklearn.feature_selection import RFE

selector = RFE(rfc, n_features_to_select=10, step=5)  # 每次删除5个特征
  1. 采样评估:先用部分数据做快速评估
from sklearn.model_selection import train_test_split

X_sample, _, y_sample, _ = train_test_split(X, y, train_size=0.3)
selector.fit(X_sample, y_sample)

7. 业务场景中的特殊处理

在实际项目中,单纯依赖算法选择可能出问题。比如我做信贷风控时,虽然某些敏感特征(如性别)被RFE排除了,但合规要求必须保留。这时就需要:

  1. 设置必须保留的特征列表
  2. 对可选特征进行RFE选择
  3. 人工复核业务合理性

另一个经验是:对于关键业务指标对应的特征,即使重要性不高也建议保留,因为模型的可解释性有时比绝对精度更重要。

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐