随机森林分类器中的特征重要性是如何确定的?

146

我有一个分类任务,其数据输入为时间序列,其中每个属性(n = 23)表示特定的时间点。除了绝对的分类结果外,我想要找出哪些属性/日期对结果产生了何种程度的贡献。因此,我只是使用了feature_importances_,这对我来说效果不错。

然而,我想知道它们是如何计算的,以及使用了哪种度量/算法。不幸的是,我没有找到任何关于这个主题的文档。


14
哇,一个 Stack Overflow 的帖子里有三个核心开发者在线,这一定是某种记录 ^^ - Andreas Mueller
7个回答

179

确实有几种方法可以获得特征的“重要性”。通常情况下,对于这个词的含义并没有严格一致的共识。

在scikit-learn中,我们实现了在[1]中描述的重要性(经常被引用,但不幸的是很少被阅读...)。它有时被称为“基尼重要性”或“平均减少不纯度”,定义为整个集成树的节点不纯度减少总量(加权节点到达的概率(由到达该节点的样本比例近似))的平均值。

在文献或其他软件包中,您还可以找到将特征重要性实现为“平均减少准确性”的方法。基本上,这个想法是当您随机置换该特征的值时,在OOB数据上测量准确性降低的程度。如果减少程度较低,则该特征不重要,反之亦然。

(注意,这两个算法都在randomForest R软件包中可用。)

[1]: Breiman, Friedman,“分类和回归树”,1984年。


52
如果在重要属性/示例的文档中提到了这个答案,那就太好了。我也已经搜索了一段时间了 :) - d1337
3
重要性分值似乎是相对值?例如,所有特征的重要性分值之和始终为1(请参见此处的示例http://scikit-learn.org/stable/auto_examples/ensemble/plot_forest_importances.html#example-ensemble-plot-forest-importances-py)。 - RNA
6
是的,在scikit-learn中,变量重要性默认情况下是被归一化的,以便它们总和为一。您可以通过循环遍历每个基础估计器并调用tree_.compute_feature_importances(normalize=False)来避免这种情况。 - Gilles Louppe
3
@GillesLouppe您是否使用袋外样本来测量每棵树中决策树回归器森林的MSE减少?还是所有训练数据都用于树上? - Cokes
1
两个有用的资源。 (1) Ando Saabas的博客(http://blog.datadive.net/selecting-good-features-part-iii-random-forests/) 实现了Gilles提到的“平均减少不纯度”和“平均减少准确性”。(2)下载并阅读Gilles Louppe的论文。 - Mark Teese
显示剩余4条评论

58
计算单个树的特征重要性值通常有以下步骤:
  1. 初始化一个大小为n_features的全零数组feature_importances

  2. 遍历树:对于每个在特征i上分裂的内部节点,计算该节点的错误减少量乘以被路由到该节点的样本数,并将此数量添加到feature_importances[i]中。

错误减少量取决于使用的不纯度标准(例如Gini、熵、MSE等)。它是被路由到内部节点的示例集合的不纯度减去拆分创建的两个分区的不纯度之和。

这些值与特定数据集相关(错误减少量和样本数都是数据集特定的),因此这些值不能在不同数据集之间进行比较。

据我所知,有其他方法可以计算决策树中的特征重要性值。以上方法的简要说明可在Trevor Hastie、Robert Tibshirani和Jerome Friedman的《统计学习基础》一书中找到。


16

这是集成树中涉及该特征的决策节点样本数与训练集中总样本数之比。

出现在决策树顶层节点的特征通常会看到更多样本,因此可能具有更重要的作用。

编辑: 这个描述只有部分正确:Gilles和Peter的答案才是正确的。


1
你知道是否有关于确切方法的论文/文档吗?例如,Breiman,2001年的。如果我有一份适当的文件可以引用该方法,那将是非常好的。 - user2244670
1
@ogrisel 如果您能清楚地标记您的回复作为“加权”的解释,那将是非常好的。仅仅加权并不能确定特征的重要性。通过树的加权“不纯度指标”(“基尼重要性”或RSS),平均后确定整体特征的重要性。不幸的是,scikit-learn文档中关于此处的说明:http://scikit-learn.org/stable/modules/ensemble.html#feature-importance-evaluation 不准确,并错误地提到了“深度”作为不纯度指标。 - Ariel

14
如 @GillesLouppe 上面指出的,scikit-learn 目前实现了“平均减少不纯度”这种特征重要性度量。我个人觉得第二种度量方法更有趣,即对每个特征随机置换其值,然后观察袋外表现变差的程度。
由于特征重要性的目的在于衡量每个特征对整体模型预测性能的贡献,第二种度量方法实际上直接给出了这一度量,而“平均减少不纯度”只是一个好的近似。
如果您感兴趣,我写了一个小包,可以实现排列重要性度量,并可用于计算 scikit-learn 随机森林类的实例中的值:

https://github.com/pjh2011/rf_perm_feat_import

编辑:这适用于Python 2.7,不适用于3


嗨@Peter,当我使用你的代码时,我遇到了这个错误:NameError: name 'xrange' is not defined。 - Aizzaac
嗨@Aizzaac。抱歉我是新手写包,所以我应该注意到我是为Python 2.7编写的。在运行之前尝试def xrange(x):return iter(range(x))。 - Peter

4

代码:

iris = datasets.load_iris()  
X = iris.data  
y = iris.target  
clf = DecisionTreeClassifier()  
clf.fit(X, y)  

决策树图:
在此输入图片描述
我们得到:

compute_feature_importance:[0. ,0.01333333,0.06405596,0.92261071]   

检查源代码:

cpdef compute_feature_importances(self, normalize=True):
    """Computes the importance of each feature (aka variable)."""
    cdef Node* left
    cdef Node* right
    cdef Node* nodes = self.nodes
    cdef Node* node = nodes
    cdef Node* end_node = node + self.node_count

    cdef double normalizer = 0.

    cdef np.ndarray[np.float64_t, ndim=1] importances
    importances = np.zeros((self.n_features,))
    cdef DOUBLE_t* importance_data = <DOUBLE_t*>importances.data

    with nogil:
        while node != end_node:
            if node.left_child != _TREE_LEAF:
                # ... and node.right_child != _TREE_LEAF:
                left = &nodes[node.left_child]
                right = &nodes[node.right_child]

                importance_data[node.feature] += (
                    node.weighted_n_node_samples * node.impurity -
                    left.weighted_n_node_samples * left.impurity -
                    right.weighted_n_node_samples * right.impurity)
            node += 1

    importances /= nodes[0].weighted_n_node_samples

    if normalize:
        normalizer = np.sum(importances)

        if normalizer > 0.0:
            # Avoid dividing by zero (e.g., when root is pure)
            importances /= normalizer

    return importances

尝试计算特征重要性:

print("sepal length (cm)",0)
print("sepal width (cm)",(3*0.444-(0+0)))
print("petal length (cm)",(54* 0.168 - (48*0.041+6*0.444)) +(46*0.043 -(0+3*0.444)) + (3*0.444-(0+0)))
print("petal width (cm)",(150* 0.667 - (0+100*0.5)) +(100*0.5-(54*0.168+46*0.043))+(6*0.444 -(0+3*0.444)) + (48*0.041-(0+0)))

我们得到了特征重要性:np.array([0,1.332,6.418,92.30])
经过归一化处理后,我们得到了array ([0., 0.01331334, 0.06414793, 0.92253873]),这与clf.feature_importances_相同。
请注意,所有类别的权重都应该为1。

3

对于那些寻找关于此主题的scikit-learn文档或@GillesLouppe答案的参考资料:

在RandomForestClassifier中,estimators_属性是DecisionTreeClassifier的列表(如文档中所述)。为了计算RandomForestClassifier的feature_importances_,在scikit-learn源代码中,它对集成中所有估计器(所有DecisionTreeClassifer)的feature_importances_属性进行平均。

在DecisionTreeClassifer的文档中,提到:“特征的重要性被计算为该特征带来的标准总减少量的(归一化)值。它也被称为基尼重要性[1]。”

这里提供了关于变量和Gini重要性的更多信息的直接链接,该链接由下面scikit-learn的参考提供。

[1] L. Breiman和A. Cutler,“随机森林”,http://www.stat.berkeley.edu/~breiman/RandomForests/cc_home.htm


-1

随机森林中的特征重要性

  • 随机森林使用许多树,因此方差降低了
  • 随机森林还允许更多的特征组合探索
  • 决策树给出变量重要性,如果减少不纯度(减少基尼不纯度),则变量重要性更高
  • 每个树都有不同的重要性顺序

    这是背后发生的事情!
  • 我们取一个属性并在所有树中检查它是否存在,并取该属性分裂上的同质性变化的平均值。同质性变化的平均值给出了该属性的特征重要性 enter image description here

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接