前言:
在昨天我们提到了如何利用聚类方法来获得新的、信息量更多的特征以此来提升模型的各项性能指标,本文将采用相反的思路,通过一些常见的特征筛选方法减少部分特征以筛选出真正有信息的特征,进而减少计算量、提升模型的精度,相应的特征越少,越容易理解模型决策逻辑。
一、几种常见的算法
1.方差筛选
解决什么问题:剔除"几乎不变"的无信息特征
例子:用户ID字段(每个值都唯一但无预测力)
为什么需要:先做数据清洗,避免噪音进入后续流程
2.皮尔逊相关系数筛选
解决什么问题:快速找到与目标有线性关系的特征
例子:房价预测中,面积与价格的高度线性相关
为什么需要:简单直观,统计意义明确,适合初步探索
3.Lasso筛选
解决什么问题:
高维数据(特征数 >> 样本数)
自动特征选择 + 防止过拟合
独特价值:L1正则化产生稀疏解,天然适合特征选择
场景:基因数据(2万个基因→几百个样本)
4.树模型重要性
解决什么问题:捕捉非线性关系和特征交互
对比:皮尔逊只能发现线性,树模型能发现复杂模式
例子:金融风控中,年龄与收入的交互作用对风险的影响
为什么需要:现实世界的关系大多是复杂的、非线性的
5.SHAP重要性
解决什么问题:传统特征重要性的公平分配问题
独特价值:
基于博弈论,理论严谨
既能全局解释,也能局部解释
可比性强(不同模型间的SHAP值可比)
例子:医疗诊断中,需要知道每个特征如何影响单个患者的预测
6.递归特征消除(RFE)
解决什么问题:寻找最优特征子集,最大化模型性能
工作方式:从全部特征开始,递归剔除最不重要特征
为什么需要:贪婪搜索特征组合,通常得到性能更好的子集
对比:过滤法独立评估每个特征,RFE考虑特征组合效应
具体的各个方法的应用场景:
1. 数据清洗 → 方差筛选(移除无信息特征)
2. 相关性分析 → 皮尔逊系数(了解线性关系,去除高相关冗余)
3. 非线性筛选 → 树模型重要性(捕捉复杂关系)
4. 精筛选 → Lasso 或 RFE(基于模型性能)
5. 可解释性 → SHAP(分析重要特征如何影响预测)
二、具体实现
作业:我们采取对心脏病数据集进行相应的特征筛选,并对比筛选前后模型的精度。
1、默认情况:
# 先运行之前预处理好的代码 import pandas as pd #用于数据处理和分析,可处理表格数据。 import numpy as np #用于数值计算,提供了高效的数组操作。 import matplotlib.pyplot as plt #用于绘制各种类型的图表 import seaborn as sns #基于matplotlib的高级绘图库,能绘制更美观的统计图形。 import warnings from sklearn.utils import resample from sklearn.model_selection import train_test_split warnings.filterwarnings("ignore") # 设置中文字体(解决中文显示问题) plt.rcParams['font.sans-serif'] = ['SimHei'] # Windows系统常用黑体字体 plt.rcParams['axes.unicode_minus'] = False # 正常显示负号 df = pd.read_csv(r'D:\Python60DaysChallenge-main\heart.csv') X = df.iloc[:, :-1] y = df.iloc[:, -1] # 切分数据 X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42) from sklearn.ensemble import RandomForestClassifier #随机森林分类器 from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score # 用于评估分类器性能的指标 from sklearn.metrics import classification_report, confusion_matrix #用于生成分类报告和混淆矩阵 import warnings #用于忽略警告信息 warnings.filterwarnings("ignore") # 忽略所有警告信息 # --- 1. 默认参数的随机森林 --- # 评估基准模型,这里确实不需要验证集 print("--- 1. 默认参数随机森林 (训练集 -> 测试集) ---") import time # 这里介绍一个新的库,time库,主要用于时间相关的操作,因为调参需要很长时间,记录下会帮助后人知道大概的时长 start_time = time.time() # 记录开始时间 rf_model = RandomForestClassifier(random_state=42) rf_model.fit(X_train, y_train) # 在训练集上训练 rf_pred = rf_model.predict(X_test) # 在测试集上预测 end_time = time.time() # 记录结束时间 print(f"训练与预测耗时: {end_time - start_time:.4f} 秒") print("\n默认随机森林 在测试集上的分类报告:") print(classification_report(y_test, rf_pred)) print("默认随机森林 在测试集上的混淆矩阵:") print(confusion_matrix(y_test, rf_pred))运行结果:
2、方差筛选:
运行代码:
# 先运行之前预处理好的代码 import pandas as pd #用于数据处理和分析,可处理表格数据。 import numpy as np #用于数值计算,提供了高效的数组操作。 import matplotlib.pyplot as plt #用于绘制各种类型的图表 import seaborn as sns #基于matplotlib的高级绘图库,能绘制更美观的统计图形。 import warnings from sklearn.utils import resample from sklearn.model_selection import train_test_split warnings.filterwarnings("ignore") # 设置中文字体(解决中文显示问题) plt.rcParams['font.sans-serif'] = ['SimHei'] # Windows系统常用黑体字体 plt.rcParams['axes.unicode_minus'] = False # 正常显示负号 df = pd.read_csv(r'D:\Python60DaysChallenge-main\heart.csv') X = df.iloc[:, :-1] y = df.iloc[:, -1] # 切分数据 X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42) # 打印标题,表明这是方差筛选的部分 print("--- 方差筛选 (Variance Threshold) ---") # 导入需要的工具库 from sklearn.feature_selection import VarianceThreshold # 方差筛选工具,用于剔除方差小的特征 import time # 用于记录代码运行时间,方便比较效率 from sklearn.ensemble import RandomForestClassifier #随机森林分类器 from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score # 用于评估分类器性能的指标 from sklearn.metrics import classification_report, confusion_matrix #用于生成分类报告和混淆矩阵 import warnings #用于忽略警告信息 # 记录开始时间,后面会计算整个过程耗时 start_time = time.time() # 创建方差筛选器,设置方差阈值为0.01 # 阈值是指方差的最小值,低于这个值的特征会被删除(可以根据数据情况调整阈值) selector = VarianceThreshold(threshold=0.01) # 对训练数据进行方差筛选,fit_transform会计算每个特征的方差并剔除不满足阈值的特征 # X_train是原始训练数据,X_train_var是筛选后的训练数据 X_train_var = selector.fit_transform(X_train) # 对测试数据应用同样的筛选规则,transform会直接用训练数据的筛选结果处理测试数据 # X_test是原始测试数据,X_test_var是筛选后的测试数据 X_test_var = selector.transform(X_test) # 获取被保留下来的特征名称 # selector.get_support()返回一个布尔值列表,表示哪些特征被保留,这个是selector这个实例化的类的一个方法 # X_train.columns是特征的名称,结合布尔值列表可以提取保留特征的名字 selected_features_var = X_train.columns[selector.get_support()].tolist() # 打印筛选后保留的特征数量和具体特征名称,方便查看结果 print(f"方差筛选后保留的特征数量: {len(selected_features_var)}") print(f"保留的特征: {selected_features_var}") # 创建一个随机森林分类模型,用于在筛选后的数据上进行训练和预测 # random_state=42是为了保证每次运行结果一致,方便教学和对比 rf_model_var = RandomForestClassifier(random_state=42) # 在筛选后的训练数据上训练模型 # X_train_var是筛选后的特征数据,y_train是对应的目标标签 rf_model_var.fit(X_train_var, y_train) # 使用训练好的模型对筛选后的测试数据进行预测 # X_test_var是筛选后的测试特征数据,rf_pred_var是预测结果 rf_pred_var = rf_model_var.predict(X_test_var) # 记录结束时间,计算整个训练和预测过程的耗时 end_time = time.time() print(f"训练与预测耗时: {end_time - start_time:.4f} 秒") # 打印模型在测试集上的分类报告,展示模型的性能 # 分类报告包括精确率、召回率、F1分数等指标,帮助评估模型好坏 print("\n方差筛选后随机森林在测试集上的分类报告:") print(classification_report(y_test, rf_pred_var)) # 打印混淆矩阵,展示模型预测的详细结果 # 混淆矩阵显示了真实标签和预测标签的对应情况,比如多少样本被正确分类,多少被错分 print("方差筛选后随机森林在测试集上的混淆矩阵:") print(confusion_matrix(y_test, rf_pred_var))运行结果:
3、皮尔逊相关系数筛选
运行代码:
# 先运行之前预处理好的代码 import pandas as pd #用于数据处理和分析,可处理表格数据。 import numpy as np #用于数值计算,提供了高效的数组操作。 import matplotlib.pyplot as plt #用于绘制各种类型的图表 import seaborn as sns #基于matplotlib的高级绘图库,能绘制更美观的统计图形。 import warnings from sklearn.utils import resample from sklearn.model_selection import train_test_split warnings.filterwarnings("ignore") # 设置中文字体(解决中文显示问题) plt.rcParams['font.sans-serif'] = ['SimHei'] # Windows系统常用黑体字体 plt.rcParams['axes.unicode_minus'] = False # 正常显示负号 df = pd.read_csv(r'D:\Python60DaysChallenge-main\heart.csv') X = df.iloc[:, :-1] y = df.iloc[:, -1] # 切分数据 X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42) # 打印标题,表明这是方差筛选的部分 print("--- 方差筛选 (Variance Threshold) ---") # 导入需要的工具库 from sklearn.feature_selection import VarianceThreshold # 方差筛选工具,用于剔除方差小的特征 import time # 用于记录代码运行时间,方便比较效率 from sklearn.ensemble import RandomForestClassifier #随机森林分类器 from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score # 用于评估分类器性能的指标 from sklearn.metrics import classification_report, confusion_matrix #用于生成分类报告和混淆矩阵 import warnings #用于忽略警告信息 print("--- 皮尔逊相关系数筛选 ---") from sklearn.feature_selection import SelectKBest, f_classif import time start_time = time.time() # 计算特征与目标变量的相关性,选择前k个特征(这里设为10个,可调整) # 注意:皮尔逊相关系数通常用于回归问题(连续型目标变量),但如果目标是分类问题,可以用f_classif k = 10 selector = SelectKBest(score_func=f_classif, k=k) X_train_corr = selector.fit_transform(X_train, y_train) X_test_corr = selector.transform(X_test) # 获取筛选后的特征名 selected_features_corr = X_train.columns[selector.get_support()].tolist() print(f"皮尔逊相关系数筛选后保留的特征数量: {len(selected_features_corr)}") print(f"保留的特征: {selected_features_corr}") # 训练随机森林模型 rf_model_corr = RandomForestClassifier(random_state=42) rf_model_corr.fit(X_train_corr, y_train) rf_pred_corr = rf_model_corr.predict(X_test_corr) end_time = time.time() print(f"训练与预测耗时: {end_time - start_time:.4f} 秒") print("\n皮尔逊相关系数筛选后随机森林在测试集上的分类报告:") print(classification_report(y_test, rf_pred_corr)) print("皮尔逊相关系数筛选后随机森林在测试集上的混淆矩阵:") print(confusion_matrix(y_test, rf_pred_corr))运行结果:
4、lasso筛选(基于L1正则化)
运行代码:
# 先运行之前预处理好的代码 import pandas as pd #用于数据处理和分析,可处理表格数据。 import numpy as np #用于数值计算,提供了高效的数组操作。 import matplotlib.pyplot as plt #用于绘制各种类型的图表 import seaborn as sns #基于matplotlib的高级绘图库,能绘制更美观的统计图形。 import warnings from sklearn.utils import resample from sklearn.model_selection import train_test_split warnings.filterwarnings("ignore") # 设置中文字体(解决中文显示问题) plt.rcParams['font.sans-serif'] = ['SimHei'] # Windows系统常用黑体字体 plt.rcParams['axes.unicode_minus'] = False # 正常显示负号 df = pd.read_csv(r'D:\Python60DaysChallenge-main\heart.csv') X = df.iloc[:, :-1] y = df.iloc[:, -1] # 切分数据 X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42) # 打印标题,表明这是方差筛选的部分 print("--- 方差筛选 (Variance Threshold) ---") # 导入需要的工具库 from sklearn.feature_selection import VarianceThreshold # 方差筛选工具,用于剔除方差小的特征 import time # 用于记录代码运行时间,方便比较效率 from sklearn.ensemble import RandomForestClassifier #随机森林分类器 from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score # 用于评估分类器性能的指标 from sklearn.metrics import classification_report, confusion_matrix #用于生成分类报告和混淆矩阵 import warnings #用于忽略警告信息 print("--- Lasso筛选 (L1正则化) ---") from sklearn.linear_model import Lasso from sklearn.feature_selection import SelectFromModel import time start_time = time.time() # 使用Lasso回归进行特征筛选 lasso = Lasso(alpha=0.01, random_state=42) # alpha值可调整 selector = SelectFromModel(lasso) selector.fit(X_train, y_train) X_train_lasso = selector.transform(X_train) X_test_lasso = selector.transform(X_test) # 获取筛选后的特征名 selected_features_lasso = X_train.columns[selector.get_support()].tolist() print(f"Lasso筛选后保留的特征数量: {len(selected_features_lasso)}") print(f"保留的特征: {selected_features_lasso}") # 训练随机森林模型 rf_model_lasso = RandomForestClassifier(random_state=42) rf_model_lasso.fit(X_train_lasso, y_train) rf_pred_lasso = rf_model_lasso.predict(X_test_lasso) end_time = time.time() print(f"训练与预测耗时: {end_time - start_time:.4f} 秒") print("\nLasso筛选后随机森林在测试集上的分类报告:") print(classification_report(y_test, rf_pred_lasso)) print("Lasso筛选后随机森林在测试集上的混淆矩阵:") print(confusion_matrix(y_test, rf_pred_lasso))运行结果:
5、树模型
运行代码:
# 先运行之前预处理好的代码 import pandas as pd #用于数据处理和分析,可处理表格数据。 import numpy as np #用于数值计算,提供了高效的数组操作。 import matplotlib.pyplot as plt #用于绘制各种类型的图表 import seaborn as sns #基于matplotlib的高级绘图库,能绘制更美观的统计图形。 import warnings from sklearn.utils import resample from sklearn.model_selection import train_test_split warnings.filterwarnings("ignore") # 设置中文字体(解决中文显示问题) plt.rcParams['font.sans-serif'] = ['SimHei'] # Windows系统常用黑体字体 plt.rcParams['axes.unicode_minus'] = False # 正常显示负号 df = pd.read_csv(r'D:\Python60DaysChallenge-main\heart.csv') X = df.iloc[:, :-1] y = df.iloc[:, -1] # 切分数据 X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42) # 打印标题,表明这是方差筛选的部分 print("--- 方差筛选 (Variance Threshold) ---") # 导入需要的工具库 from sklearn.feature_selection import VarianceThreshold # 方差筛选工具,用于剔除方差小的特征 import time # 用于记录代码运行时间,方便比较效率 from sklearn.ensemble import RandomForestClassifier #随机森林分类器 from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score # 用于评估分类器性能的指标 from sklearn.metrics import classification_report, confusion_matrix #用于生成分类报告和混淆矩阵 import warnings #用于忽略警告信息 print("--- 树模型自带的重要性筛选 ---") from sklearn.feature_selection import SelectFromModel import time start_time = time.time() # 使用随机森林的特征重要性进行筛选 rf_selector = RandomForestClassifier(random_state=42) rf_selector.fit(X_train, y_train) selector = SelectFromModel(rf_selector, threshold="mean") # 阈值设为平均重要性,可调整 X_train_rf = selector.transform(X_train) X_test_rf = selector.transform(X_test) # 获取筛选后的特征名 selected_features_rf = X_train.columns[selector.get_support()].tolist() print(f"树模型重要性筛选后保留的特征数量: {len(selected_features_rf)}") print(f"保留的特征: {selected_features_rf}") # 训练随机森林模型 rf_model_rf = RandomForestClassifier(random_state=42) rf_model_rf.fit(X_train_rf, y_train) rf_pred_rf = rf_model_rf.predict(X_test_rf) end_time = time.time() print(f"训练与预测耗时: {end_time - start_time:.4f} 秒") print("\n树模型重要性筛选后随机森林在测试集上的分类报告:") print(classification_report(y_test, rf_pred_rf)) print("树模型重要性筛选后随机森林在测试集上的混淆矩阵:") print(confusion_matrix(y_test, rf_pred_rf))运行结果:
6、SHAP重要性筛选
运行代码:
# 先运行之前预处理好的代码 import pandas as pd #用于数据处理和分析,可处理表格数据。 import numpy as np #用于数值计算,提供了高效的数组操作。 import matplotlib.pyplot as plt #用于绘制各种类型的图表 import seaborn as sns #基于matplotlib的高级绘图库,能绘制更美观的统计图形。 import warnings from sklearn.utils import resample from sklearn.model_selection import train_test_split warnings.filterwarnings("ignore") # 设置中文字体(解决中文显示问题) plt.rcParams['font.sans-serif'] = ['SimHei'] # Windows系统常用黑体字体 plt.rcParams['axes.unicode_minus'] = False # 正常显示负号 df = pd.read_csv(r'D:\Python60DaysChallenge-main\heart.csv') X = df.iloc[:, :-1] y = df.iloc[:, -1] # 切分数据 X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42) # 打印标题,表明这是方差筛选的部分 print("--- 方差筛选 (Variance Threshold) ---") # 导入需要的工具库 from sklearn.feature_selection import VarianceThreshold # 方差筛选工具,用于剔除方差小的特征 import time # 用于记录代码运行时间,方便比较效率 from sklearn.ensemble import RandomForestClassifier #随机森林分类器 from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score # 用于评估分类器性能的指标 from sklearn.metrics import classification_report, confusion_matrix #用于生成分类报告和混淆矩阵 import warnings #用于忽略警告信息 print("--- SHAP重要性筛选 ---") import shap from sklearn.feature_selection import SelectKBest import time start_time = time.time() # 使用随机森林模型计算SHAP值 rf_shap = RandomForestClassifier(random_state=42) rf_shap.fit(X_train, y_train) explainer = shap.TreeExplainer(rf_shap) shap_values = explainer.shap_values(X_train) # 计算每个特征的平均SHAP值(取绝对值的平均) mean_shap = np.abs(shap_values[1]).mean(axis=0) # shap_values[1]对应正类 k = 10 # 选择前10个特征,可调整 top_k_indices = np.argsort(mean_shap)[-k:] X_train_shap = X_train.iloc[:, top_k_indices] X_test_shap = X_test.iloc[:, top_k_indices] # 获取筛选后的特征名 selected_features_shap = X_train.columns[top_k_indices].tolist() print(f"SHAP重要性筛选后保留的特征数量: {len(selected_features_shap)}") print(f"保留的特征: {selected_features_shap}") # 训练随机森林模型 rf_model_shap = RandomForestClassifier(random_state=42) rf_model_shap.fit(X_train_shap, y_train) rf_pred_shap = rf_model_shap.predict(X_test_shap) end_time = time.time() print(f"训练与预测耗时: {end_time - start_time:.4f} 秒") print("\nSHAP重要性筛选后随机森林在测试集上的分类报告:") print(classification_report(y_test, rf_pred_shap)) print("SHAP重要性筛选后随机森林在测试集上的混淆矩阵:") print(confusion_matrix(y_test, rf_pred_shap))运行结果:
7、递归特征消除RFE
运行代码:
# 先运行之前预处理好的代码 import pandas as pd #用于数据处理和分析,可处理表格数据。 import numpy as np #用于数值计算,提供了高效的数组操作。 import matplotlib.pyplot as plt #用于绘制各种类型的图表 import seaborn as sns #基于matplotlib的高级绘图库,能绘制更美观的统计图形。 import warnings from sklearn.utils import resample from sklearn.model_selection import train_test_split warnings.filterwarnings("ignore") # 设置中文字体(解决中文显示问题) plt.rcParams['font.sans-serif'] = ['SimHei'] # Windows系统常用黑体字体 plt.rcParams['axes.unicode_minus'] = False # 正常显示负号 df = pd.read_csv(r'D:\Python60DaysChallenge-main\heart.csv') X = df.iloc[:, :-1] y = df.iloc[:, -1] # 切分数据 X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42) # 打印标题,表明这是方差筛选的部分 print("--- 方差筛选 (Variance Threshold) ---") # 导入需要的工具库 from sklearn.feature_selection import VarianceThreshold # 方差筛选工具,用于剔除方差小的特征 import time # 用于记录代码运行时间,方便比较效率 from sklearn.ensemble import RandomForestClassifier #随机森林分类器 from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score # 用于评估分类器性能的指标 from sklearn.metrics import classification_report, confusion_matrix #用于生成分类报告和混淆矩阵 import warnings #用于忽略警告信息 print("--- 递归特征消除 (RFE) ---") from sklearn.feature_selection import RFE import time start_time = time.time() # 使用随机森林作为基础模型进行RFE base_model = RandomForestClassifier(random_state=42) rfe = RFE(base_model, n_features_to_select=10) # 选择10个特征,可调整 rfe.fit(X_train, y_train) X_train_rfe = rfe.transform(X_train) X_test_rfe = rfe.transform(X_test) # 获取筛选后的特征名 selected_features_rfe = X_train.columns[rfe.support_].tolist() print(f"RFE筛选后保留的特征数量: {len(selected_features_rfe)}") print(f"保留的特征: {selected_features_rfe}") # 训练随机森林模型 rf_model_rfe = RandomForestClassifier(random_state=42) rf_model_rfe.fit(X_train_rfe, y_train) rf_pred_rfe = rf_model_rfe.predict(X_test_rfe) end_time = time.time() print(f"训练与预测耗时: {end_time - start_time:.4f} 秒") print("\nRFE筛选后随机森林在测试集上的分类报告:") print(classification_report(y_test, rf_pred_rfe)) print("RFE筛选后随机森林在测试集上的混淆矩阵:") print(confusion_matrix(y_test, rf_pred_rfe))运行结果:
分析与讨论:
1.方差筛选、皮尔逊、Lasso筛选
结果:与基准模型完全一致
分析:
这表明原始特征中几乎没有以下情况:
方差极低的常数特征
与目标无关的线性不相关特征
完全冗余的线性组合特征
结论:这三种简单的过滤/嵌入法对此问题效果有限
2、树模型:
模型在两个类别的平衡性上发生了变化
对类别1的召回率提升(88%),但精确率略降
说明树模型筛选出了一些能更好区分两个类别边界的特征
这可能改善了类别分布不均的问题
3.SHAP筛选 ⚠️
结果:准确率大幅下降(0.84→0.64)
原因分析:
特征丢失问题:SHAP可能筛选掉了一些协同作用的重要特征
阈值设置不当:可能保留了太少的特征
计算稳定性:SHAP值计算在小样本上不够稳定
适用性问题:SHAP更适合特征解释,而非特征筛选
教训:SHAP作为筛选工具要谨慎,需仔细调参
4.RFE(递归特征消除) ✅
结果:准确率最佳(0.85),性能最均衡
成功原因:
RFE逐步剔除最不重要特征,找到最优特征子集
考虑特征间的交互作用,而非孤立评估
在保持类别0性能的同时,显著提升了类别1的召回率
5、总结
性能提升:RFE > 树模型重要性 > 其他方法(持平) > SHAP(下降)
计算成本:SHAP > RFE > 树模型 > Lasso > 皮尔逊 ≈ 方差筛选
@浙大疏锦行