在昨天的学习中,了解了常见的特征筛选方法中的四种:方差筛选、皮尔逊系数筛选、Lasso筛选以及树模型自带的重要性筛选。
今日将接着学习剩下的SHAP重要性筛选以及RFE筛选,并对心脏数据集实践特征筛选的过程。
SHAP重要性筛选
基于SHAP值的模型解释性的高级特征选择方法。根据每个特征对模型预测的贡献度大小排序,选择最重要的k个特征。
具体而言,步骤如下:
- 训练模型并创建shap解释器,计算shap值
- 计算SHAP值的平均值,得到特征的全局重要性
- 排序并选择top-k特征,使用特征索引筛选训练集和测试集的对应特征iloc
- 重新训练,对比结果
import shap
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) #耗时长
#筛选特征
#取绝对值是为了排除方向的影响,取平均是为了得到每个特征的全局重要性,axis=0按行
mean_shap = np.abs(shap_values[:,:,1]).mean(axis=0) #shap_values[:,:,1])获取正类的特征与样本
#排序并选择重要特征,np.argsort()返回数组排序后的索引数组
k = 10
top_k_indices = np.argsort(mean_shap)[-k:] #获取前k个重要特征的索引,重要性逐渐降低
X_train_shap = X_train.iloc[:,top_k_indices] #训练集的特征筛选,iloc方法切片
X_test_shap = X_test.iloc[:,top_k_indices] #测试集的特征筛选
#查看特征
selected_features_shap = X_train.columns[top_k_indices].tolist()
print('保留特征:{}, \n数量:{}'.format(selected_features_shap,len(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("默认随机森林 在测试集上的混淆矩阵:")
print(confusion_matrix(y_test, rf_pred_shap))
通过SHAP筛选前10个特征,进行训练得到如下结果:

递归特征消除RFE
递归特征消除法(Recursive Feature Elimination,RFE),属于包裹法,通过递归地构建模型并剔除最不重要的特征,最终保留最优的特征子集。它是一个反复迭代的过程,每次迭代都会训练一个模型,根据特征重要性排序,淘汰掉最不重要的特征,直到达到指定的特征数量。
递归特征消除法筛选特征可以通过sklearn中的RFE(estimator,n_features_to_select,...)实现:
from sklearn.feature_selection import RFE
start_time = time.time()
#创建模型并训练
rf_rfe = RandomForestClassifier(random_state=42)
k = 10
selector = RFE(estimator=rf_rfe,n_features_to_select=k)
selector.fit(X_train,y_train)
X_train_rfe = selector.transform(X_train)
X_test_rfe = selector.transform(X_test)
#查看特征
selected_features_rfe = X_train.columns[selector.get_support()].tolist()
print('保留特征:{}, \n数量:{}'.format(selected_features_rfe,len(selected_features_rfe)))
#重新训练模型
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("\nRFE 随机森林 在测试集上的分类报告:")
print(classification_report(y_test, rf_pred))
print("RFE 随机森林 在测试集上的混淆矩阵:")
print(confusion_matrix(y_test, rf_pred))
通过RFE筛选前10个特征,进行训练得到如下结果:

综上,通过这六种特征筛选方法后,可以发现,将特征数降低之后,模型的效果并没有大幅度下降。
下面是六种方法的对比,实际使用中可以采用组合的方法。此外还可以从下几方面考虑:
- 数据量:小用RFE,大用Lasso或方差
- 关系类型:线性用皮尔逊或Lasso,非线性用树模型或SHAP
- 解释需求:高用SHAP,低用树模型或RFE
- 计算资源:充足用SHAP或RFE,紧张用方差或皮尔逊
| 方法 | 原理 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| 方差筛选 | 移除低方差特征 | 速度快、简单 | 忽略与目标关系 | 数据预处理 |
| 皮尔逊系数 | 线性相关性 | 计算快、易解释 | 仅线性关系 | 线性问题初步筛选 |
| Lasso筛选 | L1正则化 | 自动选择、处理高维 | 线性假设 | 高维数据、线性模型 |
| 树模型重要性 | 基于树模型 | 捕捉非线性、高效 | 可能偏向高基数 | 树模型、非线性数据 |
| SHAP筛选 | 特征贡献度 | 解释性最强、统一度量 | 计算成本高 | 可解释性要求高 |
| RFE筛选 | 递归消除 | 效果优、找到最优子集 | 计算慢、不稳定 | 中等数据集、追求性能 |
作业:心脏病数据集特征筛选
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
# 设置中文字体(解决中文显示问题)
plt.rcParams['font.sans-serif'] = ['SimHei'] # Windows系统常用黑体字体
plt.rcParams['axes.unicode_minus'] = False # 正常显示负号
#读取数据
data = pd.read_csv(r'heart.csv')
data.head() #读取前5行
#对于多分类特征进行独热编码
continous_features = ['age','trestbps','chol','thalach','oldpeak']
discrete_features = ['sex','cp','fbs','restecg','exang','slope','thal']
multi_features = ['cp','restecg','slope','thal']
data = pd.get_dummies(data=data,columns=multi_features,prefix=multi_features) #返回的是新的dataframe+编码列
data.head()
#标签和特征
X = data.drop(['target'],axis=1)
y = data['target']
from sklearn.model_selection import train_test_split
X_train,X_test,y_train,y_test = train_test_split(X,y,train_size=0.7,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') # 忽略所有警告信息
import 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))
默认随机森林:

由于筛选方法与信贷数据集没有什么区别,流程和代码基本相同,下面只展示结果:
(1)方差筛选,选取了22个特征,结果与默认差别不大

(2)皮尔逊系数,选取了10个特征,结果与默认有差距

(3)Lasso筛选,选取了15个特征,结果与默认有差距

(4)树模型自带,选取了10个特征,结果与默认有差距

(5)shap筛选,选取了10个特征,结果与默认有差距

(6)RFE,选取了10个特征,结果与默认相同

对比特征筛选前后的结果,发现方差筛选和RFE筛选,两种方法的结果与默认的几乎无差别。
2037

被折叠的 条评论
为什么被折叠?



