sklearn 特征选择实战:用 RFE 找到最优特征组合

简介: 特征越多模型未必越好,过多特征易导致过拟合、训练慢、难解释。递归特征消除(RFE)通过反复训练与特征评分,逐步剔除不重要特征,提升模型泛化能力与效率。本文详解RFE原理,并用scikit-learn实战葡萄酒数据集,展示如何结合逻辑回归与随机森林进行特征选择,比较不同模型的筛选差异,并通过RFECV自动确定最优特征数量,辅以可视化分析,帮助构建更简洁、高效、可解释的模型。

特征越多模型效果就越好?这个想法在实践中往往站不住脚,因为过多的特征反而会带来过拟合、训练时间过长、模型难以解释等一堆麻烦。递归特征消除(RFE)就是用来解决这类问题的,算是特征选择里面比较靠谱的方法之一。

本文会详细介绍RFE 的工作原理,然后用 scikit-learn 跑一个完整的例子。

RFE 是什么

递归特征消除本质上是个反向筛选过程。它会先用全部特征训练模型,然后根据模型给出的重要性评分把最不重要的特征踢掉,接着用剩下的特征重新训练,如此反复直到达到设定的特征数量。

这可以理解成雕刻的过程,一点点削掉不重要的部分,最后留下对预测真正有用的核心特征。

RFE 比单变量特征选择高明的地方在于,它考虑了特征之间的交互关系。每次删掉特征后都会重新训练,这样能捕捉到特征组合的效果。

RFE 适用范围比较广,只要模型能给出特征重要性就能用。它会自己考虑特征之间怎么配合,这点很关键。删掉无关特征后模型泛化能力会更好,不容易过拟合。特征少了模型自然更好理解,训练和预测的速度也快。

实际操作

我们这次用 sklearn 自带的葡萄酒数据集。这个数据集有 13 个化学特征,任务是预测葡萄酒的类别。先把需要的库导入:

 # importing necessary libraries  
import numpy as np  
import pandas as pd  
from sklearn.datasets import load_wine  
from sklearn.model_selection import train_test_split, cross_val_score  
from sklearn.preprocessing import StandardScaler  
from sklearn.linear_model import LogisticRegression  
from sklearn.ensemble import RandomForestClassifier  
from sklearn.feature_selection import RFE, RFECV  
from sklearn.metrics import accuracy_score, classification_report  
import matplotlib.pyplot as plt  
import seaborn as sns  

# loading the wine dataset  
wine = load_wine()  
X = pd.DataFrame(wine.data, columns=wine.feature_names)  
y = wine.target  
print(f"Dataset shape: {X.shape}")  
print(f"Number of classes: {len(np.unique(y))}")  
print(f"\nFeature names:")  
print('*'*15)  
for col in X.columns:  
     print(col)

看下输出:

数据集有 178 个样本,13 个特征,3 个类别。接下来我们做数据准备和基线测试:

 # train-test splitting  
X_train, X_test, y_train, y_test = train_test_split(  
    X, y, test_size=0.3, random_state=42, stratify=y  
)  

# scaling the features  
scaler = StandardScaler()  
X_train_scaled = scaler.fit_transform(X_train)  
X_test_scaled = scaler.transform(X_test)  

# baseline model including all features  
baseline_model = LogisticRegression(max_iter=1000, random_state=42)  
baseline_model.fit(X_train_scaled, y_train)  
baseline_score = baseline_model.score(X_test_scaled, y_test)  
 print(f"Baseline accuracy with all {X.shape[1]} features: {baseline_score:.3f}")

用全部特征训练的逻辑回归准确率是 0.981,这个作为基准线。

固定特征数量的 RFE

现在用 RFE 筛选 5 个最重要的特征:

 # initializing the estimator  
estimator = LogisticRegression(max_iter=1000, random_state=42)  

# creating and fitting the RFE object to select best 5 features  
rfe = RFE(estimator, n_features_to_select=5, step=1)  
rfe.fit(X_train_scaled, y_train)  

# view the selected features  
selected_features = X.columns[rfe.support_].tolist()  
print(f"\nSelected features: {selected_features}")  

# we can also get the feature rankings (where 1 is best)  
feature_ranking = pd.DataFrame({  
    'feature': X.columns,  
    'ranking': rfe.ranking_  
}).sort_values('ranking')  

print("\nFeature Rankings:")  
display(feature_ranking)  

# model with selected features  
X_train_rfe = rfe.transform(X_train_scaled)  
X_test_rfe = rfe.transform(X_test_scaled)  
rfe_model = LogisticRegression(max_iter=1000, random_state=69)  
rfe_model.fit(X_train_rfe, y_train)  
rfe_score = rfe_model.score(X_test_rfe, y_test)  
print(f"\nRFE accuracy with {rfe.n_features_} features: {rfe_score:.4f}")  
 print(f"Accuracy difference: {rfe_score - baseline_score:.3f}")

结果如下:

选出了 5 个特征后准确率达到 0.9815,和用全部特征几乎没差别。也就是说特征数量直接砍掉了 60% 多,但模型性能基本没损失。

不同模型的表现

RFE 可以配合各种模型使用。比如说随机森林,看看选出来的特征有什么区别:

 # RFE with Random Forest  
rf_estimator = RandomForestClassifier(n_estimators=200, random_state=69)  
rfe_rf = RFE(rf_estimator, n_features_to_select=5, step=1)  
rfe_rf.fit(X_train_scaled, y_train)  

# comparing Logistic Regression RFE and Random Forest RFE  
lr_features = set(X.columns[rfe.support_])  
rf_features = set(X.columns[rfe_rf.support_])  
print("Features selected by Logistic Regression:")  
print(lr_features)  
print("\nFeatures selected by Random Forest:")  
print(rf_features)  
print("\nCommon features:")  
print(lr_features.intersection(rf_features))  
print("\nDifferent features:")  
 print(lr_features.symmetric_difference(rf_features))


两个模型选出来的特征有重叠也有差异。这很正常,因为不同模型看重的东西本来就不一样。逻辑回归更关注线性关系,而随机森林能捕捉更复杂的非线性模式。

特征排名可视化

再看看两个模型对特征的评价:

 # creating a dataframe for easy plotting  
comparison_df = pd.DataFrame({  
    'Feature': X.columns,  
    'LR_Ranking': rfe.ranking_,  
    'RF_Ranking': rfe_rf.ranking_,  
    'Selected_by_LR': rfe.support_,  
    'Selected_by_RF': rfe_rf.support_  
})  

# plot 1: feature rankings comparison  
comparison_melted=comparison_df.melt(id_vars='Feature', value_vars=['LR_Ranking', 'RF_Ranking'],  
                                     var_name='Model', value_name='Ranking')  
sns.barplot(data=comparison_melted,x='Feature', y='Ranking', hue='Model', palette=['darkblue','yellow'])  
plt.grid(True, axis='y')  
plt.xticks(rotation=90)  
plt.yticks(np.arange(1, comparison_melted['Ranking'].max()+1))  
plt.xlabel('Features')  
plt.ylabel('Ranking (lower is better)')  
plt.title('Feature Rankings by Model')  
plt.show()  

# Plot 2: selected features visualization using heatmap  
selected_data = comparison_df[['Feature', 'Selected_by_LR', 'Selected_by_RF']].set_index('Feature').T  
sns.heatmap(selected_data, annot=False, cmap='RdYlGn', cbar=False)  
plt.title('Selected Features Matrix', fontsize=12, fontweight='bold')  
plt.xlabel('Features', fontsize=11)  
plt.ylabel('Model', fontsize=11)  
 plt.show()


柱状图显示了各个特征在不同模型中的排名,热力图直观地展示了哪些特征被选中。可以看到 proline、flavanoids 等几个特征在两个模型中都比较重要。

RFECV 自动找最优特征数

前面都是手动指定要保留 5 个特征。实际应用中很难事先知道该留多少个。所以RFECV 用交叉验证自动确定最优数量:

 # create and fit RFECV object  
rfecv=RFECV(  
    estimator=LogisticRegression(max_iter=1000, random_state=42),  
    step=1,  
    cv=5,  # 5-fold cross-validation  
    scoring='accuracy',  
    min_features_to_select=1  
)  

rfecv.fit(X_train_scaled, y_train)  
print(f"Optimal number of features: {rfecv.n_features_} with a score of {rfecv.cv_results_['mean_test_score'].max():.4f}")  
print('The selected features are:')  
forcolinX.columns[rfecv.support_]:  
    print(f" -> {col}")  

    # plotting number of selected features vs. cross-validation scores  
plt.figure(figsize=(8, 5))  
y1=rfecv.cv_results_['mean_test_score']  
x1=rfecv.cv_results_['n_features']  

ax=sns.lineplot(x=x1, y=y1)  
plt.xlabel("Number of Features Selected")  
plt.ylabel("Mean CV Accuracy")  
plt.title("RFECV: Number of Features vs. CV Accuracy")  
ax.axhline(y=np.max(y1), color='r', linestyle=':', label='Max CV Accuracy')  
plt.xticks(x1)  
plt.yticks(np.arange(0.75,1.01,0.02))  
plt.grid(True)  
plt.legend()  
plt.show()  

# creating model with optimal features  
X_train_rfecv=rfecv.transform(X_train_scaled)  
X_test_rfecv=rfecv.transform(X_test_scaled)  
rfecv_model=LogisticRegression(max_iter=1000, random_state=42)  
rfecv_model.fit(X_train_rfecv, y_train)  
rfecv_score=rfecv_model.score(X_test_rfecv, y_test)  
 print(f"\nModel accuracy with {rfecv.n_features_} features: {rfecv_score:.4f}")

输出:

交叉验证结果显示 10 个特征时准确率最高,达到 0.9839。曲线图能清楚看到随着特征数量增加,准确率的变化趋势。通过图标可视化,比拍脑袋决定保留几个特征靠谱多了。

注意事项

特征标准化很重要,尤其是用距离相关的算法时。选择合适的基模型也关键,得确保它能给出有意义的特征重要性分数。数据量大的时候 RFE 跑起来会比较慢,所以可以调大 step 参数一次删多个特征。

交叉验证能避免特征选择过程中的过拟合,RFECV 的一个主要功能就是解决这个问题。最终模型的评估一定要在独立的测试集上做。有时候领域知识比算法选择更管用,别完全依赖自动化方法。

也可以试试不同的基模型也有价值,线性模型和树模型关注的点不同,选出的特征也会有差别。

总结

RFE 在特征选择这块确实好用。需要降维但又不想损失太多性能的时候,或者想搞清楚哪些特征最重要的时候,都可以考虑用它。

RFE 根据模型评分系统地删除特征;RFECV 能自动找到最优特征数量;不同模型可能选出不同特征;测试集验证不能少。

下次碰到高维数据的时候可以试试 RFE,说不定会有惊喜。

https://avoid.overfit.cn/post/2ef37f6acc184f2dbf8ae46fba3377bf

作者:Prathik C

目录
相关文章
|
机器学习/深度学习 Python
【机器学习】包裹式特征选择之递归特征消除法
【机器学习】包裹式特征选择之递归特征消除法
2505 4
|
Serverless 数据处理 索引
Pandas中的shift函数:轻松实现数据的前后移动
Pandas中的shift函数:轻松实现数据的前后移动
2485 0
|
机器学习/深度学习 数据采集 监控
机器学习-特征选择:如何使用递归特征消除算法自动筛选出最优特征?
机器学习-特征选择:如何使用递归特征消除算法自动筛选出最优特征?
2282 0
|
存储 SQL 缓存
Hadoop入门(一篇就够了)
Hadoop入门(一篇就够了)
36974 4
Hadoop入门(一篇就够了)
|
5月前
|
人工智能 自然语言处理 安全
氛围编程陷阱:为什么AI生成代码正在制造大量"伪开发者"
AI兴起催生“氛围编程”——用自然语言生成代码,看似高效实则陷阱。它让人跳过编程基本功,沦为只会提示、不懂原理的“中间商”。真实案例显示,此类项目易崩溃、难维护,安全漏洞频出。AI是技能倍增器,非替代品;真正强大的开发者,永远是那些基础扎实、能独立解决问题的人。
572 11
氛围编程陷阱:为什么AI生成代码正在制造大量"伪开发者"
|
5月前
|
数据采集 监控 API
告别手动埋点!Android 无侵入式数据采集方案深度解析
传统的Android应用监控方案需要开发者在代码中手动添加埋点,不仅侵入性强、工作量大,还难以维护。本文深入探讨了基于字节码插桩技术的无侵入式数据采集方案,通过Gradle插件 + AGP API + ASM的技术组合,实现对应用性能、用户行为、网络请求等全方位监控,真正做到零侵入、易集成、高稳定。
714 67
|
4月前
|
机器学习/深度学习 数据挖掘 BI
Pandas GroupBy 的 10 个实用技巧
本文介绍Pandas中groupby的10个实用技巧,突破传统聚合认知。涵盖多函数聚合、结果命名、transform特征构造、组内累积计算、自定义逻辑、唯一值统计、分类分组、多级索引、扁平化输出及透视表结合应用,助你高效处理复杂数据场景,提升数据分析效率。(238字)
347 4
Pandas GroupBy 的 10 个实用技巧
|
5月前
|
存储 安全 Java
泛型在Java集合框架中的类型擦除机制是如何工作的?
泛型在Java集合框架中的类型擦除机制是如何工作的?
213 2
|
5月前
|
存储 消息中间件 Kafka
Confluent 首席架构师万字剖析 Apache Fluss(三):湖流一体
原文:https://jack-vanlightly.com/blog/2025/9/2/understanding-apache-fluss 作者:Jack Vanlightly 翻译:Wayne Wang@腾讯 译注:Jack Vanlightly 是一位专注于数据系统底层架构的知名技术博主,他的文章以篇幅长、细节丰富而闻名。目前 Jack 就职于 Confluent,担任首席技术架构师,因此这篇 Fluss 深度分析文章,具备一定的客观参考意义。译文拆成了三篇文章,本文是第二篇。
731 25
Confluent 首席架构师万字剖析 Apache Fluss(三):湖流一体
|
5月前
|
存储 消息中间件 Kafka
Confluent 首席架构师万字剖析 Apache Fluss(二):核心架构
原文:https://jack-vanlightly.com/blog/2025/9/2/understanding-apache-fluss 作者:Jack Vanlightly 翻译:Wayne Wang@腾讯 译注:Jack Vanlightly 是一位专注于数据系统底层架构的知名技术博主,他的文章以篇幅长、细节丰富而闻名。目前 Jack 就职于 Confluent,担任首席技术架构师,因此这篇 Fluss 深度分析文章,具备一定的客观参考意义。译文拆成了三篇文章,本文是第二篇。
690 19

热门文章

最新文章