Day 19 常见的特征筛选算法

发布于:2025-08-13 ⋅ 阅读:(18) ⋅ 点赞:(0)

@浙大疏锦行

  1. 方差筛选
  2. 皮尔逊相关系数筛选
  3. lasso筛选
  4. 树模型重要性
  5. shap重要性
  6. 递归特征消除REF

面对高维特征,为了减少算力浪费和冗余特征的干扰,通常都会进行特征降维处理。如基因数据、微生物数据、传感器数据等,特征较多

特征降维一般有2种策略:

1. 特征筛选:从n个特征中筛选出m个特征,比如方差筛选,剔除方差过小的特征;利用皮尔逊相关系数筛选;lasso筛选(lasso自带的系数可以理解为重要性)、利用树模型自带的重要性、shap重要性等筛选;特征递归方法

2. 特征组合:从n个特征中组合出m个特征,如pca等

今天主要学习特征筛选:

 方差筛选(简单有效)

它的核心逻辑是:特征的方差反映了数据的变化程度,方差很小的特征几乎没有变化,对模型的预测帮助不大。比如,一个特征的值在所有样本中几乎都一样(方差接近0),那么它对区分不同类别或预测结果几乎没有贡献。因此,方差筛选会设定一个方差阈值,剔除方差低于这个阈值的特征,保留那些变化较大的特征,从而减少特征数量,提高模型效率。

这种方法特别适合处理高维数据,能快速去掉不重要的特征,但它不考虑特征与目标变量之间的关系,可能会误删一些低方差但有意义的特征

# 打印标题,表明这是方差筛选的部分
print("--- 方差筛选 (Variance Threshold) --- ")


# 导入需要的工具库
from sklearn.feature_selection import VarianceThreshold  # 方差筛选工具,用于剔除方差小的特征
import time  # 用于记录代码运行时间,方便比较效率


# 记录开始时间,后面会计算整个过程耗时
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()返回一个布尔值列表,表示哪些特征被保留
# X_train.columns是特征的名称,结合布尔值列表可以提取保留特征的名字
selected_features_var = X_train.columns[selector.get_support()].tolist()


# 打印筛选后保留的特征数量和具体特征名称,方便查看结果
print(f"方差筛选后保留的特征数量: {len(selected_features_var)}")
print(f"保留的特征: {selected_features_var}")


# 创建一个逻辑回归模型,用于在筛选后的数据上进行训练和预测
# 保持与基准模型一致,使用相同的参数设置
lr_model_var = LogisticRegression(random_state=42)


# 在筛选后的训练数据上训练模型
# X_train_var是筛选后的特征数据,y_train是对应的目标标签
lr_model_var.fit(X_train_var, y_train)


# 使用训练好的模型对筛选后的测试数据进行预测
# X_test_var是筛选后的测试特征数据,lr_pred_var是预测结果
lr_pred_var = lr_model_var.predict(X_test_var)


# 记录结束时间,计算整个训练和预测过程的耗时
end_time = time.time()
print(f"训练与预测耗时: {end_time - start_time:.4f} 秒")


# 打印模型在测试集上的分类报告,展示模型的性能
print("\n方差筛选后逻辑回归在测试集上的分类报告:")
print(classification_report(y_test, lr_pred_var))


# 打印混淆矩阵,展示模型预测的详细结果
print("方差筛选后逻辑回归在测试集上的混淆矩阵:")
print(confusion_matrix(y_test, lr_pred_var))
--- 方差筛选 (Variance Threshold) ---
方差筛选后保留的特征数量: 25
保留的特征: ['age', 'trestbps', 'chol', 'thalach', 'oldpeak', 'sex_1', 'cp_1', 'cp_2', 'cp_3', 'fbs_1', 'restecg_1', 'restecg_2', 'exang_1', 'slope_1', 'slope_2', 'ca_1', 'ca_2', 'ca_3', 'ca_4', 'thal_1', 'thal_2', 'thal_3', 'thalach_age_ratio', 'bp_chol_risk', 'st_depression_ratio']
训练与预测耗时: 0.0123 秒

方差筛选后逻辑回归在测试集上的分类报告:
              precision    recall  f1-score   support

           0       0.87      0.93      0.90        29
           1       0.93      0.88      0.90        32

    accuracy                           0.90        61
   macro avg       0.90      0.90      0.90        61
weighted avg       0.90      0.90      0.90        61

方差筛选后逻辑回归在测试集上的混淆矩阵:
[[27  2]
 [ 4 28]]

因为这个数据集特征本来就不是很多,主要来熟悉流程。

皮尔逊相关系数筛选

核心是通过计算 每个特征与目标变量的线性相关程度 来筛选重要特征。

关键要点:
1. 1.
   原理 :皮尔逊相关系数(范围-1到1)衡量两个连续变量的线性相关强度——绝对值越大,相关性越强(接近1正相关,接近-1负相关,0无线性相关)。
2. 2.
   筛选逻辑 :计算所有特征与目标变量的相关系数,保留绝对值超过设定阈值的特征(如|r|>0.3),剔除低相关特征。
3. 3.
   适用场景 :主要用于 目标变量为连续型 的任务;若目标是分类变量(如0/1标签),需先将其编码为数值(如one-hot编码)再计算。
简单说,就是挑出与目标变量“线性关系最密切”的特征,减少冗余,提升模型效率。

# 打印标题,表明这是皮尔逊相关系数筛选的部分
print("--- 皮尔逊相关系数筛选 (Pearson Correlation) --- ")

# 导入需要的工具库
from sklearn.feature_selection import SelectKBest
from sklearn.feature_selection import f_classif  # 用于分类问题的ANOVA F值
import time

# 记录开始时间
start_time = time.time()

# 计算特征与目标变量之间的皮尔逊相关系数
# 注意:SelectKBest默认使用f_classif(ANOVA F值),对于分类问题更合适
# 我们可以通过mutual_info_classif获取互信息,或者使用SelectKBest配合相关系数

# 对于分类问题,使用f_classif(ANOVA F值)来衡量特征重要性
selector_pearson = SelectKBest(score_func=f_classif, k=10)  # 选择前10个最重要的特征

# 拟合数据并转换
X_train_pearson = selector_pearson.fit_transform(X_train, y_train)
X_test_pearson = selector_pearson.transform(X_test)

# 获取被保留下来的特征名称
selected_features_pearson = X_train.columns[selector_pearson.get_support()].tolist()

# 打印筛选后保留的特征数量和具体特征名称
print(f"皮尔逊相关系数筛选后保留的特征数量: {len(selected_features_pearson)}")
print(f"保留的特征: {selected_features_pearson}")

# 创建逻辑回归模型
lr_model_pearson = LogisticRegression(random_state=42)

# 在筛选后的训练数据上训练模型
lr_model_pearson.fit(X_train_pearson, y_train)

# 预测
lr_pred_pearson = lr_model_pearson.predict(X_test_pearson)

# 记录结束时间
end_time = time.time()
print(f"训练与预测耗时: {end_time - start_time:.4f} 秒")

# 打印分类报告
print("\n皮尔逊相关系数筛选后逻辑回归在测试集上的分类报告:")
print(classification_report(y_test, lr_pred_pearson))

# 打印混淆矩阵
print("皮尔逊相关系数筛选后逻辑回归在测试集上的混淆矩阵:")
print(confusion_matrix(y_test, lr_pred_pearson))

--- 皮尔逊相关系数筛选 (Pearson Correlation) ---
皮尔逊相关系数筛选后保留的特征数量: 10
保留的特征: ['thalach', 'oldpeak', 'sex_1', 'cp_2', 'exang_1', 'slope_1', 'slope_2', 'ca_1', 'thal_2', 'thal_3']
训练与预测耗时: 0.0279 秒

皮尔逊相关系数筛选后逻辑回归在测试集上的分类报告:
              precision    recall  f1-score   support

           0       0.84      0.90      0.87        29
           1       0.90      0.84      0.87        32

    accuracy                           0.87        61
   macro avg       0.87      0.87      0.87        61
weighted avg       0.87      0.87      0.87        61

皮尔逊相关系数筛选后逻辑回归在测试集上的混淆矩阵:
[[26  3]
 [ 5 27]]

--- 特征选择方法比较 ---
原始特征数量: 25
方差筛选保留特征数量: 25
皮尔逊相关系数筛选保留特征数量: 10
基准模型准确率: 0.9016
方差筛选模型准确率: 0.9016
皮尔逊相关系数筛选模型准确率: 0.8689

Lasso回归(Least Absolute Shrinkage and Selection Operator)是一种结合特征选择和模型训练的方法。它的核心逻辑是:在进行线性回归的同时,通过引入L1正则化项(即惩罚项),强制将一些不重要特征的回归系数压缩到0,从而实现特征筛选。换句话说,Lasso会自动“挑选”对预测目标有贡献的特征(系数不为0),而剔除无关或冗余的特征(系数为0)。这种方法特别适合处理高维数据,可以减少特征数量,提高模型的解释性和计算效率。

# 打印标题,表明这是Lasso筛选的部分
print("--- Lasso筛选 (L1正则化) --- ")

# 导入需要的工具库
from sklearn.linear_model import LogisticRegressionCV
import time

# 记录开始时间
start_time = time.time()

# 创建Lasso逻辑回归模型(使用L1正则化)
# Cs参数表示正则化强度的候选值数量,cv表示交叉验证折数
lasso_model = LogisticRegressionCV(penalty='l1', solver='liblinear', Cs=10, cv=5, random_state=42)

# 拟合数据
lasso_model.fit(X_train, y_train)

# 获取非零系数的特征(即被选中的特征)
selected_features_lasso = X_train.columns[lasso_model.coef_[0] != 0].tolist()

# 筛选特征
X_train_lasso = X_train[selected_features_lasso]
X_test_lasso = X_test[selected_features_lasso]

# 打印筛选后保留的特征数量和具体特征名称
print(f"Lasso筛选后保留的特征数量: {len(selected_features_lasso)}")
print(f"保留的特征: {selected_features_lasso}")

# 使用筛选后的特征重新训练逻辑回归模型
lr_model_lasso = LogisticRegression(random_state=42)
lr_model_lasso.fit(X_train_lasso, y_train)

# 预测
lr_pred_lasso = lr_model_lasso.predict(X_test_lasso)

# 记录结束时间
end_time = time.time()
print(f"训练与预测耗时: {end_time - start_time:.4f} 秒")

# 打印分类报告
print("\nLasso筛选后逻辑回归在测试集上的分类报告:")
print(classification_report(y_test, lr_pred_lasso))

# 打印混淆矩阵
print("Lasso筛选后逻辑回归在测试集上的混淆矩阵:")
print(confusion_matrix(y_test, lr_pred_lasso))

--- Lasso筛选 (L1正则化) ---
Lasso筛选后保留的特征数量: 22
保留的特征: ['age', 'trestbps', 'chol', 'thalach', 'oldpeak', 'sex_1', 'cp_1', 'cp_2', 'cp_3', 'fbs_1', 'restecg_1', 'exang_1', 'slope_2', 'ca_1', 'ca_2', 'ca_3', 'ca_4', 'thal_2', 'thal_3', 'thalach_age_ratio', 'bp_chol_risk', 'st_depression_ratio']
训练与预测耗时: 0.0945 秒

Lasso筛选后逻辑回归在测试集上的分类报告:
              precision    recall  f1-score   support

           0       0.84      0.93      0.89        29
           1       0.93      0.84      0.89        32

    accuracy                           0.89        61
   macro avg       0.89      0.89      0.89        61
weighted avg       0.89      0.89      0.89        61

Lasso筛选后逻辑回归在测试集上的混淆矩阵:
[[27  2]
 [ 5 27]]

--- 特征选择方法比较 ---
原始特征数量: 25
方差筛选保留特征数量: 25
皮尔逊相关系数筛选保留特征数量: 10
Lasso筛选保留特征数量: 22
基准模型准确率: 0.9016
方差筛选模型准确率: 0.9016
皮尔逊相关系数筛选模型准确率: 0.8689
Lasso筛选模型准确率: 0.8852

要注意,lasso本质上是回归模型,实际上用这个方法来筛选也是用回归模型对分类问题建模结束了,然后打印特征重要度,她是把0和1目标变量视为连续值来进行回归的。效果会差一点,不符合逻辑,但是确实可以计算

树模型重要性筛选
基于决策树、随机森林等树模型,通过计算特征在节点分裂时的贡献度(如信息增益)评估重要性,简单直观、能处理特征交互,但可能高估相关特征中某一个的重要性,适用于快速初步筛选。

SHAP重要性筛选
基于博弈论Shapley值,量化每个特征对预测的边际贡献,同时考虑特征依赖关系,理论严谨、能解释单个预测结果(正负影响方

向),计算成本较高,适用于需深入理解特征影响的场景。

递归特征消除(RFE)
迭代式特征选择方法:从所有特征开始,训练模型→移除最不重要特征→重复,直到目标特征数。考虑特征交互、无需人为设阈值,计算复杂度高(需多次训练模型),适用于系统减少特征维度的场景。

# 打印标题,表明这是树模型重要性筛选的部分
print("--- 树模型重要性筛选 (Random Forest) --- ")

# 导入需要的工具库
from sklearn.ensemble import RandomForestClassifier
import time

# 记录开始时间
start_time = time.time()

# 创建随机森林模型
rf_model = RandomForestClassifier(n_estimators=100, random_state=42)
rf_model.fit(X_train, y_train)

# 获取特征重要性
feature_importance = rf_model.feature_importances_

# 排序并选择前10个重要特征
indices = np.argsort(feature_importance)[::-1][:10]
selected_features_rf = X_train.columns[indices].tolist()

# 筛选特征
X_train_rf = X_train[selected_features_rf]
X_test_rf = X_test[selected_features_rf]

# 打印筛选后保留的特征数量和具体特征名称
print(f"树模型重要性筛选后保留的特征数量: {len(selected_features_rf)}")
print(f"保留的特征: {selected_features_rf}")

# 使用筛选后的特征重新训练逻辑回归模型
lr_model_rf = LogisticRegression(random_state=42)
lr_model_rf.fit(X_train_rf, y_train)

# 预测
lr_pred_rf = lr_model_rf.predict(X_test_rf)

# 记录结束时间
end_time = time.time()
print(f"训练与预测耗时: {end_time - start_time:.4f} 秒")

# 打印分类报告
print("\n树模型重要性筛选后逻辑回归在测试集上的分类报告:")
print(classification_report(y_test, lr_pred_rf))

# 打印混淆矩阵
print("树模型重要性筛选后逻辑回归在测试集上的混淆矩阵:")
print(confusion_matrix(y_test, lr_pred_rf))

# 打印标题,表明这是SHAP重要性筛选的部分
print("--- SHAP重要性筛选 --- ")

# 导入需要的工具库
import shap
import time

# 记录开始时间
start_time = time.time()

# 使用随机森林模型进行SHAP分析
# 创建解释器
explainer = shap.TreeExplainer(rf_model)

# 计算SHAP值
shap_values = explainer.shap_values(X_train)

# 获取特征重要性(使用SHAP值的绝对值均值)
shap_importance = np.abs(shap_values[1]).mean(0)

# 排序并选择前10个重要特征
indices = np.argsort(shap_importance)[::-1][:10]
selected_features_shap = X_train.columns[indices].tolist()

# 筛选特征
X_train_shap = X_train[selected_features_shap]
X_test_shap = X_test[selected_features_shap]

# 打印筛选后保留的特征数量和具体特征名称
print(f"SHAP重要性筛选后保留的特征数量: {len(selected_features_shap)}")
print(f"保留的特征: {selected_features_shap}")

# 使用筛选后的特征重新训练逻辑回归模型
lr_model_shap = LogisticRegression(random_state=42)
lr_model_shap.fit(X_train_shap, y_train)

# 预测
lr_pred_shap = lr_model_shap.predict(X_test_shap)

# 记录结束时间
end_time = time.time()
print(f"训练与预测耗时: {end_time - start_time:.4f} 秒")

# 打印分类报告
print("\nSHAP重要性筛选后逻辑回归在测试集上的分类报告:")
print(classification_report(y_test, lr_pred_shap))

# 打印混淆矩阵
print("SHAP重要性筛选后逻辑回归在测试集上的混淆矩阵:")
print(confusion_matrix(y_test, lr_pred_shap))

# 打印标题,表明这是递归特征消除(RFE)的部分
print("--- 递归特征消除 (RFE) --- ")

# 导入需要的工具库
from sklearn.feature_selection import RFE
import time

# 记录开始时间
start_time = time.time()

# 创建逻辑回归模型作为RFE的基模型
estimator = LogisticRegression(random_state=42)

# 创建RFE对象,选择10个特征
rfe = RFE(estimator=estimator, n_features_to_select=10, step=1)

# 拟合数据
rfe.fit(X_train, y_train)

# 获取被保留下来的特征名称
selected_features_rfe = X_train.columns[rfe.support_].tolist()

# 筛选特征
X_train_rfe = X_train[selected_features_rfe]
X_test_rfe = X_test[selected_features_rfe]

# 打印筛选后保留的特征数量和具体特征名称
print(f"RFE筛选后保留的特征数量: {len(selected_features_rfe)}")
print(f"保留的特征: {selected_features_rfe}")

# 使用筛选后的特征重新训练逻辑回归模型
lr_model_rfe = LogisticRegression(random_state=42)
lr_model_rfe.fit(X_train_rfe, y_train)

# 预测
lr_pred_rfe = lr_model_rfe.predict(X_test_rfe)

# 记录结束时间
end_time = time.time()
print(f"训练与预测耗时: {end_time - start_time:.4f} 秒")

# 打印分类报告
print("\nRFE筛选后逻辑回归在测试集上的分类报告:")
print(classification_report(y_test, lr_pred_rfe))

# 打印混淆矩阵
print("RFE筛选后逻辑回归在测试集上的混淆矩阵:")
print(confusion_matrix(y_test, lr_pred_rfe))
--- 树模型重要性筛选 (Random Forest) ---
树模型重要性筛选后保留的特征数量: 10
保留的特征: ['thal_2', 'thalach', 'exang_1', 'oldpeak', 'age', 'thalach_age_ratio', 'st_depression_ratio', 'chol', 'bp_chol_risk', 'trestbps']
训练与预测耗时: 0.1133 秒

树模型重要性筛选后逻辑回归在测试集上的分类报告:
              precision    recall  f1-score   support

           0       0.81      0.86      0.83        29
           1       0.87      0.81      0.84        32

    accuracy                           0.84        61
   macro avg       0.84      0.84      0.84        61
weighted avg       0.84      0.84      0.84        61

树模型重要性筛选后逻辑回归在测试集上的混淆矩阵:
[[25  4]
 [ 6 26]]
--- SHAP重要性筛选 ---
SHAP重要性筛选后保留的特征数量: 10
保留的特征: ['thal_2', 'exang_1', 'thal_3', 'oldpeak', 'thalach', 'slope_2', 'ca_1', 'age', 'cp_2', 'sex_1']
训练与预测耗时: 0.1685 秒

SHAP重要性筛选后逻辑回归在测试集上的分类报告:
              precision    recall  f1-score   support

           0       0.81      0.90      0.85        29
           1       0.90      0.81      0.85        32

    accuracy                           0.85        61
   macro avg       0.85      0.85      0.85        61
weighted avg       0.86      0.85      0.85        61

SHAP重要性筛选后逻辑回归在测试集上的混淆矩阵:
[[26  3]
 [ 6 26]]
--- 递归特征消除 (RFE) ---
RFE筛选后保留的特征数量: 10
保留的特征: ['oldpeak', 'sex_1', 'cp_2', 'cp_3', 'exang_1', 'slope_1', 'ca_1', 'ca_2', 'ca_3', 'thal_3']
训练与预测耗时: 0.0443 秒

RFE筛选后逻辑回归在测试集上的分类报告:
              precision    recall  f1-score   support

           0       0.86      0.86      0.86        29
           1       0.88      0.88      0.88        32

    accuracy                           0.87        61
   macro avg       0.87      0.87      0.87        61
weighted avg       0.87      0.87      0.87        61

RFE筛选后逻辑回归在测试集上的混淆矩阵:
[[25  4]
 [ 4 28]]

--- 特征选择方法比较 ---
原始特征数量: 25
方差筛选保留特征数量: 25
皮尔逊相关系数筛选保留特征数量: 10
Lasso筛选保留特征数量: 22
树模型重要性筛选保留特征数量: 10
SHAP重要性筛选保留特征数量: 10
RFE筛选保留特征数量: 10
基准模型准确率: 0.9016
方差筛选模型准确率: 0.9016
皮尔逊相关系数筛选模型准确率: 0.8689
Lasso筛选模型准确率: 0.8852
树模型重要性筛选模型准确率: 0.8361
SHAP重要性筛选模型准确率: 0.8525
RFE筛选模型准确率: 0.8689


网站公告

今日签到

点亮在社区的每一天
去签到