Notes—Random Forest-feature importance随机森林对特征排序

随机森林特征重要性:Mean Decrease Impurity与Accuracy
本文介绍了随机森林中评估特征重要性的两种方法:Mean Decrease Impurity(MDI)和Mean Decrease Accuracy(MDA)。MDI通过计算每个特征对袋外数据误差的影响来衡量重要性,而MDA则是通过扰动特征值观察对预测准确性的影响。在sklearn中,MDI得到实现,而MDA未直接提供。在Spark的ml.classification.RandomForestClassificationModel中,可以通过featureImportances获取特征重要性。

……未完待补充……
ref:http://blog.datadive.net/selecting-good-features-part-iii-random-forests/

two methods:
1.Mean decrease impurity
大概是对于每颗树,按照impurity(gini /entropy /information gain)给特征排序,然后整个森林取平均

2.Mean decrease accuracy
大概就是measure一下对每个特征加躁,看对结果的准确率的影响。影响小说明这个特征不重要,反之重要
具体步骤如下:
在随机森林中某个特征X的重要性的计算方法如下:
1:对于随机森林中的每一颗决策树,使用相应的OOB(袋外数据)数据来计算它的袋外数据误差,记为errOOB1.
2: 随机地对袋外数据OOB所有样本的特征X加入噪声干扰(就可以随机的改变样本在特征X处的值),再次计算它的袋外数据误差,记为errOOB2.
3:假设随机森林中有Ntree棵树,那么对于特征X的重要性=∑(errOOB2-errOOB1)/Ntree,之所以可以用这个表达式来作为相应特征的重要性的度量值是因为:若给某个特征随机加入噪声之后,袋外的准确率大幅度降低,则说明这个特征对于样本的分类结果影响很大,也就是说它的重要程度比较高。
ref:https://www.cnblogs.com/justcxtoworld/p/3447231.html
http://www.cnblogs.com/justcxtoworld/p/3434266.html

1.Mean decrease impurity
sklearn中实现如下:

from sklearn.datasets import load_boston
from sklearn.ensemble import RandomForestRegressor
import numpy as np
#Load boston housing dataset as an example
boston = load_boston()
X = boston["data"]
print type(X),X.shape
Y = boston["target"]
names = boston["feature_names"]
print names
rf = RandomForestRegressor()
rf.fit(X, Y)
print "Features sorted by their score:"
print sorted(zip(map(lambda x: round(x, 4), rf.feature_importances_), names), reverse=True)

结果如下:

Features sorted by their score:
[(0.5104, 'RM'), (0.2837, 'LSTAT'), (0.0812, 'DIS'), (0.0303, 'CRIM'), (0.0294, 'NOX'), (0.0176, 'PTRATIO'), (0.0134, 'AGE'), (0.0115, 'B'), (0.0089, 'TAX'), (0.0077, 'INDUS'), (0.0051, 'RAD'), (0.0006, 'ZN'), (0.0004, 'CHAS')]

2.Mean decrease accuracy
sklearn中并没有……具体实现见链接

3.Spark中的feature importance使用
ml.classification.RandomForestClassificationModel中有featureImportance可以直接调用
定义如下:
lazy val featureImportances:Vector
调用:
Model.featureImportances.toArray.mkString(“,”)

下面给出一个专业、系统的解题框架,围绕题干要求设计一个“Funnelling(分层筛选)”的多步特征选择流程,综合滤波(filter)、包装(wrapper)和嵌入(embedded)三类方法,并给出可直接运行的 Python 实现。 一、题干分析与思路提示 目标:在一个二分类或回归任务中,通过三类特征选择方法的多步“漏斗式”组合,逐步筛出对模型最有用、且彼此之间较为独立的特征集合,最终得到一个稳健且可解释的特征子集。 三类方法的角色: 滤波(Filter):快速、无模型依赖地评估每个特征与目标的统计相关性,去除明显无信息的特征,降低维度与噪声,提供初步的候选集合。 包装(Wrapper):在真实预测建模的情境下评估特征子集的组合效果,考虑特征之间的交互与冗余,选出对模型性能贡献最大的子集。 嵌入(Embedded):在模型训练过程中进行特征选择(如正则化导致的系数稀疏化、基于特征重要性的裁剪等),实现对特征在特定模型中的“实际贡献”评估。 融合策略(Funnelling 重点):将三步得到的候选集合进行整合,常用的方法包括取并集/交集、权重投票或简单的交叉验证性能对比。在本题中,推荐使用“按强制交叉一致性(intersection)”的做法:最终特征集为三步都认可的特征的交集,得到稳定性较高的特征子集;如需要,可以在此基础上做一个小的后处理(如在交集基础上再加上部分_wrapper_或_embedded_中表现稳定的特征)。 关键注意点: 避免数据泄露:筛选过程尤其在 wrapper/embedded 阶段应使用交叉验证,且跨步骤尽量使用相同分割策略,避免在同一步骤中对测试数据进行暴露。 。从实现角度,建议把整套流程包装成一个管道(或分阶段管道),并尽量将数据分割、交叉验证和特征评估的设置统一。 处理类型:若数据包含类别型特征,需要在滤波阶段做适当的编码或对数值化处理;本实现主要以数值型特征为主,若有类别特征可在管道中使用 OneHotEncoder 进行编码后再进行筛选。 二、解题步骤(a、b、c) A. (a) 三类方法的逐步特征选择流程(分步解释) 滤波阶段(Filter) 目的:快速初筛,去除明显无关或冗余的特征,降低后续计算成本。 常用工具与指标: 低方差(Variance Threshold)去除常量/近似常量特征。 互信息(mutual_info_classif / mutual_info_regression)或信息增益,衡量单个特征与目标之间的非线性关系。 相关性过滤:基于与目标的相关性排序,或与其他特征的相关性矩阵,去除高度相关的特征以降低多重共线性。 实施要点: 首先应用 VarianceThreshold 去除零方差特征。 使用 SelectKBest( mutual_info_classif / mutual_info_regression ) 选取前 k 个最相关特征(k 根据数据规模设定,例如 0.6 × n_features)。 计算特征之间的相关性矩阵,按相关性阈值(如 |corr| > 0.95)筛选出高度共线的特征,保留信息量更高的那个特征。 产出:一个“候选特征集”F_filter。 包装阶段(Wrapper) 目的:在一个具体的预测模型框架下评估特征子集对模型性能的贡献,考虑特征之间的交互和冗余。 常用工具与策略: RFECV(递归特征消除带交叉验证):在每一步去掉性能贡献最小的特征,评估跨越多种子集的模型性能,选出最佳特征子集大小及对应特征。 也可以使用 Sequential Feature Selector(逐步前向/后向选择),但 RFECV 更自动、稳定。 实施要点: 选择一个基础估计器(如 Logistic Regression、Random Forest、SVM 等),通常建议使用线性模型(如 Logistic Regression)作为 Wrapper 的起点,因为其系数直接可解释,计算成本相对较低。 使用 StratifiedKFold(分类)或 KFold(回归)进行交叉验证,确保对不平衡数据的鲁棒性。 产出:一个“Wrapper 选中的特征集”F_wrapper。 嵌入阶段(Embedded) 目的:在模型训练过程中自带的特征选择机制,通常实现稀疏性(如 L1 正则化导致系数为零)或基于特征重要性的裁剪。 常用工具与策略: L1/L1/L2 正则化的逻辑回归、线性模型等(penalty='l1',solver='liblinear' 等),通过学习到的系数来决定保留的特征(非零系数)。 基于树的模型(如 RandomForest、GradientBoosting)自带的特征重要性排序,保留重要性高的特征。 对于嵌入阶段,通常需要对超参数(如 C)进行交叉验证来选择合适的正则化强度,以获得稳定且可解释的特征集合。 实施要点: 使用 L1 正则化的逻辑回归并通过网格搜索选择最佳 C,使若干系数降为零,保留的特征即为嵌入阶段的输出。 产出:一个“Embedded 选中的特征集”F_embedded。 融合/最终选取 策略:将三步的结果进行交集(F_final = F_filter ∩ F_wrapper ∩ F_embedded),得到在三步中均被认为有意义的特征,用以提升鲁棒性和可解释性。 如若交集为空,可以考虑放宽条件(如允许交集与某一步的特征并集的交叉折中),但优先保持严格的交集以获得稳定特征。 最终输出:最终特征列表 F_final(按原始数据中的特征名称排序)。 B. (b) 选择在各步保留的特征的原因与证据 滤波阶段 原因:快速消除无信息、冗余度高的特征,降低维度,避免后续步骤的过拟合与计算开销。 证据/理由:单变量指标(如互信息)能快速揭示与目标之间的相关性,且与模型无关,具有较高的鲁棒性。 约束/注意:不能完全忽视特征间的交互;因此仍需进入 Wrapper 阶段以捕捉特征组合效应。 包装阶段 原因:考虑特征之间的相互作用与依赖性,评估子集在具体预测任务中的实际表现,能发现单变量关系被忽视的组合性特征。 证据/理由:RFECV 能在多轮交叉验证下选择对模型最有贡献的特征子集,减少冗余度和过拟合风险。 约束/注意:计算开销较大,需合理选择基模型与交叉验证设置,避免数据泄露。 嵌入阶段 原因:在模型训练过程中对特征进行稀疏化或基于树的重要性筛选,是对特征在特定模型中的“实际贡献”的直接量化。 证据/理由:L1 正则化能将不必要的系数压缩至零,从而自然选出稳定、可解释的特征集合;树模型的特征重要性也能揭示对预测有显著贡献的特征。 约束/注意:不同模型的偏好可能不同,最好结合多种嵌入策略并在同一数据集上进行对照分析。 融合策略的合理性 采用交集方式强调三个阶段的一致性,通常能得到鲁棒且可解释的特征,降低因任一阶段偶然性带来的误选风险。 如数据规模较大、噪声较多,也可采用加权投票或分层过滤进行慎重的放宽。 C. (c) 最终选取的特征清单 最终特征应来自 F_final = F_filter ∩ F_wrapper ∩ F_embedded。请注意: F_final 的具体内容依赖于数据集的特征、目标、以及实施中的参数设置(比如 k 的取值、相关性阈值、C 的取值等)。 在真实作业中,需给出你自己的数据集、执行上述步骤后得到的最终特征清单。 下面给出一个完整的可运行实现(Python),并附带一个示例数据集演示,最终输出最终特征清单。你可以将数据替换成你自己的数据来得到你课程作业中的最终结果。 三、Python 实现(完整代码示例) 说明 该实现以分类任务为例,若数据是回归任务,请将互信息函数与相关指标替换为 mutual_info_classif -> mutual_info_regression,并相应调整评估指标(如 accuracy 替换为 r2、neg_mean_squared_error 等)。 代码包含三个阶段的实现,以及最终的交集输出和一个简要的性能对比示例(全特征 vs 最终特征)以便评估效果。 代码(请直接运行) """ Python 3.x 需要的库(请确保已安装):pandas scikit-learn numpy pip install numpy pandas scikit-learn """ import numpy as np import pandas as pd from sklearn.datasets import load_breast_cancer from sklearn.model_selection import train_test_split, StratifiedKFold, RFECV, GridSearchCV from sklearn.impute import SimpleImputer from sklearn.feature_selection import VarianceThreshold, SelectKBest, mutual_info_classif from sklearn.metrics import accuracy_score from sklearn.linear_model import LogisticRegression from sklearn.pipeline import Pipeline from sklearn.preprocessing import StandardScaler from sklearn.pipeline import FeatureUnion --------- 1) 数据加载与分割 --------- 这里用乳腺癌数据集作为示例,特征命名采用 data.feature_names data = load_breast_cancer(as_frame=True) X_all = data.data y = data.target 将特征名作为 DataFrame 的列名,确保后续能跟踪原始特征 X = pd.DataFrame(X_all, columns=data.feature_names) feature_names = list(X.columns) 划分训练集与测试集(为了演示,后续交叉验证在训练集内部完成) X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, stratify=y, random_state=42) --------- 辅助函数 --------- def prune_by_correlation(X_df, mi_scores, corr_threshold=0.95): """ 在给定特征集合和对应的MI分数下,去除高度相关的特征(> corr_threshold),保留 MI 较高者。 输入: X_df: DataFrame,待筛选的特征集合 mi_scores: dict{feature: MI score} corr_threshold: float,相关性阈值 输出: 保留的特征列表 """ features = list(X_df.columns) if len(features) <= 1: return features corr_matrix = X_df.corr().abs() to_drop = set() # 遍历上三角,找出高度相关的特征对 for i in range(len(features)): for j in range(i + 1, len(features)): f1 = features[i] f2 = features[j] if corr_matrix.loc[f1, f2] > corr_threshold: # 保留相关性较高的那个(MI更高者) if mi_scores.get(f1, 0) >= mi_scores.get(f2, 0): to_drop.add(f2) else: to_drop.add(f1) kept = [f for f in features if f not in to_drop] return kept --------- 2) 滤波阶段(Filter) --------- def filter_stage(X, y, variance_thresh=0.0, top_k=None, corr_thresh=0.95, seed=0): """ 滤波阶段: - VarianceThreshold: 移除低方差特征 - SelectKBest with mutual_info_classif: 选出前 top_k 个特征(若 top_k=None,则保留所有经过方差过滤的特征- 基于相关性阈值进行冗余特征剔除:保留 MI 值更高的特征,丢弃相关性高但信息量低的特征 返回: filter_features:最终在滤波阶段保留的特征名列表 X_filtered:对应的特征子集 DataFrame mi_scores:每个保留特征的 MI 分数(供后续阶段使用) """ # 1) VarianceThreshold vt = VarianceThreshold(threshold=variance_thresh) X_var = vt.fit_transform(X) var_mask = vt.get_support(indices=True) features_after_var = [X.columns[i] for i in var_mask] if len(features_after_var) == 0: return [], pd.DataFrame(), {} X_after_var = pd.DataFrame(X_var, columns=features_after_var) # 2) SelectKBest with mutual_info_classif if top_k is None or top_k >= len(features_after_var): # 直接计算 MI for all features after variance filter mi = mutual_info_classif(X_after_var, y, discrete_features=False, random_state=seed) mi_scores = dict(zip(X_after_var.columns, mi)) # 排序并选取全部作为候选集(仍可后续再过滤) features_after_mi = list(X_after_var.columns) scores = mi_scores else: selector = SelectKBest(score_func=mutual_info_classif, k=top_k) _ = selector.fit(X_after_var, y) idx = selector.get_support(indices=True) features_after_mi = [X_after_var.columns[i] for i in idx] scores = dict(zip(X_after_var.columns, selector.scores_)) mi_scores = {f: scores.get(f, 0.0) for f in features_after_mi} X_after_mi = X_after_var[features_after_mi] # 3) 相关性过滤(按 MI 分数保留信息量更高的特征,剔除高度相关的特征) if len(features_after_mi) > 1: mi_scores_all = {f: mi_scores.get(f, 0.0) for f in features_after_mi} features_after_corr = prune_by_correlation(X_after_mi, mi_scores_all, corr_thresh) else: features_after_corr = features_after_mi X_filtered = X_after_mi[features_after_corr] filter_features = features_after_corr return filter_features, X_filtered, mi_scores_all if len(features_after_mi) > 0 else {} --------- 3) 包装阶段(Wrapper) --------- def wrapper_stage(X, y, estimator=None, cv_splits=5): """ Wrapper 阶段: - 使用 RFECV 在给定的估计器下寻找最佳特征子集大小与特征集合 - 返回选中的特征名称列表 """ if estimator is None: estimator = LogisticRegression(max_iter=1000, solver='liblinear', random_state=0) rfecv = RFECV(estimator=estimator, step=1, cv= StratifiedKFold(n_splits=cv_splits, shuffle=True, random_state=0), scoring='accuracy') rfecv.fit(X, y) selected_features = list(X.columns[rfecv.support_]) return selected_features --------- 4) 嵌入阶段(Embedded) --------- def embedded_stage(X, y, C_values=None, cv_splits=5): """ Embedded 阶段: - 使用 L1 正则化的逻辑回归进行特征选择(非零系数即保留) - 通过网格搜索选择最佳 C 返回: embedded_features:在嵌入阶段保留的特征名称列表 """ if C_values is None: C_values = [0.01, 0.1, 1, 10] lr_l1 = LogisticRegression(penalty='l1', solver='liblinear', max_iter=1000, random_state=0) grid = GridSearchCV(estimator=lr_l1, param_grid={'C': C_values}, cv=StratifiedKFold(n_splits=cv_splits, shuffle=True, random_state=0), scoring='accuracy') grid.fit(X, y) best_C = grid.best_params_['C'] best_model = grid.best_estimator_ # 取得非零系数的特征 coef = best_model.coef_ # 一般二分类单输出,coef 的形状为 (1, n_features) non_zero_mask = coef[0] != 0 embedded_features = list(pd.Series(X.columns)[non_zero_mask]) return embedded_features --------- 5) 融合:最终选取 --------- def funnel_feature_selection(X, y, variance_thresh=0.0, top_k=None, corr_thresh=0.95, wrapper_seed=0, C_values=None): """ 完整的 Funnelling 流程: 1) 滤波阶段 -> 获取 F_filter 与 X_filtered 2) 包装阶段 -> 获取 F_wrapper 3) 嵌入阶段 -> 获取 F_embedded 4) 最终特征为 F_final = F_filter ∩ F_wrapper ∩ F_embedded 返回: final_features:最终筛选出的特征名列表 details:包含每一阶段的特征集合供分析 """ # 1) 滤波阶段 F_filter, X_filter, _ = filter_stage(X, y, variance_thresh=variance_thresh, top_k=top_k, corr_thresh=corr_thresh, seed=wrapper_seed) # 2) 包装阶段 if len(F_filter) == 0: # 如果滤波阶段全部失败,直接用原始特征进行包装 X_for_wrapper = X else: X_for_wrapper = X_filter # 选择一个合适的估计器(默认 Logistic Regression) estimator = LogisticRegression(max_iter=1000, solver='liblinear', random_state=wrapper_seed) F_wrapper = wrapper_stage(pd.DataFrame(X_for_wrapper, columns=X_for_wrapper.columns), y, estimator=estimator, cv_splits=5) # 将 Wrapper 的特征映射回原始特征名称(若 X_for_wrapper 是 X_filter 的子集,需保持一致) # 这里确保 F_wrapper 使用的是 X_for_wrapper 的列名;直接使用即可 # 3) 嵌入阶段 embedded_features = embedded_stage(X_for_wrapper[F_wrapper] if len(F_wrapper) > 0 else X_for_wrapper, y, C_values=C_values) # 4) 最终交集 F_filter_set = set(F_filter) F_wrapper_set = set(F_wrapper) F_embedded_set = set(embedded_features) final_features = sorted(list(F_filter_set & F_wrapper_set & F_embedded_set)) details = { 'F_filter': F_filter, 'F_wrapper': F_wrapper, 'F_embedded': embedded_features, 'F_final': final_features } return final_features, details --------- 6) 演示:在乳腺癌数据集上运行 --------- 设定参数,执行 Funnel final_features, details = funnel_feature_selection(X, y, variance_thresh=0.0, top_k=20, # 滤波阶段选择前 20 个特征作为候选 corr_thresh=0.95, wrapper_seed=0, C_values=[0.01, 0.1, 1, 10]) print("最终选取的特征数量:", len(final_features)) print("最终特征列表(按字母序排序):") print(final_features) 可选:对比全量特征的模型性能与最终特征的模型性能 简单示例:使用 Logistic Regression,在测试集上评估 from sklearn.metrics import accuracy_score from sklearn.pipeline import make_pipeline 使用最终特征的子集训练一个简单模型 def evaluate_model_with_features(feature_list): if len(feature_list) == 0: return None X_train_sub = X_train[feature_list] X_test_sub = X_test[feature_list] model = LogisticRegression(max_iter=1000, solver='liblinear') model.fit(X_train_sub, y_train) preds = model.predict(X_test_sub) acc = accuracy_score(y_test, preds) return acc 全量特征 acc_full = evaluate_model_with_features(feature_names) 最终特征 acc_final = evaluate_model_with_features(final_features) print("全量特征的测试集准确率(示例):", acc_full) print("最终特征集的测试集准确率(示例):", acc_final) """ 说明 如你需要把这段代码直接运用于你的数据集,请把数据读入 X、y,替换相关参数即可。 输出的 final_features 即为你的最终特征清单,请将其作为你作业的(c)部分的“最终列表”提交。 """ 运行结束 若你需要,我还能: 基于你给的具体数据集,替换数据加载部分并输出你数据集上的最终特征清单。 提供一个可选的并集/交集混合策略(如若交集为空时采用并集的稳健备选)。 给出更详细的性能评估(如交叉验证的平均准确率、标准差、特征稳定性分析等)。 简要总结 本解法把 Feature Selection 划分为三步:Filter(快速初筛)、Wrapper(评估子集的实际预测效果)、Embedded(模型内的稀疏化/重要性筛选),最后通过交集融合得到稳定的最终特征集。 该流程兼顾了计算效率、特征之间的相互作用、以及模型在特定任务中的实际贡献,能够提供可解释且鲁棒的特征子集。 将上述内容翻译成英文
最新发布
10-17
### 随机森林特征重要性计算方法 随机森林是一种基于决策树集成的学习算法,其核心优势之一在于能够评估特征的重要性。这种评估对于特征选择以及深入理解数据集的内在结构具有重要意义。具体而言,随机森林通过计算每个特征在整个森林中所有树的信息增益来衡量该特征的重要程度[^1]。 #### 方法实现 在随机森林中,特征重要性的计算通常采用两种主要策略: 1. **基尼不纯度减少法 (Gini Impurity Reduction)** 这种方法利用每棵树节点分裂时所使用的特征及其对应的基尼指数变化量来进行统计。每次分割都会带来一定的信息增益(即基尼不纯度的降低),这些增益会被累积并最终取平均值作为某个特征的整体重要性得分。 2. **排列重要性 (Permutation Importance)** 排列重要性则是在模型已经训练完成的基础上重新打乱单个特征的值,观察这样做对预测误差的影响大小。如果某特征被打乱后显著增加了测试错误率,则说明这个特征非常重要;反之亦然。这种方法虽然更耗时但也更加通用,因为它独立于具体的损失函数定义之外。 以下是使用Python中的`scikit-learn`库实现上述第一种方法的具体代码示例: ```python from sklearn.datasets import load_iris from sklearn.ensemble import RandomForestClassifier import numpy as np import matplotlib.pyplot as plt # 加载样本数据集 data = load_iris() X, y = data.data, data.target # 训练随机森林分类器 rfc = RandomForestClassifier(n_estimators=100, random_state=42) rfc.fit(X, y) # 获取特征重要性分数 feature_importances = rfc.feature_importances_ # 打印结果并与原始特征名称对应起来 for name, importance in zip(data.feature_names, feature_importances): print(f"{name}: {importance}") # 可视化展示 indices = np.argsort(feature_importances)[::-1] plt.figure(figsize=(8, 6)) plt.title("Feature Importances") plt.bar(range(len(indices)), feature_importances[indices], align="center", color='b') plt.xticks(range(len(indices)), [data.feature_names[i] for i in indices]) plt.show() ``` 此脚本展示了如何加载Iris数据集、构建随机森林模型,并提取和可视化各个输入变量相对于目标标签的关键贡献比例。 值得注意的是,在其他梯度提升框架比如LightGBM里也有类似的特性评价机制,不过它们可能采用了不同的内部逻辑去量化所谓的“重要性”,例如基于权重(weight)或者收益(gain),其中官方文档建议优先考虑weight模式下的指标解释力更强一些[^2]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值