Boosting 的相对变量重要性

机器算法验证 机器学习 数据挖掘 预测模型 大车 助推
2022-01-21 14:05:52

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

这些度量基于选择用于拆分的变量的次数,通过每次拆分对模型的平方改进进行加权,并对所有树进行平均[伊利斯等人。2008,增强回归树的工作指南]

这不如以下抽象:

Ij2^(T)=t=1J1it2^1(vt=j)

总和在非终端节点上tJ-终端节点树T,vt是与节点关联的分裂变量t, 和it2^是由于拆分导致的平方误差的相应经验改进,定义为i2(Rl,Rr)=wlwrwl+wr(yl¯yr¯)2, 在哪里yl¯,yr¯分别是左右子响应均值,wl,wr是相应的权重之和。[弗里德曼 2001,贪心函数逼近:梯度提升机]

最后,我没有发现Elements of Statistical Learning (Hastie et al. 2008)在这里阅读很有帮助,因为相关部分(10.13.1 第 367 页)的味道与上面的第二个参考非常相似(可能会被解释弗里德曼是这本书的合著者)。

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

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

1个回答

我将使用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_是一个包含助推器中的各个树的数组,因此 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

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

在梯度提升的背景下,这些树总是回归树(贪婪地最小化平方误差),适合损失函数的梯度。