1.方差筛选
2.皮尔逊相关系数筛选
3.lasso 筛选
树模型重要性
5.shap 重要性
6.递归特征消除 REF
特征筛选:原理、方法与实践
核心概念:什么是特征筛选?为什么要这么做?
想象一下,原始数据(如 data.csv)如同一个人的全部信息,涵盖身高、体重、年龄、收入、爱好、有无车房等几十个特征。特征筛选的目标,是从这众多特征中挑出对解决问题(如预测信用违约)最有用的特征,去除无关紧要甚至有干扰的特征。
为什么要进行特征筛选?
- 提升模型性能和精度
- 减少噪声:并非所有特征都有用,像"鞋码"与信用违约可能毫无关联,这类无关特征就是"噪声",留存可能误导模型,降低预测准确性。
- 避免过拟合:特征过多,模型易在训练数据上学得"太好",记住训练数据的细节(包括噪声),但在新数据(测试集)上表现不佳。减少特征可降低模型复杂度,增强泛化能力。
- 提高模型训练效率
特征越少,计算的数据量越小。对于大型数据集和复杂模型,减少特征数量可大幅缩短训练时间,节省计算资源,在工业界尤为重要。
- 增强模型的可解释性
当模型输入仅含少数关键特征时,更易理解模型的决策方式。例如,若最终只剩"年收入""破产次数""信用分"三个特征,就能明确模型主要依据这三点判断信用风险,对业务分析和决策帮助很大。
特征筛选方式
方差筛选
from sklearn.feature_selection import VarianceThreshold # 方差筛选工具,用于剔除方差小的特征
什么是方差筛选?
-
方差 (Variance):统计学中,方差衡量一组数据的离散程度或变化范围。
-
方差大:特征数值在不同样本间变化大,波动剧烈。
-
方差小:特征数值在所有样本中相近,几乎无变化。
-
筛选逻辑:方差小的特征在所有客户身上表现相近,如数据集中 99% 的客户"信用问题数量"为 0,该特征方差接近 0,对区分"好客户"和"坏客户"无有用信息。VarianceThreshold 设定方差门槛 (threshold),计算每个特征的方差,删除方差低于门槛的特征,保留方差大于门槛、有足够变化的特征。
selector = VarianceThreshold(threshold=0.01)
代码示例
rf_pred_var = rf_model_var.predict(X_test_var)
print(classification_report(y_test, rf_pred_var))
print(confusion_matrix(y_test, rf_pred_var))
结论
效果和基准模型相近,略有下降,但训练时间可能稍快。说明这种"一刀切"方法可能去除了一些虽方差小但有用的信息。
皮尔逊相关系数筛选
核心思想
计算每个特征和目标(是否违约)的相关性,只保留与目标最相关的特征,比方差筛选更进了一步,考虑了特征与目标的关系。
from sklearn.feature_selection import SelectKBest, f_classif
selector = SelectKBest(score_func=f_classif, k=10) # 选择与目标最相关的 k 个特征
X_train_corr = selector.fit_transform(X_train, y_train)
统计学原理
score_func=f_classif 代表用于分类问题的方差分析 F 值(ANOVA F - value),是衡量数值型特征和分类型目标之间相关性的统计方法。
核心问题:如何衡量"相关性"?
当目标为分类(如预测客户"违约"或"不违约")时,一个数值特征(如"年收入")与目标"相关"意味着"违约"群体和"不违约"群体的平均年收入有显著差异。若差异大,该特征是好的区分指标,相关性强;若差异小,则相关性弱。
ANOVA F- value 计算原理
ANOVA 核心思想是比较不同组之间的差异,在该场景中是比较"违约组"和"不违约组"在某个特征上的差异,通过计算 F 值实现:
- 组间差异 (Between - group variance):衡量不同组均值之间的差异大小,如计算"违约组"和"不违约组"的平均年收入差异,组间差异越大,特征区分能力可能越强。
- 组内差异 (Within - group variance):衡量每个组内部数据的分散程度,如分别计算"违约组"和"不违约组"内部成员年收入的离散程度并综合,组内差异越小,组内成员越相似,数据越集中。
F 值含义
- F 值很大:组间差异 >> 组内差异,说明特征能清晰区分两个群体,相关性强。
- F 值很小 (接近 1 或更小):组间差异 ≈ 组内差异,说明特征无法区分两个群体,相关性弱。
结论
效果比基准模型略有提升,特征数量大幅减少,训练速度更快,是不错的结果。
Lasso 筛选 (基于 L1 正则化)
核心思想
训练线性模型(Lasso 回归)时,自动给不重要的特征分配为 0 的权重(系数),相当于自动"关掉"这些特征,只需保留权重不为 0 的特征。
from sklearn.linear_model import Lasso
from sklearn.feature_selection import SelectFromModel
lasso = Lasso(alpha=0.01, random_state=42) # alpha 是惩罚强度,可以调
selector = SelectFromModel(lasso)
selector.fit(X_train, y_train)
结果分析
- 特征数量减至 7 个,为最少。
- 对于标签 1 的 recall 达到 0.34。
结论
效果最佳!大幅减少特征数量,显著提升模型召回率,说明 Lasso 成功抓住最核心、最关键的预测因子。
其他特征筛选方法
基于树模型的特征筛选 (回顾与深化)
这是 day19 讲过的方法,先简单回顾以作铺垫。
核心思想
利用决策树、随机森林等模型训练后自带的 feature_importances_ 属性,该重要性分数代表每个特征在模型决策(分裂节点)时的贡献大小,贡献越大,特征越重要。
工作流程
- 用全部特征训练树模型。
- 提取
feature_importances_。 - 根据重要性分数排序,保留前 N 个特征。
优缺点
- 优点:实现简单、速度快、效果通常不错。
- 缺点
- 有偏见:可能偏向取值类别更多(高基数性)的特征。
- 不稳定:数据有微小变动时,重要性排序可能变化较大。
SHAP 重要性筛选 (更公平、更准确的"法官")
SHAP (SHapley Additive exPlanations) 是更先进、可靠的模型解释工具,也可用于特征筛选。
核心思想
- 源于博弈论中的"沙普利值",要公平评估团队中每个成员(特征)的贡献,需考虑所有可能的"出场"组合。
- 对于模型预测,SHAP 不像
feature_importances_只给出模糊的"平均贡献",而是能精确计算每个特征对每个样本每次预测的贡献。 - 全局重要性 :将每个特征在所有样本上贡献的"分数"绝对值取平均,得到比传统
feature_importances_更稳定、公平的全局特征重要性。
工作流程
- 训练模型(如随机森林)。
- 创建 SHAP 的 Explainer(解释器),指定要解释的模型。
- 用解释器计算所有样本的 SHAP 值。
- 处理 SHAP 值,得到全局特征重要性排序。
- 根据排序,保留前 N 个特征。
关键代码示例
import shap
# 1. 假设 rf_model 是一个已经训练好的随机森林模型
# 2. 创建解释器
explainer = shap.TreeExplainer(rf_model)
# 3. 计算 SHAP 值 (通常在测试集或一个代表性样本上计算)
shap_values = explainer.shap_values(X_test)
# 4. 得到全局重要性 (通过 SHAP 自带的条形图)
# shap_values[1] 代表针对"正类别"(比如违约=1)的 SHAP 值
shap.summary_plot(shap_values[1], X_test, plot_type="bar")
优缺点
- 优点
- 理论坚实:结果更公平、一致、可靠。
- 信息丰富:不仅知特征重要性,还知其对预测结果的正负作用。
- 缺点 :计算量大,相比直接获取
feature_importances_慢得多。
递归特征消除 (Recursive Feature Elimination - RFE)
这是经典且强大的"包裹式 (Wrapper)"特征选择方法。
核心思想
如同"末位淘汰赛",不断循环:训练模型 -> 淘汰最弱特征 -> 再训练 -> 再淘汰,直至剩下指定数量的特征。该方法认为一次性计算的特征重要性可能不准确,因为特征间相互影响,去掉一个最弱特征后,其他特征重要性排名可能改变。
工作流程
- 设定目标 :确定最终要保留的特征数量(如
n_features_to_select = 10)。 - 第一轮:用全部特征训练模型,得到特征重要性。
- 淘汰:找到最不重要的特征,从特征列表中永久删除。
- 下一轮:用剩余特征重新训练模型,得到新的特征重要性。
- 循环:重复第 3 步和第 4 步,每轮淘汰一个最弱特征,直至特征数量达到目标。
代码示例
from sklearn.feature_selection import RFE
from sklearn.ensemble import RandomForestClassifier
# 1. 创建一个用于 RFE 内部循环训练的模型
model_for_rfe = RandomForestClassifier(random_state=42)
# 2. 创建 RFE 选择器
# estimator: 用来进行每一轮评估的模型
# n_features_to_select: 最终要保留的特征数量
# step=1: 每一轮淘汰掉 1 个最弱的特征
rfe_selector = RFE(estimator=model_for_rfe, n_features_to_select=10, step=1)
# 3. 在训练数据上执行整个 RFE 流程
rfe_selector.fit(X_train, y_train)
# 4. 查看被选中的特征
selected_features_rfe = X_train.columns[rfe_selector.support_]
print(f"RFE 选中的特征: {selected_features_rfe.tolist()}")
优缺点
- 优点
- 考虑特征间交互:每轮重新训练模型,能更好发现一组"组合起来"最强大的特征子集。
- 效果通常很好:被认为是效果顶尖的特征选择方法之一。
- 缺点:非常慢,原始特征多的情况下,需训练模型成百上千次,计算成本极高。