sklearn源码解读:1.10 Decision Trees & 1.11 Ensemble methods

本文深入探讨了sklearn库中决策树和集成学习的源码实现,包括Cython底层的_tree.pyx, _splitter.pyx, _criterion.pyx文件,以及基于Python的Tree和Ensemble架构。详细介绍了树的构建方式、分裂策略、分裂准则以及随机森林和梯度提升树的实现细节。文章强调理解源码对于掌握模型运作的重要性,并对比了GBDT、Xgboost和LightGBM的异同。" 113316294,10543410,PowerDesigner导出SQL脚本及常见问题解答,"['数据库设计', 'PowerDesigner', 'SQL脚本', '数据迁移', '数据库管理']
摘要由CSDN通过智能技术生成

本文讨论sklearn源码中的树模型,其中包括 Decision Trees 与 Ensemble methods 两篇,源码文件夹在 sklearn/tree 与 sklearn/ensemble 下。

本文涉及到的模型参数解释请先自行翻阅官方文档,如非必要本文将不再列出。本文将简要介绍全部模型源码(工具源码不介绍),建议在看本文时自己打开源码对照查看,本文不再附大片源码来灌水。涉及到Cython语法细节本文将不详细讲解,因为我也看不懂,用着自己仅有的C++和Python那点知识勉强的在Cython的海洋中狗刨。

 

基于Cython底层架构

首先树模型的底层代码均由Cython编写,核心文件是为 _tree.pyx _splitter.pyx_criterion.pyx _utils.pyx 是源代码所使用的的工具文件,就不细说了。

tree目录树

先大致看一下架构:

  • _tree.pyx 是树模型的主架构,负责树的level生成,由Cython类Tree封装。生成树的结点有两种方法:DepthFirstTreeBuilder类与BestFirstTreeBuilder类。它们均继承自TreeBuilder类,其实就是为了继承它的_check_input方法,检查输入类型,并转化为占用连续内存储存形式(目的是加速计算与索引)如np.asfortranarray和np.ascontiguousarray格式。

  • _splitter.pyx 是负责叶节点分裂的架构,有四种分裂方式,两种Sparse处理,两种Dense处理。Dense处理中分为BestSplitter和RandomSplitter两种分裂方式,其实很容易理解,一种是正常的挑最好的特征和特征点分裂,一种是像随机分裂树那样瞎分裂。他们均继承自BaseDenseSplitter,继承它的初始化方法,而BaseDense继承自Splitter类,这是基类,提供了对接分裂准则方法的方法等。

  • _criterion.pyx 是负责叶节点分裂准则的架构,有N多种准则计算方法,有分类ClassificationCriterion和连续RegressionCriterion两种计算准则。这两种类均继承自Criterion基类,继承他的对接Splitter接口。就拿默认的分类准则基尼指数举例子。Gini类继承自ClassificationCriterion,Gini类仅提供了自己的计算准则方法(算基尼指数)。

 

_tree.pyx

主要有两种构造树的方式,DepthFirstTreeBuilder和BestFirstTreeBuilder,当不限制叶子结点数的时候就会使用DepthFirstTreeBuilder,使用的方式递归先在左子树分裂;限制叶子结点数的时候就会BestFirstTreeBuilder,使用优先级队列的方式将得分最大叶子结点进行分裂;堆和栈都定义在util.pyx中,比较常规的定义,就不说了。

DepthFirstTreeBuilder&BestFirstTreeBuilder类

  • _check_input :检查输入类型并转化为占用连续内存储存形式;

  • build:建立树;

  • _add_split_node(BestFirstTreeBuilder):添加树节点

Tree类

  • _resize、_resize_c:设置内存缓冲区;

  • _add_node:添加树节点;

  • apply:返回叶子索引;

  • _apply_dense、_apply_sparse_csr:返回dense和sparse树叶子节点索引;

  • _get_value_ndarray:返回所有树节点各类别权重和;

  • _get_node_ndarray:返回节点;

  • predict:返回当前节点各类别权重和权重和;

  • decision_path:返回决策路径;

  • _decision_path_dense、_decision_path_sparse_csr:返回dense和sparse树决策路径;

  • compute_feature_importances:计算特征重要度,(当前节点的样本权重数*损失数-左右节点的样本权重数*损失数)/样本权重数,然后再归一化即可得,计算方法如下所示;

    cpdef compute_feature_importances(self, normalize=True):
        
        ...

        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

 

_splitter.pyx 

有两类四种分裂方式,BestSplitter是最优分裂,跟正常的分裂方式一样,RandomSplitter是随机分裂。

BestSplitter&RandomSplitter类

  • init :初始化,有效样本(样本权重>0)samples,所有样本权重和 weighted_n_samples,预排序特征指针X_idx_sorted_ptr,预排序特征内存间隔 X_idx_sorted_stride,有效样本标志位 sample_mask

  • node_reset: 初始化ClassificationCriterion类的样本区间,得到区间样本权重和 weighted_n_node_samples

  • node_impurity:计算当前节点得分数;

  • node_split:分裂节点,使用了Fisher-Yates随机方法选取特征列,记录常量特征剔除出运算;

  • node_value:保存得分,将类别权重和 sum_total 复制到树的 value 内存,当做树得分;

对于稀疏类BestSparseSplitter和RandomSparseSplitter,他们相比于Dense类来说仅仅多了一个排序过程,由于稀疏类的特性,对他们进行如Dense类的预排序性能是非常差的:Python内置sort函数是基于快速排序,对于稀疏问题快排时间复杂度直接飙升到O(n方),使用插入排序效率最高,但还是O(n)的复杂度,所以sklearn源码中选择了只sort排序非0特征,时间复杂度只有O(m),其中m<<n。

  • extract_nnz:提取非零特征;

  • extract_nnz_index_to_samples:通过样本索引值 提取非零特征;

  • extract_nnz_binary_search:通过二分法查找 提取非零特征;

  • sparse_swap:交换样本位置,使正特征值样本在零特征值样本之后;

  • _partition:分割样本;

  • binary_search:二分法查找当前样本行索引以确定对应特征是否为非零;

  • <
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值