文章目录
- 自动特征选择
- 使用单一变量法进行特征选择
- 基于模型的特征选择
- 迭代式特征选择
- 📊 结果解读与对比
自动特征选择
在复杂数据集中,各特征对模型预测结果的重要性往往不同,有些特征贡献显著,有些则影响较弱。本文将介绍如何利用 scikit-learn 实现自动特征选择,以帮助筛选出对建模最有价值的特征。
使用单一变量法进行特征选择
在进行统计分析时,我们通常优先选择置信度最高的样本特征进行分析。这种方法尤其适用于特征之间无明显关联的情形,也常被称为单一变量法。在 scikit-learn 中,提供了多种特征选择方法,其中较为简单的两种是 SelectPercentile 和 SelectKBest。SelectPercentile 可按指定百分比自动选取最重要的特征,而 SelectKBest 则根据评分自动选择排名前 K 个的特征。
书中此时用到了当日A股股票的数据,这里推荐大家使用akshare获取
importakshareasak# 获取沪深京A股实时行情数据stock_zh_a_spot_em_df=ak.stock_zh_a_spot_em()print(stock_zh_a_spot_em_df)stock_zh_a_spot_em_df.to_csv("./A.csv",encoding='GBK')如果在jupyter notebook的ipython中获取接口数据,可能会报错,1.18.3版本的akshare要求python内核版本大于等于3.11
importpandasaspdimportnumpyasnp# 1. 读取数据stock=pd.read_csv('./A.csv',encoding='GBK')print(f"原始数据形状:{stock.shape}")# 2. 检查并处理缺失值print("\n缺失值统计:")print(stock.isnull().sum())# 可考虑针对性删除或填充,而不是直接删除所有含缺失值的行stock_cleaned=stock.dropna()print(f"清洗后数据形状:{stock_cleaned.shape}")# 3. 定义目标变量y=stock_cleaned['涨跌幅'].astype(np.float64)# 确保为浮点型# 4. 定义特征(排除指定列)excluded_columns=['涨跌幅','涨跌额','Unnamed: 0','序号','代码','名称']feature_columns=[colforcolinstock_cleaned.columnsifcolnotinexcluded_columns]X=stock_cleaned[feature_columns].values# 5. 验证结果print(f"\n目标变量 y 形状:{y.shape}")print(f"特征矩阵 X 形状:{X.shape}")print(f"\n特征列名:{feature_columns}")print(f"第一行特征值:\n{X[:1]}")原始数据形状: (5792, 24) 缺失值统计: Unnamed: 0 0 序号 0 代码 0 名称 0 最新价 338 涨跌幅 338 涨跌额 338 成交量 339 成交额 339 振幅 338 最高 339 最低 339 今开 339 昨收 2 量比 339 换手率 0 市盈率-动态 321 市净率 321 总市值 321 流通市值 319 涨速 338 5分钟涨跌 338 60日涨跌幅 3 年初至今涨跌幅 3 dtype: int64 清洗后数据形状: (5453, 24) 目标变量 y 形状: (5453,) 特征矩阵 X 形状: (5453, 18) 特征列名: ['最新价', '成交量', '成交额', '振幅', '最高', '最低', '今开', '昨收', '量比', '换手率', '市盈率-动态', '市净率', '总市值', '流通市值', '涨速', '5分钟涨跌', '60日涨跌幅', '年初至今涨跌幅'] 第一行特征值: [[ 2.22000000e+00 2.77997000e+05 5.44917634e+07 3.74300000e+01 2.22000000e+00 1.58000000e+00 1.60000000e+00 1.71000000e+00 1.64000000e+00 5.92800000e+01 -3.60000000e+00 2.94000000e+00 1.48739778e+08 1.04103224e+08 0.00000000e+00 0.00000000e+00 -7.66800000e+01 -8.49800000e+01]]# 导入必要的库fromsklearn.model_selectionimporttrain_test_splitfromsklearn.preprocessingimportStandardScalerfromsklearn.neural_networkimportMLPRegressor# 初始化一个多层感知器(MLP)回归模型# random_state=62 设置随机种子确保结果可复现# hidden_layer_sizes=[100,100] 定义两个隐藏层,每层100个神经元# alpha=0.001 设置L2正则化强度,防止过拟合mlpr=MLPRegressor(random_state=62,hidden_layer_sizes=[100,100],alpha=0.001)# 将数据集拆分为训练集和测试集# test_size默认值为0.25,即25%的数据作为测试集# random_state=62 确保每次拆分结果一致X_train,X_test,y_train,y_test=train_test_split(X,y,random_state=62)# 初始化标准化器,将特征数据缩放到均值为0、方差为1的分布scaler=StandardScaler()# 只在训练集上拟合标准化器(计算均值和标准差)scaler.fit(X_train)# 使用训练集得到的标准化参数转换训练集和测试集# 注意:测试集必须使用与训练集相同的标准化参数,避免数据泄露X_train_scaled=scaler.transform(X_train)X_test_scaled=scaler.transform(X_test)# 使用标准化后的训练数据训练MLP回归模型mlpr.fit(X_train_scaled,y_train)# 在标准化后的测试集上评估模型性能,输出R²分数(越接近1越好)print(mlpr.score(X_test_scaled,y_test))0.576631460901945# 从原始数据中提取股票名称列wanted=stock_cleaned.loc[:,"名称"]# 找出涨幅超过10%的股票名称并打印# 注意:这里使用的是整个数据集的y值,而不仅仅是测试集print(wanted[y>=10])# 导入特征选择模块fromsklearn.feature_selectionimportSelectPercentile# 初始化SelectPercentile特征选择器,选择前50%最重要的特征select=SelectPercentile(percentile=50)# 使用标准化后的训练数据和对应的标签来训练特征选择器select.fit(X_train_scaled,y_train)# 根据选择器筛选出的重要特征,转换训练集和测试集X_train_selected=select.transform(X_train_scaled)# 打印特征选择前后训练集的维度变化print(X_train_scaled.shape)# 原始特征维度print(X_train_selected.shape)# 特征选择后的维度# 获取一个布尔掩码,表示每个特征是否被选中(True表示选中)mask=select.get_support()print(mask)# 导入绘图库importmatplotlib.pyplotasplt# 将一维掩码转换为二维矩阵(1行×特征数列)并用热图可视化# cmap=plt.cm.cool 指定颜色映射plt.matshow(mask.reshape(1,-1),cmap=plt.cm.cool)plt.xlabel('Features Selected')# 设置x轴标签plt.show()# 显示图形# 使用相同的特征选择器转换测试集X_test_selected=select.transform(X_test_scaled)# 初始化一个新的MLP回归模型(用于特征选择后的数据)mlpr_sp=MLPRegressor(random_state=62,hidden_layer_sizes=[100,100],alpha=0.001)# 使用特征选择后的训练数据训练新的MLP模型mlpr_sp.fit(X_train_selected,y_train)# 在特征选择后的测试集上评估模型性能print(mlpr_sp.score(X_test_selected,y_test))0 广道退 1 C天溯 2 百纳千成 3 标榜股份 4 宏源药业 ... 65 中国卫星 66 名臣健康 67 华联控股 68 江西铜业 69 德力股份 Name: 名称, Length: 70, dtype: object (4089, 18) (4089, 9) [False True True True False False False False True True False False False False True True True True]0.35557528368026325# 添加更多评估指标fromsklearn.metricsimportmean_squared_error,mean_absolute_error y_pred_full=mlpr.predict(X_test_scaled)y_pred_selected=mlpr_sp.predict(X_test_selected)print(f"全特征模型 - R²:{mlpr.score(X_test_scaled,y_test):.4f}, "f"MSE:{mean_squared_error(y_test,y_pred_full):.4f}")print(f"特征选择后 - R²:{mlpr_sp.score(X_test_selected,y_test):.4f}, "f"MSE:{mean_squared_error(y_test,y_pred_selected):.4f}")全特征模型 - R²: 0.5766, MSE: 2.8013 特征选择后 - R²: 0.3556, MSE: 4.2639特征选择显著降低了模型的性能:
解释力下降:R²分数从0.5766降至0.3556,意味着模型对“涨跌幅”波动的解释能力大幅减弱。
误差增大:MSE从2.8013升至4.2639,表明模型预测值与真实值的平均偏差扩大了约52%。
这通常意味着当前的SelectPercentile方法过于激进,可能删除了对预测至关重要的特征。
基于模型的特征选择
基于模型的特征选择旨在通过评估并保留关键特征来提升模型性能。其核心是借助随机森林、梯度提升等模型内置的重要性评估机制,自动完成特征筛选。
# 1. 导入基于模型的特征选择工具和随机森林回归模型# SelectFromModel: 这是一个元转换器,可以与任何在拟合后具有`coef_`或`feature_importances_`属性的评估器一起使用# RandomForestRegressor: 集成学习模型,适合评估特征在复杂非线性关系中的重要性fromsklearn.feature_selectionimportSelectFromModelfromsklearn.ensembleimportRandomForestRegressor# 2. 创建基于随机森林的特征选择器# RandomForestRegressor参数:# n_estimators=100: 使用100棵决策树,数量越多通常效果越稳定,但计算成本也越高# random_state=38: 固定随机种子确保结果可复现# threshold='median': 选择阈值策略,保留重要性高于中位数的特征# 这意味着大约会保留50%的特征,但这是基于特征重要性分布动态决定的sfm=SelectFromModel(RandomForestRegressor(n_estimators=100,random_state=38),threshold='median')# 3. 在标准化后的训练数据上拟合特征选择器# 随机森林会同时评估所有特征的重要性,考虑特征间的相互作用# 相比SelectPercentile的单变量分析,这种方法更适合捕捉复杂关系sfm.fit(X_train_scaled,y_train)# 4. 使用训练好的选择器转换训练数据# 只保留被随机森林认为重要的特征X_train_sfm=sfm.transform(X_train_scaled)# 打印特征选择后的训练数据形状,查看保留了多少个特征print(X_train_sfm.shape)# 5. 获取特征选择掩码# 一个布尔数组,True表示对应位置的特征被选中,False表示被丢弃mask_sfm=sfm.get_support()print(mask_sfm)# 6. 可视化特征选择结果importmatplotlib.pyplotasplt# 将一维掩码转换为二维矩阵(1行×特征数列)并用热图可视化plt.matshow(mask_sfm.reshape(1,-1),cmap=plt.cm.cool)plt.xlabel('Features Selected')# 设置x轴标签plt.show()# 显示图形# 7. 使用相同的特征选择器转换测试数据# 重要:测试集必须使用与训练集相同的特征选择和标准化参数,避免数据泄露X_test_sfm=sfm.transform(X_test_scaled)# 8. 创建新的MLP回归模型(使用与之前相同的架构,便于公平比较)mlpr_sfm=MLPRegressor(random_state=62,hidden_layer_sizes=[100,100],alpha=0.001)# 9. 使用基于模型选择后的特征训练MLP模型mlpr_sfm.fit(X_train_sfm,y_train)# 10. 在特征选择后的测试集上评估模型性能print(mlpr_sfm.score(X_test_sfm,y_test))(4089,9)[FalseTrueFalseTrueFalseFalseFalseFalseTrueTrueTrueFalseFalseTrueFalseTrueTrueTrue]0.3650082808317642SelectFromModel的R²介于两者之间 (0.35-0.55),说明这种方法比SelectPercentile好,但仍有改进空间。
迭代式特征选择
迭代式特征选择是一种通过多轮建模逐步优化特征子集的高级策略。其核心思想在于:不依赖单次评估结果做决策,而是通过“模型训练 → 特征评估 → 剔除末位 → 再次训练”的递归循环,动态地筛选出最具预测力的特征组合。
在scikit-learn中,递归特征剔除法(Recursive Feature Elimination, RFE)是这一方法的经典实现。RFE的工作流程通常如下:首先,它利用一个基模型(如线性回归、支持向量机或随机森林)在全部特征上进行训练,并根据模型提供的权重或重要性评分对特征排序;然后,剔除排名最靠后的一定比例或固定数量的特征;接着,用剩余的特征集重复上述训练和剔除的过程,直到达到预设的特征数量或迭代次数。通过这种递归操作,RFE能够克服单变量选择忽略特征间交互作用的局限,也避免了单次模型选择可能带来的偏差,最终遴选出在协同作用下贡献最大的核心特征子集。
这种方法特别适用于特征数量庞大、且特征间存在复杂关联的场景。其优势在于,通过多轮迭代,模型能持续重新评估特征在变化的环境(即不同的特征组合)中的重要性,从而做出更稳健的选择。需要注意的是,RFE的计算成本相对较高,且其效果在很大程度上依赖于所选基模型评估特征重要性的准确性。
# 1. 从sklearn库导入递归特征消除(RFE)模块# RFE通过递归地移除最不重要的特征,实现迭代式特征选择fromsklearn.feature_selectionimportRFE# 2. 创建RFE特征选择器# 第一个参数:指定基评估器,这里使用随机森林回归器作为特征重要性判断的基础模型# - RandomForestRegressor: 集成学习模型,能有效评估特征在复杂关系中的重要性# - n_estimators=100: 使用100棵决策树构建随机森林# - random_state=38: 固定随机种子确保结果可复现# 第二个参数:n_features_to_select=12,指定最终要选择的特征数量# - 这意味着RFE会递归地剔除特征,直到只剩下12个最重要的特征# - 这个数字通常基于领域知识或通过交叉验证确定rfe=RFE(RandomForestRegressor(n_estimators=100,random_state=38),n_features_to_select=12)# 3. 在标准化后的训练数据上拟合RFE选择器# RFE会执行以下递归过程:# 1) 使用当前所有特征训练随机森林模型# 2) 根据特征重要性排序,剔除最不重要的特征# 3) 重复步骤1-2,直到剩余特征数达到n_features_to_select=12rfe.fit(X_train_scaled,y_train)# 4. 获取特征选择掩码# 返回一个布尔数组,True表示对应位置的特征被选中,False表示被剔除mask_rfe=rfe.get_support()print(mask_rfe)# 打印掩码,查看哪些特征被选中# 5. 可视化特征选择结果importmatplotlib.pyplotasplt# 将一维掩码转换为二维矩阵(1行×特征数列)并用热图可视化plt.matshow(mask_rfe.reshape(1,-1),cmap=plt.cm.cool)plt.xlabel('Features Selected')# 设置x轴标签plt.show()# 显示图形# 6. 使用训练好的RFE选择器转换训练数据# 只保留被RFE选中的12个特征X_train_rfe=rfe.transform(X_train_scaled)# 7. 使用相同的RFE选择器转换测试数据# 重要:测试集必须使用与训练集相同的特征选择参数,避免数据泄露X_test_rfe=rfe.transform(X_test_scaled)# 8. 创建新的MLP回归模型(使用与之前相同的架构,便于公平比较)mlpr_rfe=MLPRegressor(random_state=62,hidden_layer_sizes=[100,100],alpha=0.001)# 9. 使用RFE选择后的特征训练MLP模型mlpr_rfe.fit(X_train_rfe,y_train)# 10. 在特征选择后的测试集上评估模型性能# 输出R²分数,衡量模型对"涨跌幅"的解释能力r2_score=mlpr_rfe.score(X_test_rfe,y_test)print(f"RFE特征选择后的模型R²分数:{r2_score:.4f}")[False True True True False False False True True True True True False True False True True True]RFE特征选择后的模型R²分数: 0.4114RFE的R²介于全特征和SelectPercentile之间 (0.36-0.57),说明RFE比简单的单变量筛选更有效,但特征数量(12个)可能需要调整。
根据您提供的RFE特征选择结果(R²: 0.4114)和选中的12个特征,我们可以进行深入分析,并与之前的实验结果对比,从而明确下一步的优化方向。
📊 结果解读与对比
首先,我们将所有方法的性能进行汇总对比:
| 特征选择方法 | R²分数 | 特征数量 (约) | 效果评价 |
|---|---|---|---|
| 全特征模型(基准) | 0.5766 | 全部特征 | 性能最佳,但模型复杂,可能过拟合。 |
| SelectPercentile (50%) | 0.3556 | 约50% | 性能下降严重,单变量线性筛选不适合此数据。 |
| RFE (选12个特征) | 0.4114 | 12个 | 表现居中,比单变量方法好,但距离全特征仍有差距。 |
核心结论:
- RFE是有效的:其性能显著优于
SelectPercentile,说明其多轮迭代、考虑特征交互的筛选策略更适合您复杂的股票数据。 - 仍有提升空间:0.4114的R²分数意味着模型仅能解释约41%的“涨跌幅”波动,当前选出的12个特征可能尚未达到最优组合,或者数量不足。