You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

sklearn RandomForestClassifier根节点为何与最重要特征不一致

scikit-learn中RandomForestClassifier特征重要性计算规则

scikit-learn中随机森林的feature_importances_默认采用平均不纯度减少(MDI) 方法计算,规则如下:

  • 对单棵决策树:遍历树的所有分裂节点,计算每个节点用对应特征分裂带来的不纯度(由criterion参数指定为gini或熵)下降值,乘以该节点覆盖的加权样本数(无自定义样本权重时就是节点包含的样本数),得到该特征在这个节点的贡献值;把同一特征在所有节点的贡献值求和,再除以所有特征的总贡献值做归一化,保证所有特征重要性之和为1,就是单棵树的特征重要性。
  • 对随机森林集成:先对每一棵基决策树按上述规则计算单树特征重要性,再对所有树的特征重要性取平均值,最后再做一次归一化,得到全局特征重要性。
单棵基决策树根节点与全局最重要特征不匹配的原因

这个现象是随机森林的训练机制导致的,属于正常情况,核心原因有三点:

  • 基决策树训练存在双重随机,单棵树的分裂选择不具备全局代表性:
    • 样本随机:每棵基树的训练数据是从原数据集有放回bootstrap抽样得到,平均仅包含63%左右的原样本,不同树的训练数据分布存在天然差异。
    • 特征随机:随机森林训练时,每个节点分裂不会从全部特征里选最优,只会从随机抽取的max_features个特征中选择最优分裂特征。分类任务默认max_features="sqrt",对应测试代码里10个特征的场景,每次分裂仅随机抽3个特征候选,根节点分裂时没抽到全局最重要特征的概率高达70%,自然不可能选该特征作为根节点。
  • 仅可视化了森林中的第0棵基树,单棵树的结果方差极高。随机森林默认包含100棵基树(n_estimators=100),单棵树的分裂结果受随机采样影响波动很大,不能代表上百棵树的平均统计结果。如果统计所有基树的根节点特征,会发现全局重要性最高的特征出现的占比最高,但不会达到100%。
  • 特征重要性统计的是特征在全树所有分裂节点的累计贡献,不是仅统计根节点的贡献。某个特征即使很少出现在根节点位置,但如果它在大量树的中、深层节点分裂时带来很高的不纯度下降,累计贡献值也可能成为全局最高;反过来某个特征在少数树里当根节点,但其他节点几乎不用它,累计重要性也排不到第一。

单棵决策树不存在该问题的原因:单棵决策树训练时没有bootstrap采样,用全量数据训练,且所有节点分裂都从全部特征中选最优,根节点本身就是全量数据下带来单次不纯度下降最大的特征,特征重要性也是基于自身所有分裂节点计算的,自然根节点特征和自身输出的最重要特征完全匹配。

复现代码

核心训练逻辑

from sklearn.datasets import make_classification
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier
from sklearn.tree import export_graphviz
from IPython.display import Image, display
from subprocess import call
import matplotlib.pyplot as plt
import numpy as np
from sklearn.datasets import load_wine
from sklearn.datasets import load_iris
wines = load_wine()
iris = load_iris()

    
def create_and_fit(clf,model_name):
    print(clf)
    # 定义数据集
    X, y = make_classification(n_samples=1000, n_features=10, n_informative=3, n_redundant=5, random_state=seed)
    # 可切换iris、wine数据集测试
    # X,y = iris.data, iris.target
    # X,y = wines.data, wines.target
    # 模型训练
    clf.fit(X, y)

    # 获取并打印特征重要性
    importance = clf.feature_importances_
    indices = np.argsort(importance)[::-1]
    
    for f in range(X.shape[1]):
        print("feature {}: ({})".format(indices[f], importance[indices[f]]))
    filename = model_name+model.criterion
    if model_name == 'forest_':
        print('forest')
        # 导出随机森林第0棵基树的结构
        export_graphviz(clf.estimators_[0], out_file=filename+'.dot')
    else:
        # 导出单棵决策树的结构
        export_graphviz(clf, out_file=filename+'.dot')
    call(['dot', '-Tpng', filename+'.dot', '-o', filename+'.png', '-Gdpi=600'])


seed=0    
# 随机森林训练
models = [
    RandomForestClassifier(criterion='gini',max_depth=5, random_state=seed),
    RandomForestClassifier(criterion='entropy',max_depth=5, random_state=seed),
]    
names =['forest_', 'forest_']

for name, model in zip(names, models):
    create_and_fit(model,name)

随机森林结构可视化

# 加载gini准则随机森林第0棵基树可视化结果
Image(filename = 'forest_gini'+'.png')
# 加载entropy准则随机森林第0棵基树可视化结果
Image(filename = 'forest_entropy'+'.png')

单棵决策树对照实验

# 单决策树对照训练
models = [
    DecisionTreeClassifier(criterion='gini',max_depth=5, random_state=seed),
    DecisionTreeClassifier(criterion='entropy',max_depth=5, random_state=seed)
]    
names =['tree_', 'tree_']

for name, model in zip(names, models):
    create_and_fit(model,name)

决策树结构可视化

# 加载gini准则决策树可视化结果
Image(filename = 'tree_gini'+'.png')
# 加载entropy准则决策树可视化结果
Image(filename = 'tree_entropy'+'.png')

内容的提问来源于stack exchange,提问作者asiehh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 09:27:28