相对变量重要性的提升


33

我正在寻找一种解释,说明如何在梯度增强树中计算相对变量重要性,而这并不是过于笼统/过于简单:

度量基于选择变量进行拆分的次数,每次拆分后对模型的平方改进加权的权重并在所有树上取平均值。[ Elith等。2008年,增强回归树的工作指南 ]

那还不如:

一世Ĵ2^Ť=Ť=1个Ĵ-1个一世Ť2^1个vŤ=Ĵ

其中求和在终端节点树的非终端节点上,是与节点相关的拆分变量,是平方误差的相应经验改进作为分割的结果,定义为,其中分别是左子代反应手段和右子代反应手段,而是权重的相应总和。J T v t t ^ i 2 t i 2R lR r= w l w rŤĴŤvŤŤ一世Ť2^一世2[R[R[R=ww[Rw+w[Rÿ¯-ÿ[R¯2ÿ¯ÿ[R¯ww[R[ Friedman 2001,贪婪函数近似:梯度提升机 ]

最后,我没有发现统计学学习的内容(Hastie等人,2008年)对这里的学习非常有帮助,因为相关部分(第10.13.1页,第367页)的味道与上述第二篇参考文献非常相似(可以对此进行解释)因为弗里德曼是这本书的合著者)。

PS:我知道gbm R包中的summary.gbm给出了相对变量重要性度量。我试图探索源代码,但似乎找不到实际的计算位置。

布朗尼要点:我想知道如何在R中获得这些


我刚刚为链接的问题添加了一个新的答案,该问题关于如何按类绘制变量重要性,这可能会有所帮助stackoverflow.com/a/51952918/3277050
参阅

Answers:


55

我将使用sklearn代码,因为它通常比R代码干净得多。

这是GradientBoostingClassifier的feature_importances属性的实现(我删除了一些妨碍概念性内容的代码行)

def feature_importances_(self):
    total_sum = np.zeros((self.n_features, ), dtype=np.float64)
    for stage in self.estimators_:
        stage_sum = sum(tree.feature_importances_
                        for tree in stage) / len(stage)
        total_sum += stage_sum

    importances = total_sum / len(self.estimators_)
    return importances

这很容易理解。 self.estimators_是在booster中包含各个树的数组,因此for循环在各个树上进行迭代。有一个与

stage_sum = sum(tree.feature_importances_
                for tree in stage) / len(stage)

这正在处理非二进制响应的情况。在这里,我们在每个阶段以一对多的方式拟合多棵树。从概念上讲,最简单的方法是集中在二进制情况下,即总和为一个被加数,而这恰好是tree.feature_importances_。因此,在二进制情况下,我们可以将所有内容重写为

def feature_importances_(self):
    total_sum = np.zeros((self.n_features, ), dtype=np.float64)
    for tree in self.estimators_:
        total_sum += tree.feature_importances_ 
    importances = total_sum / len(self.estimators_)
    return importances

因此,换句话说,总结出各个树木的特征重要性,然后除以树木总数。剩下的要看如何计算单个树的特征重要性。

树的重要性计算是在cython级别实现的,但是仍然可以遵循。这是代码的清理版本

cpdef compute_feature_importances(self, normalize=True):
    """Computes the importance of each feature (aka variable)."""

    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

    return importances

这很简单。遍历树的节点。只要您不在叶节点上,就可以根据该节点处的分割计算节点纯度的加权减少量,并将其归因于分割的特征

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)

然后,完成后,将其全部除以数据的总权重(在大多数情况下,是观察值的数量)

importances /= nodes[0].weighted_n_node_samples

值得回顾的是,杂质是度量标准的通用名称,该度量标准用于确定生长树木时进行的分割。有鉴于此,我们只是简单地总结了每个特征的分割量,使我们能够减少树中所有分割处的杂质。

在梯度增强的情况下,这些树始终是适合损失函数梯度的回归树(贪婪地最小化平方误差)。


非常感谢您提供这个非常详细的答案。在我接受之前,请让我花一些时间仔细检查它。
Antoine

4
尽管似乎可以使用各种杂质标准,但基尼指数并不是弗里德曼使用的标准。如我的问题和第三个链接的第878行所述,弗里德曼(Friedman)使用均方误差杂质标准和改善得分。如果您可以更新答案的这一部分,那就太好了。是的,您是对的,似乎权重确实是观测值的数量。
Antoine

3
还是保留有关基尼系数和弗里德曼原始标准的所有部分,使您的答案更好,并强调第一个用于分类,第二个用于回归?
Antoine

Antoine,感谢您的更新。知道均方误差是用于回归树的改进标准,对我们很有帮助。目前尚不清楚如何将其用于分类。但是,即使在梯度分类中,我也认为与分类树相反,仍在使用回归树。至少在python中,正在对每个提升阶段的当前错误进行回归分析。
相当Nerdy

你们对回归树是正确的。
马修·德鲁里
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.