CART(决策分类树)原理和实现

前面我们了解了决策树和adaboost的决策树墩的原理和实现,在adaboost我们看到,用简单的决策树墩的效果也很不错,但是对于更多特征的样本来说,可能需要很多数量的决策树墩

或许我们可以考虑使用更加高级的弱分类器,下面我们看下CART(Classification And Regression Tree)的原理和实现吧

CART也是决策树的一种,不过是满二叉树,CART可以是强分类器,就跟决策树一样,但是我们可以指定CART的深度,使之成为比较弱的分类器

CART生成的过程和决策树类似,也是采用递归划分的,不过也存在很多的不同之处

数据集:第一列为样本名称,最后一列为类别,中间为特征

human    constant    hair    true    false    false    false    true    false    mammal
python    cold_blood    scale    false    true    false    false    false    true    reptile
salmon    cold_blood    scale    false    true    false    true    false    false    fish
whale    constant    hair    true    false    false    true    false    false    mammal
frog    cold_blood    none    false    true    false    sometime    true    true    amphibious
lizard    cold_blood    scale    false    true    false    false    true    false    reptile
bat    constant    hair    true    false    true    false    true    false    mammal
cat    constant    skin    true    false    false    false    true    false    mammal
shark    cold_blood    scale    true    false    false    true    false    false    fish
turtle    cold_blood    scale    false    true    false    sometime    true    false    reptile
pig    constant    bristle    true    false    false    false    true    true    mammal
eel    cold_blood    scale    false    true    false    true    false    false    fish
salamander    cold_blood    none    false    true    false    sometime    true    true    amphibious

特征名称如下

["temperature","cover","viviparity","egg","fly","water","leg","hibernate"]

1:数据集划分评分

CART使用gini系数来衡量数据集的划分效果而不是香农熵(借用下面的一张图)

def calGini(dataSet):
    numEntries = len(dataSet)
    labelCounts={}
    for featVec in dataSet:
        currentLabel = featVec[-1]
        if currentLabel not in labelCounts.keys(): labelCounts[currentLabel] = 0
        labelCounts[currentLabel] += 1
    gini=1
    for label in labelCounts.keys():
        prop=float(labelCounts[label])/numEntries
        gini -=prop*prop
    return gini

2:数据集划分

决策树是遍历每一个特征的特征值,每个特征值得到一个划分,然后计算每个特征的信息增益从而找到最优的特征;

CART每一个分支都是二分的,当特征值大于两个的时候,需要考虑特征值的组合得到两个“超级特征值”作为CART的分支;当然我们也可以偷懒,每次只取多个特征值的一个,挑出最优的一个和剩下的分别作为一个分支,但无疑这得到的cart不是最优的

# 传入的是一个特征值的列表,返回特征值二分的结果
def featuresplit(features):
    count = len(features)#特征值的个数
    if count < 2:
        print "please check sample‘s features,only one feature value"
        return -1
    # 由于需要返回二分结果,所以每个分支至少需要一个特征值,所以要从所有的特征组合中选取1个以上的组合
    # itertools的combinations 函数可以返回一个列表选多少个元素的组合结果,例如combinations(list,2)返回的列表元素选2个的组合
    # 我们需要选择1-(count-1)的组合
    featureIndex = range(count)
    featureIndex.pop(0)
    combinationsList = []
    resList=[]
    # 遍历所有的组合
    for i in featureIndex:
        temp_combination = list(combinations(features, len(features[0:i])))
        combinationsList.extend(temp_combination)
        combiLen = len(combinationsList)
    # 每次组合的顺序都是一致的,并且也是对称的,所以我们取首尾组合集合
    # zip函数提供了两个列表对应位置组合的功能
    resList = zip(combinationsList[0:combiLen/2], combinationsList[combiLen-1:combiLen/2-1:-1])
    return resList

得到特征的划分结果之后,我们使用二分后的特征值划分数据集

def splitDataSet(dataSet, axis, values):
    retDataSet = []
    for featVec in dataSet:
        for value in values:
            if featVec[axis] == value:
                reducedFeatVec = featVec[:axis]     #剔除样本集
                reducedFeatVec.extend(featVec[axis+1:])
                retDataSet.append(reducedFeatVec)
    return retDataSet    

遍历每个特征的每个二分特征值,得到最好的特征以及二分特征值

# 返回最好的特征以及二分特征值
def chooseBestFeatureToSplit(dataSet):
    numFeatures = len(dataSet[0]) - 1      #
    bestGiniGain = 1.0; bestFeature = -1;bestBinarySplit=()
    for i in range(numFeatures):        #遍历特征
        featList = [example[i] for example in dataSet]#得到特征列
        uniqueVals = list(set(featList))       #从特征列获取该特征的特征值的set集合
        # 三个特征值的二分结果:
        # [((‘young‘,), (‘old‘, ‘middle‘)), ((‘old‘,), (‘young‘, ‘middle‘)), ((‘middle‘,), (‘young‘, ‘old‘))]
        for split in featuresplit(uniqueVals):
            GiniGain = 0.0
            if len(split)==1:
                continue
            (left,right)=split
            # 对于每一个可能的二分结果计算gini增益
            # 左增益
            left_subDataSet = splitDataSet(dataSet, i, left)
            left_prob = len(left_subDataSet)/float(len(dataSet))
            GiniGain += left_prob * calGini(left_subDataSet)
            # 右增益
            right_subDataSet = splitDataSet(dataSet, i, right)
            right_prob = len(right_subDataSet)/float(len(dataSet))
            GiniGain += right_prob * calGini(right_subDataSet)
            if (GiniGain <= bestGiniGain):       #比较是否是最好的结果
                bestGiniGain = GiniGain         #记录最好的结果和最好的特征
                bestFeature = i
                bestBinarySplit=(left,right)
    return bestFeature,bestBinarySplit    

所有特征用完时多数表决程序

def majorityCnt(classList):
    classCount={}
    for vote in classList:
        if vote not in classCount.keys(): classCount[vote] = 0
        classCount[vote] += 1
    sortedClassCount = sorted(classCount.iteritems(), key=operator.itemgetter(1), reverse=True)
    return sortedClassCount[0][0]

现在来生成cart吧

def createTree(dataSet,labels):
    classList = [example[-1] for example in dataSet]
    # print dataSet
    if classList.count(classList[0]) == len(classList):
        return classList[0]#所有的类别都一样,就不用再划分了
    if len(dataSet) == 1: #如果没有继续可以划分的特征,就多数表决决定分支的类别
        # print "here"
        return majorityCnt(classList)
    bestFeat,bestBinarySplit = chooseBestFeatureToSplit(dataSet)
    # print bestFeat,bestBinarySplit,labels
    bestFeatLabel = labels[bestFeat]
    if bestFeat==-1:
        return majorityCnt(classList)
    myTree = {bestFeatLabel:{}}
    featValues = [example[bestFeat] for example in dataSet]
    uniqueVals = list(set(featValues))
    for value in bestBinarySplit:
        subLabels = labels[:]       # #拷贝防止其他地方修改
        if len(value)<2:
            del(subLabels[bestFeat])
        myTree[bestFeatLabel][value] = createTree(splitDataSet(dataSet, bestFeat, value),subLabels)
    return myTree 

看下效果,左边是cart,右边是决策树,(根节点用cover和temperature是一样的,为了对比决策树,此时我选了cover),第三个图是temperature作为根节点的cart

上面的代码是不考虑特征继续使用的,也就是每个特征只使用一次;但是我们发现有些有些分支里面特征值个数多余两个的,其实我们应该让这些特征继续参与下一次的划分

可以发现,temperature作为根节点的cart没有变化,而cover作为根节点的cart深度变浅了,并且cover特征出现了两次(或者说效果变好了)

下面是有变化的代码

特征值多余两个的分支保留特征值

def splitDataSet(dataSet, axis, values):
    retDataSet = []
    if len(values) < 2:
        for featVec in dataSet:
            if featVec[axis] == values[0]:#如果特征值只有一个,不抽取当选特征
                reducedFeatVec = featVec[:axis]
                reducedFeatVec.extend(featVec[axis+1:])
                retDataSet.append(reducedFeatVec)
    else:
        for featVec in dataSet:
            for value in values:
                if featVec[axis] == value:#如果特征值多于一个,选取当前特征
                    retDataSet.append(featVec)

    return retDataSet

createTree函数for循环判断是否需要移除当前最优特征

    for value in bestBinarySplit:
        if len(value)<2:
            del(labels[bestFeat])
        subLabels = labels[:]       #拷贝防止其他地方修改
        myTree[bestFeatLabel][value] = createTree(splitDataSet(dataSet, bestFeat, value),subLabels)

这样我们就生成了一个cart,但是这个数据集没有出现明显的过拟合的情景,我们换一下数据集看看

sunny    hot    high    FALSE    no
sunny    hot    high    TRUE    no
overcast    hot    high    FALSE    yes
rainy    mild    high    FALSE    yes
rainy    cool    normal    FALSE    yes
rainy    cool    normal    TRUE    no
overcast    cool    normal    TRUE    yes
sunny    mild    high    FALSE    no
sunny    cool    normal    FALSE    yes
rainy    mild    normal    FALSE    yes
sunny    mild    normal    TRUE    yes
overcast    mild    high    TRUE    yes
overcast    hot    normal    FALSE    yes
rainy    mild    high    TRUE    no

特征名称:"Outlook" , "Temperature" , "Humidity" , "Wind"

生成的cart比价合理,这是因为数据比较合理,我们添加一条脏数据看看cart会变成怎么样(右图),可以看到cart为了拟合我新加的这条脏数据,

树深度增加1,叶子节点增加3,不过另一方面也是因为样本数少的原因,一个噪声样本就产生了如此大的印象

overcast    mild    normal    FALSE    no

本来想接着写剪枝的,但是手上没什么好的样本,可能剪枝的效果不明显 ,等以后有适合的样本在另起一篇的,要不单讲理论没什么意思。

时间: 2024-10-20 00:08:25

CART(决策分类树)原理和实现的相关文章

用cart(分类回归树)作为弱分类器实现adaboost

在之前的决策树到集成学习里我们说了决策树和集成学习的基本概念(用了adaboost昨晚集成学习的例子),其后我们分别学习了决策树分类原理和adaboost原理和实现, 上两篇我们学习了cart(决策分类树),决策分类树也是决策树的一种,也是很强大的分类器,但是cart的深度太深,我们可以指定cart的深度使得cart变成强一点的弱分类器. 在决策树到集成学习我们提到,单棵复杂的决策树可以达到100%,而简单的集成学习只能有85%的正确率,下面我们尝试用强一点的弱分类器来看下集成学习的效果有没有提

C#无限极分类树-创建-排序-读取 用Asp.Net Core+EF实现之方法二:加入缓存机制

在上一篇文章中我用递归方法实现了管理菜单,在上一节我也提到要考虑用缓存,也算是学习一下.Net Core的缓存机制. 关于.Net Core的缓存,官方有三种实现: 1.In Memory Caching 我理解是在内容中实现,这种方法适用于单服务器的生产环境. 2.a Distributed Cache 分部式缓存实现. 3.Response Cache 这种方式我理解为客户端缓存. 今天我只用了第一种实现方法,内存中缓存,之所以用这种方法我是觉得我这里用缓存的初衷是为了减少访问数据库的次数,

ArcGIS中的Geostatistical Analyst 插值方法分类树

您必须要做的最重要决定之一就是确定开发插值模型的目标.换句话说,您需要模型提供什么信息才能做出决定?例如在公共卫生区域中,插值模型用于预测在统计意义上可与发病率产生关联的污染物等级.根据该信息可进一步设计采样研究和制定公共卫生政策等. Geostatistical Analyst 提供多种不同的插值方法.每种方法都有独一无二的特征并提供不同的信息(某些情况下,各种方法提供相似的信息;另外一些情况下,信息可能有很大的差异).下图显示了根据不同条件分类的方法.选择一个对您的特定情况比较重要的条件,然

Sklearn分类树在合成数集上的表现

小伙伴们大家好~o( ̄▽ ̄)ブ,今天我们开始来看一下Sklearn分类树的表现,我的开发环境是Jupyter lab,所用的库和版本大家参考: Python 3.7.1(你的版本至少要3.4以上) Scikit-learn 0.20.0 (你的版本至少要0.20) Graphviz 0.8.4 (没有画不出决策树哦,安装代码conda install python-graphviz) Numpy 1.15.3, Pandas 0.23.4, Matplotlib 3.0.1, SciPy 1.1

AVL树,红黑树,B-B+树,Trie树原理和应用

前言:本文章来源于我在知乎上回答的一个问题 AVL树,红黑树,B树,B+树,Trie树都分别应用在哪些现实场景中? 看完后您可能会了解到这些数据结构大致的原理及为什么用在这些场景,文章并不涉及具体操作(如插入删除等等) 目录 AVL树 AVL树原理与应用 红黑树 红黑树原理与应用 B/B+树 B/B+树原理与应用 Trie树 Trie树原理与应用 AVL树 简介: AVL树是最早的自平衡二叉树,在早期应用还相对来说比较广,后期由于旋转次数过多而被红黑树等结构取代(二者都是用来搜索的),AVL树内

C#无限极分类树-创建-排序-读取 用Asp.Net Core+EF实现

今天做一个管理后台菜单,想着要用无限极分类,记得园子里还是什么地方见过这种写法,可今天找了半天也没找到,没办法静下心来自己写了: 首先创建节点类(我给它取名:AdminUserTree): 1 /// <summary> 2 /// 无限极节点类 3 /// </summary> 4 public class AdminUserTree 5 { 6 /// <summary> 7 /// 节点信息 8 /// </summary> 9 public int

无限分类树操作

获取相应分类id的分类树: public static function getCategoryTree($id){ //$model=M('category'); if($id>0){ $obj=self::selectTable('category',array('id'=>$id),true);//$model->where(array('id'=>$id))->find(); if(!is_null($obj)){ $childList=self::selectTab

ROC检验分类树性能

001 ############################################################# 002 ############# 读取excel文件 ###################### 003 ############################################################# 004 #root<-"C:/Users/liming/Desktop/写书/chap7/cbc/" 005 root

分类树测试用例设计工具:CTE XL

1.1工具安装 CTE XL是一款免费的分类树测试用例设计工具,安装过程很简单,下载好安装程序后,运行安装程序:如图点击下一步:点击下一步:是否创建桌面按钮:安装:  安装完成后,需要注册才能获取免费的License,打开CTE XL,会弹出一个注册的对话框,输入好Firstname,Famliyname,Organization,Email后系统将会发送一封带License的邮件到填写的email地址上,获取后,复制到license key中就完成注册了 1.2 CTE XL的使用 在了解CT