统计学习方法c++实现之四 决策树

决策树

前言

决策树是一种基本的分类和回归算法,书中主要是讨论了分类的决策树。决策树在每一个结点分支规则是一种if-then规则,即满足某种条件就继续搜索左子树,不符合就去右子树,看起来是用二叉树实现对吧,实际的CART决策树就是二叉树,等会再介绍。现在先来看看决策树的理论部分。代码地址https://github.com/bBobxx/statistical-learning/blob/master/src/decisiontree.cpp

决策树相关理论

决策树的学习通常包括三个部分:特征选择决策树生成决策树修剪

特征选择

我们抛开烦人的公式和术语,用通俗的思想(没办法,本人只有通俗的思想)来理解一下,现在给你很多数据,有很多类,每个数据有n维的特征,怎么分?最简单的,不如来个全连接神经网络,把数据丢进去,让模型自己去学习去,恩.....这个办法可能是准确率最高的,但是我们这里学习的是决策树,而且有些场景根本不需要神经网络也可以分类的很准确,现在让我们用决策树解决这个问题。

首先,面对n维特征,和k个类别,仿佛无从下手。咋办呢,笨一点的办法,就从第一个特征开始,如果第一个特征有m个不同取值,那我就按这个特征取值把数据分成m份,对这份特征子集,我再选第二个特征,第二个特征比如有l个不同取值,那么对于m个子集,每个又可以最多分出l个子集(最多而不是一定,因为m某个子集中的数据的第二维特征可能取不全l个值),那么现在我们最多有\(m\times l\) 个子集,然后是第三维特征......直到第n维特征或者某个子集中的数据类别几乎一样我们就停止。对于这种分法很明显确实是个树结构对吧,只不过你的树可能是这样子的:

不好意思,弄错了,一般树结构是这样子的:

思路很简单,但是过程很复杂对吧,没错,这就是决策树,但是如果真写成上面这样也太没效率了,比如说,现在给你很多人的数据,让你分出是男是女,特征有这么几个:身高,体重,头发长短,身份证上的性别。没错最后一个特征一般不会给出的。现在开始按照上面的思路分类,就分10000个数据吧,身高的取值有十种,就当做150到190取十个数吧,体重先不谈,如果从身高这个特征开始分就能把你分吐血。聪明的同学(应该是不笨的)一眼就能看出来,我直接用最后一个特征,一下子就分出来了,就算没有最后这个特征,我用头发长短这个也可以很好的分。

没错,看出特征选择的重要了吧,这就是决策树的第一步,要先选择最具有分类能力的特征,注意每一维特征只用一次。怎么选呢,这就涉及到了信息增益(ID3决策树), 信息增益比(C4.5决策树),和基尼指数(CART决策树)。皮一下,这里就只介绍基尼指数吧,其他的就看书吧。

基尼指数:\(Gini(D,A)=\frac{|D_1|}{|D|}Gini(D_1)+\frac{|D_2|}{|D|}Gini(D_2)\)

其中,A代表某一维特征,D代表的数据集合,根据A是否取a将D分为\(D_1\) 和\(D_2\)两个子集,\(|D|,|D_1|,|D_2|\)分别代表各自的数量。

其中,\(Gini(D) = \sum_{k=1}^{K}\frac{|C_k|}{|D|}(1-\frac{|C_k|}{|D|})\)

\(C_k\)代表某一类,\(\frac{|C_k|}{|D|}\)代表这个集合中样本是第k类的概率。

基尼指数越大,表示样本集合的不确定性越大,我们在选取A的时候肯定希望分完后集合越确定越好,所以以后在进行特征选择的时候就需要选取基尼指数最小的那个特征。

决策树(CART)生成算法

  1. 对于当前根节点Root,对现有的样本集D,对所有的特征\(A_i\)的所有可能取值\(a_j\)计算基尼指数,选择使基尼指数最小的\(A_i\)和\(a_j\),根据样本点对\(A_i=a_j\)的测试为“是”或“否”将D分为\(D_1\)和\(D_2\)。
  2. \(D_1\)作为根节点Root的左子树的根节点Root_L的样本集,\(D_2\)作为根节点Root的右子树的根节点Root_R的样本集。
  3. 重复1,2直到结点中样本个数小于阈值,或样本集基本属于同一类,或者没有更多特征(代表已经将所有的特征都过一遍了)。

CART剪枝

请自行看书,反正我也没实现。

决策树的c++实现

代码结构

实现

这里只展示如何确定分割的特征和值

pair<int, double> DecisionTree::createSplitFeature(vector<vector<double >>& valRange){
    priority_queue<pair<double, pair<int, double>>, vector<pair<double, pair<int, double>>>, std::greater<pair<double, pair<int, double>>>> minheap;
      //pair<double, pair<int, double>> first value is Gini value, second pair (pair<int, double>) first value is split
      //axis, second value is split value
    vector<map<double, int>> dataDivByFeature(indim);  //vector size is num of axis, map‘s key is the value of feature, map‘s value is
      //num belong to feature‘value
    vector<set<double>> featureVal(indim);  //store value for each axis
    vector<map<pair<double, double>, int>> datDivByFC(indim);  //vector size is num of axis, map‘s key is the feature value and class value, map‘s value is
      //num belong to that feature value and class
    set<double> cls;  //store num of class
    for(const auto& featureId:features) {
        if (featureId<0)
            continue;
        map<double, int> dataDivByF;
        map<pair<double, double>, int> dtDivFC;
        set<double> fVal;
        for (auto& data:valRange){  //below data[featureId] is the value of one feature axis, data.back() is class value
            cls.insert(data.back());
            fVal.insert(data[featureId]);
            if (dataDivByF.count(data[featureId]))
                dataDivByF[data[featureId]] += 1;
            else
                dataDivByF[data[featureId]] = 0;
            if (dtDivFC.count(std::make_pair(data[featureId], data.back())))
                dtDivFC[std::make_pair(data[featureId], data.back())] += 1;
            else
                dtDivFC[std::make_pair(data[featureId], data.back())] = 0;
        }
        featureVal[featureId] = fVal;
        dataDivByFeature[featureId] = dataDivByF;
        datDivByFC[featureId] = dtDivFC;
    }
    for (auto& featureId: features) {  // for each feature axis
        if (featureId<0)
            continue;
        for (auto& feVal: featureVal[featureId]){  //for each feature value
            double gini1 = 0 ;
            double gini2 = 0 ;

            double prob1 = dataDivByFeature[featureId][feVal]/double(valRange.size());
            double prob2 = 1 - prob1;
            for (auto& c : cls){  //for each class
                double pro1 = double(datDivByFC[featureId][std::make_pair(feVal, c)])/dataDivByFeature[featureId][feVal];
                gini1 += pro1*(1-pro1);
                int numC = 0;
                for (auto& feVal2: featureVal[featureId])
                    numC += datDivByFC[featureId][std::make_pair(feVal2, c)];
                double pro2 = double(numC-datDivByFC[featureId][std::make_pair(feVal, c)])/(valRange.size()-dataDivByFeature[featureId][feVal]);
                gini2 += pro2*(1-pro2);
            }
            double gini = prob1*gini1+prob2*gini2;

            minheap.push(std::make_pair(gini, std::make_pair(featureId, feVal)));
        }
    }
    features[minheap.top().second.first]=-1;
    return minheap.top().second;
}

这里使用循环嵌套计算符合条件的数据的数量,效率很低,有更好方法的同学麻烦告知一下,叩拜~

原文地址:https://www.cnblogs.com/bobxxxl/p/10259049.html

时间: 2024-10-08 08:24:02

统计学习方法c++实现之四 决策树的相关文章

统计学习方法系列

统计学习方法(一)——统计学习方法概论 统计学习方法(二)——感知机 统计学习方法(三)——K近邻法 统计学习方法(四)——朴素贝叶斯法 统计学习方法(五)——决策树

决策树(统计学习方法(李航))的贷款的例子的实现

以统计学习方法(李航)这本书的例子为基础 需要注意的地方: 我用的是pycharm python版本是3.7 graphviz是一个软件,在pycharm里面下了还得去官网下 下完之后得加入环境变量可能还需要重启电脑 缺啥库就安啥库 那个数据是我自己设置的,手敲的. 贷款申请样本数据表 ID 年龄 有工作 有自己的房子 信贷情况 类别 1 青年 否 否 一般 否 2 青年 否 否 好 否 3 青年 是 否 好 是 4 青年 是 是 一般 是 5 青年 否 否 一般 否 6 中年 否 否 一般 否

统计学习方法笔记(1)——统计学习方法概论

1.统计学习 统计学习是关于计算机基于数据构建概率统计模型并运用模型对数据进行预测与分析的一门学科,也称统计机器学习.统计学习是数据驱动的学科.统计学习是一门概率论.统计学.信息论.计算理论.最优化理论及计算机科学等多个领域的交叉学科. 统计学习的对象是数据,它从数据出发,提取数据的特征,抽象出数据的模型,发现数据中的知识,又回到对数据的分析与预测中去.统计学习关于数据的基本假设是同类数据具有一定的统计规律性,这是统计学习的前提. 统计学习的目的就是考虑学习什么样的模型和如何学习模型. 统计学习

统计学习方法概论

统计学习 统计学习是关于计算机基于数据构建概率统计模型并运用模型对数据进行预测与分析的一门学科.统计学习也称为统计机器学习(statical machine learning). 统计学习的方法是基于数据构建统计模型从而对数据进行预测和分析.统计学习由监督学习.非监督学习.半监督学习和强化学习等组成. 统计学习方法包括假设空间.模型选择的准则.模型学习的算法,这些统称为统计学习方法的三要素:模型(Model).策略(Strategy).算法(Algorithm). 实现统计学习方法的步骤如下:

[读]统计学习方法

这两天看<统计学习方法>,记录了一些基本的知识点. 1.统计学习的方法 从给定的.有限的.用于学习的训练数据集合出发,假设数据时独立同分布产生:并且假设要学习的模型术语某个函数的集合,称为假设空间:应用某个评价准则,从假设空间中选取一个最优的模型,使他对已知训练数据及未知测试数据在给定的评价准则下有最优的预测:最幽默型的选取由算法实现.这样,统计学习方法包括模型的假设空间.模型选择的准则以及模型学习的算法,称其为统计学习方法的三要素,简称为模型.策略和算法. 实现统计学习的步骤如下: (1)得

统计学习方法:CART算法

作者:桂. 时间:2017-05-13  14:19:14 链接:http://www.cnblogs.com/xingshansi/p/6847334.html . 前言 内容主要是CART算法的学习笔记. CART算法是一个二叉树问题,即总是有两种选择,而不像之前的ID3以及C4.5B可能有多种选择.CART算法主要有回归树和分类树,二者常用的准则略有差别:回归树是拟合问题,更关心拟合效果的好坏,此处用的是均方误差准则; 分类树是分类问题,更像是离散变量的概率估计,用与熵类似的Gini系数进

统计学习方法 李航---第12章 统计学习方法总结

第12章 统计学习方法总结 1 适用问题 分类问题是从实例的特征向量到类标记的预测问题:标注问题是从观测序列到标记序列(或状态序列)的预测问题.可以认为分类问题是标注问题的特殊情况. 分类问题中可能的预测结果是二类或多类:而标注问题中可能的预测结果是所有的标记序列,其数目是指数级的. 感知机.k近邻法.朴素贝叶斯法.决策树是简单的分类方法,具有模型直观.方法简单.实现容易等特点: 逻辑斯谛回归与最大熵模型.支持向量机.提升方法是更复杂但更有效的分类方法,往往分类准确率更高: 隐马尔可夫模型.条件

《统计学习方法》笔记

书籍ISBN:978-7-302-27595-4 第3章 k近邻法 P37 3.1节 k近邻算法 k近邻算法简单.直观:给定一个训练数据集,对新的输入实例,在训练数据集中找到与该实例最邻近的k个实例,这k个实例的多数属于某个类,就把该输入实例分为这个类.算法描述如下: 输入:训练数据集 其中xi是n维实数空间上的实例特征向量.yi∈{c1, c2,..., ck}为实例的类别,i = 1, 2,..., N:新输入的实例特征向量x. 输出:实例x所属的类y. (1)根据给定的距离度量,在训练集T

统计学习方法 李航---第8章 提升方法

第8章提升方法 提升(boosting)方法是一种常用的统计学习方法,应用广泛且有效.在分类问题中,它通过改变训练样本的权重,学习多个分类器,并将这些分类器进行线性组合,提高分类的性能. 基本思想:对于分类问题而言,给定一个训练样本集,求比较粗糙的分类规则(弱分类器)要比求精确的分类规则(强分类器)容易得多.提升方法就是从弱学习算法出发,反复学习,得到一系列弱分类器(又称为基本分类器),然后组合这些弱分类器,构成一个强分类器.大多数的提升方法都是改变训练数据的概率分布(训练数据的权值分布),针对