《机器学习实战》基于信息论的三种决策树算法(ID3,C4.5,CART)

=====================================================================python

  《机器学习实战》系列博客是博主阅读《机器学习实战》这本书的笔记也包含一些其余python实现的机器学习算法

    github 源码同步:https://github.com/Thinkgamer/Machine-Learning-With-Pythongit

    算法实现均采用python              如需转载请注明出处,谢谢github

=====================================================================

基于ID3的决策树算法的分析与实现,请参考:《机器学习实战》决策树(ID3算法)的分析与实现

一、决策树简介
二、基于信息论的三种决策树算法
三、三种决策树算法的Python实现


     决策树简介 

     1:决策树原理
       决策树是经过一系列规则对数据进行分类的过程,他提供一种在什么条件下会获得什么值的相似规则方法,决策树分为分类树和回归树,分类树对离散变量最决策树,回归树对连续变量作决策树

若是不考虑效率等,那么样本全部特征的判断级联起来终会将某一个样本分到一个类终止块上。实际上,样本全部特征中有一些特征在分类时起到决定性做用,决策树的构造过程就是找到这些具备决定性做用的特征,根据其决定性程度来构造一个倒立的树–决定性做用最大的那个特征做为根节点,而后递归找到各分支下子数据集中次大的决定性特征,直至子数据集中全部数据都属于同一类。因此,构造决策树的过程本质上就是根据数据特征将数据集分类的递归过程,咱们须要解决的第一个问题就是,当前数据集上哪一个特征在划分数据分类时起决定性做用。算法

为了找到决定性的特征、划分出最好的结果,咱们必须评估数据集中蕴含的每一个特征,寻找分类数据集的最好特征。完成评估以后,原始数据集就被划分为几个数据子集。这些数据子集会分布在第一个决策点的全部分支上。若是某个分支下的数据属于同一类型,则则该分支处理完成,称为一个叶子节点,即肯定了分类。若是数据子集内的数据不属于同一类型,则须要重复划分数据子集的过程。如何划分数据子集的算法和划分原始数据集的方法相同,直到全部具备相同类型的数据均在一个数据子集内(叶子节点)。
app

      2:决策树的构造过程

         通常包含三个部分
         一、特征选择:特征选择是指从训练数据中众多的特征中选择一个特征做为当前节点的分裂标准,如何选择特征有着不少不一样量化评估标准标准,从而衍生出不一样的决策树算法。
          二、决策树生成 : 根据选择的特征评估标准,从上至下递归地生成子节点,直到数据集不可分则中止决策树中止生长。 树结构来讲,递归结构是最容易理解的方式。
         三、 剪枝 :决策树容易过拟合,通常来须要剪枝,缩小树结构规模、缓解过拟合。剪枝技术有预剪枝和后剪枝两种。
        核心伪代码以下:
           检测数据集中的每一个子项是否属于同一类:
           If so return 类标签
           else
                寻找划分数据集的最好特征
                划分数据集
                建立分支节点
                    for 每一个划分的子集
                         调用createBranch函数并增长返回结果到分支节点中
              return 分支节点

     3:决策树的优缺点

     决策树适用于数值型和标称型(离散型数据,变量的结果只在有限目标集中取值),可以读取数据集合,提取一些列数据中蕴含的规则。在分类问题中使用决策树模型有不少的优势,决策树计算复杂度不高、便于使用、并且高效,决策树可处理具备不相关特征的数据、可很容易地构造出易于理解的规则,而规则一般易于解释和理解。决策树模型也有一些缺点,好比处理缺失数据时的困难、过分拟合以及忽略数据集中属性之间的相关性等。

     4:基于信息论的三种决策树算法简介

划分数据集的最大原则是:使无序的数据变的有序。若是一个训练数据中有20个特征,那么选取哪一个作划分依据?这就必须采用量化的方法来判断,量化划分方法有多重,其中一项就是“信息论度量信息分类”。基于信息论的决策树算法有ID三、CART和C4.5等算法,其中C4.5和CART两种算法从ID3算法中衍生而来。机器学习

CART和C4.5支持数据特征为连续分布时的处理,主要经过使用二元切分来处理连续型变量,即求一个特定的值-分裂值:特征值大于分裂值就走左子树,或者就走右子树。这个分裂值的选取的原则是使得划分后的子树中的“混乱程度”下降,具体到C4.5和CART算法则有不一样的定义方式。函数

ID3算法由Ross Quinlan发明,创建在“奥卡姆剃刀”的基础上:越是小型的决策树越优于大的决策树(be simple简单理论)。ID3算法中根据信息论的信息增益评估和选择特征,每次选择信息增益最大的特征作判断模块。ID3算法可用于划分标称型数据集,没有剪枝的过程,为了去除过分数据匹配的问题,可经过裁剪合并相邻的没法产生大量信息增益的叶子节点(例如设置信息增益阀值)。使用信息增益的话实际上是有一个缺点,那就是它偏向于具备大量值的属性–就是说在训练集中,某个属性所取的不一样值的个数越多,那么越有可能拿它来做为分裂属性,而这样作有时候是没有意义的,另外ID3不能处理连续分布的数据特征,因而就有了C4.5算法。CART算法也支持连续分布的数据特征。学习

C4.5是ID3的一个改进算法,继承了ID3算法的优势。C4.5算法用信息增益率来选择属性,克服了用信息增益选择属性时偏向选择取值多的属性的不足在树构造过程当中进行剪枝;可以完成对连续属性的离散化处理;可以对不完整数据进行处理。C4.5算法产生的分类规则易于理解、准确率较高;但效率低,因树构造过程当中,须要对数据集进行屡次的顺序扫描和排序。也是由于必须屡次数据集扫描,C4.5只适合于可以驻留于内存的数据集。测试

CART算法的全称是Classification And Regression Tree,采用的是Gini指数(选Gini指数最小的特征s)做为分裂标准,同时它也是包含后剪枝操做。ID3算法和C4.5算法虽然在对训练样本集的学习中能够尽量多地挖掘信息,但其生成的决策树分支较大,规模较大。为了简化决策树的规模,提升生成决策树的效率,就出现了根据GINI系数来选择测试属性的决策树算法CART。
优化

基于信息论的三种决策树算法

一、ID3算法

     (1)信息熵
在几率论中,信息熵给了咱们一种度量不肯定性的方式,是用来衡量随机变量不肯定性的,熵就是信息的指望值。若待分类的事物可能划分在N类中,分别是x1,x2,……,xn,每一种取
到的几率分别是P1,P2,……,Pn,那么X的熵就定义为:
                                                                                 
                                                                            从定义中可知:0≤H(X)≤log(n)
      当随机变量只取两个值时,即X的分布为P(X=1)=p,X(X=0)=1−p,0≤p≤1则熵为:H(X)=−plog2(p)−(1−p)log2(1−p)。
      熵值越高,则数据混合的种类越高,其蕴含的含义是一个变量可能的变化越多(反而跟变量具体的取值没有任何关系,只和值的种类多少以及发生几率有关),它携带的信息量就越大。熵在信息论中是一个很是重要的概念,不少机器学习的算法都会利用到这个概念。

(2)条件熵,假设有随机变量(X,Y),其联合几率分布为:P(X=xi,Y=yi)=pij,i=1,2,⋯,n;j=1,2,⋯,m条件熵(H(Y∣X))表示在已知随机变量X的条件下随机变量Y的不肯定性,其定义为X在给定条件下Y的条件几率分布的熵对X的数学指望:
                                                        

     (3)信息增益,信息增益(information gain)表示得知特征X的信息后,而使得Y的不肯定性减小的程度。定义为:
                                                        

二、ID3算法推导

(1)分类系统信息熵

假设一个分类系统的样本空间(D,Y),D表示样本(有m个特征),Y表示n个类别,可能的取值是C1,C2,...,Cn。每个类别出现的几率是P(C1),P(C2),...,P(Cn)。该分类系统的熵为:
                                                            
离散分布中,类别Ci出现的几率P(Ci),经过该类别出现的次数除去样本总数便可获得。对于连续分布,常须要分块作离散化处理得到。

(2)条件熵

根据条件熵的定义,分类系统中的条件熵指的是当样本的某一特征X固定时的信息熵。因为该特征X可能的取值会有(x1,x2,……,xn),当计算条件熵而须要把它固定的时候,每一种可能都要固定一下,而后求统计指望。

所以样本特征X取值为xi的几率是Pi,该特征被固定为值xi时的条件信息熵就是H(C|X=xi),那么

H(C|X)就是分类系统中特征X被固定时的条件熵(X=(x1,x2,……,xn)):

                                

如果样本的该特征只有两个值(x1 = 0,x2=1)对应(出现,不出现),如文本分类中某一个单词的出现与否。那么对于特征二值的状况,咱们用T表明特征,用t表明T出现,表示该特征出现。那么:

                                  
与前面条件熵的公式对比一下,P(t)就是T出现的几率, 就是T不出现的几率。结合信息熵的计算公式,可得:
                                        
                                        
特征T出现的几率P(t),只要用出现过T的样本数除以总样本数就能够了;P(C i |t)表示出现T的时候,类别C i 出现的几率,只要用出现了T而且属于类别C i 的样本数除以出现了T的样本数就获得了。

(3)信息增益

根据信息增益的公式,分类系统中特征X的信息增益就是:Gain(D, X) = H(C)-H(C|X)

信息增益是针对一个一个的特征而言的,就是看一个特征X,系统有它和没它的时候信息量各是多少,二者的差值就是这个特征给系统带来的信息增益。每次选取特征的过程都是经过计算每一个特征值划分数据集后的信息增益,而后选取信息增益最高的特征。

对于特征取值为二值的状况,特征T给系统带来的信息增益就能够写成系统本来的熵与固定特征T后的条件熵之差:
                                

(4)通过上述一轮信息增益计算后会获得一个特征做为决策树的根节点,该特征有几个取值,根节点就会有几个分支,每个分支都会产生一个新的数据子集Dk,余下的递归过程就是对每一个Dk再重复上述过程,直至子数据集都属于同一类。

在决策树构造过程当中可能会出现这种状况:全部特征都做为分裂特征用光了,但子集还不是纯净集(集合内的元素不属于同一类别)。在这种状况下,因为没有更多信息可使用了,通常对这些子集进行“多数表决”,即便用此子集中出现次数最多的类别做为此节点类别,而后将此节点做为叶子节点。
(5)结合实例进行说明:

上面的训练集有4个属性,即属性集合A={OUTLOOK, TEMPERATURE, HUMIDITY, WINDY};而类标签有2个,即类标签集合C={Yes, No},分别表示适合户外运动和不适合户外运动,实际上是一个二分类问题。
咱们已经计算过信息增益,这里直接列出来,以下所示:
数据集D包含14个训练样本,其中属于类别“Yes”的有9个,属于类别“No”的有5个,则计算其信息熵:

1 Info(D) = -9/14 * log2(9/14) - 5/14 * log2(5/14) = 0.940

下面对属性集中每一个属性分别计算信息熵,以下所示:

1 Info(OUTLOOK) = 5/14 * [- 2/5 * log2(2/5) – 3/5 * log2(3/5)] + 4/14 * [ - 4/4 * log2(4/4) - 0/4 * log2(0/4)] + 5/14 * [ - 3/5 * log2(3/5) – 2/5 * log2(2/5)] = 0.694
2 Info(TEMPERATURE) = 4/14 * [- 2/4 * log2(2/4) – 2/4 * log2(2/4)] + 6/14 * [ - 4/6 * log2(4/6) - 2/6 * log2(2/6)] + 4/14 * [ - 3/4 * log2(3/4) – 1/4 * log2(1/4)] = 0.911
3 Info(HUMIDITY) = 7/14 * [- 3/7 * log2(3/7) – 4/7 * log2(4/7)] + 7/14 * [ - 6/7 * log2(6/7) - 1/7 * log2(1/7)] = 0.789
4 Info(WINDY) = 6/14 * [- 3/6 * log2(3/6) – 3/6 * log2(3/6)] + 8/14 * [ - 6/8 * log2(6/8) - 2/8 * log2(2/8)] = 0.892

根据上面的数据,咱们能够计算选择第一个根结点所依赖的信息增益值,计算以下所示:

1 Gain(OUTLOOK) = Info(D) - Info(OUTLOOK) = 0.940 - 0.694 = 0.246
2 Gain(TEMPERATURE) = Info(D) - Info(TEMPERATURE) = 0.940 - 0.911 = 0.029
3 Gain(HUMIDITY) = Info(D) - Info(HUMIDITY) = 0.940 - 0.789 = 0.151
4 Gain(WINDY) = Info(D) - Info(WINDY) = 0.940 - 0.892 = 0.048
能够看出第一次决策应该以OUTLOOK属性为参考,而后根据OUTLOOK属性将数据集分为三个子集,把三个子集和剩余的属性代入递归的计算,继而求得第二次的分裂属性,一次类推便可

三、C4.5算法

(1)信息增益比选择最佳特征

以信息增益进行分类决策时,存在偏向于取值较多的特征的问题。因而为了解决这个问题人们有开发了基于信息增益比的分类决策方法,也就是C4.5。C4.5与ID3都是利用贪心算法进行求解,不一样的是分类决策的依据不一样。

所以,C4.5算法在结构与递归上与ID3彻底相同,区别就在于选取决断特征时选择信息增益比最大的。

信息增益比率度量是用ID3算法中的的增益度量Gain(D,X)和分裂信息度量SplitInformation(D,X)来共同定义的。分裂信息度量SplitInformation(D,X)就至关于特征X(取值为x1,x2,……,xn,各自的几率为P1,P2,...,Pn,Pk就是样本空间中特征X取值为xk的数量除上该样本空间总数)的熵。

SplitInformation(D,X) = -P1 log2(P1)-P2 log2(P)-,...,-Pn log2(Pn)

GainRatio(D,X) = Gain(D,X)/SplitInformation(D,X)

在ID3中用信息增益选择属性时偏向于选择分枝比较多的属性值,即取值多的属性,在C4.5中因为除以SplitInformation(D,X)=H(X),能够削弱这种做用。

(2)处理连续数值型特征

C4.5既能够处理离散型属性,也能够处理连续性属性。在选择某节点上的分枝属性时,对于离散型描述属性,C4.5的处理方法与ID3相同。对于连续分布的特征,其处理方法是:

先把连续属性转换为离散属性再进行处理。虽然本质上属性的取值是连续的,但对于有限的采样数据它是离散的,若是有N条样本,那么咱们有N-1种离散化的方法:<=vj的分到左子树,>vj的分到右子树。计算这N-1种状况下最大的信息增益率。另外,对于连续属性先进行排序(升序),只有在决策属性(即分类发生了变化)发生改变的地方才须要切开,这能够显著减小运算量。经证实,在决定连续特征的分界点时采用增益这个指标(由于若采用增益率,splittedinfo影响分裂点信息度量准确性,若某分界点刚好将连续特征分红数目相等的两部分时其抑制做用最大),而选择属性的时候才使用增益率这个指标能选择出最佳分类特征。

在C4.5中,对连续属性的处理以下:

1.      对特征的取值进行升序排序

2.      两个特征取值之间的中点做为可能的分裂点,将数据集分红两部分,计算每一个可能的分裂点的信息增益(InforGain)。优化算法就是只计算分类属性发生改变的那些特征取值。

3.      选择修正后信息增益(InforGain)最大的分裂点做为该特征的最佳分裂点

4.      计算最佳分裂点的信息增益率(Gain Ratio)做为特征的Gain Ratio。注意,此处需对最佳分裂点的信息增益进行修正:减去log2(N-1)/|D|(N是连续特征的取值个数,D是训练数据数目,此修正的缘由在于:当离散属性和连续属性并存时,C4.5算法倾向于选择连续特征作最佳树分裂点)

(3)叶子裁剪

分析分类回归树的递归建树过程,不难发现它实质上存在着一个数据过分拟合问题。在决策树构造时,因为训练数据中的噪音或孤立点,许多分枝反映的是训练数据中的异常,使用这样的断定树对类别未知的数据进行分类,分类的准确性不高。所以试图检测和减去这样的分支,检测和减去这些分支的过程被称为树剪枝。树剪枝方法用于处理过度适应数据问题。一般,这种方法使用统计度量,减去最不可靠的分支,这将致使较快的分类,提升树独立于训练数据正确分类的能力。

决策树经常使用的剪枝经常使用的简直方法有两种:预剪枝(Pre-Pruning)和后剪枝(Post-Pruning)。预剪枝是根据一些原则及早的中止树增加,如树的深度达到用户所要的深度、节点中样本个数少于用户指定个数、不纯度指标降低的最大幅度小于用户指定的幅度等。预剪枝的核心问题是如何事先指定树的最大深度,若是设置的最大深度不恰当,那么将会致使过于限制树的生长,使决策树的表达式规则趋于通常,不能更好地对新数据集进行分类和预测。除了事先限定决策树的最大深度以外,还有另一个方法来实现预剪枝操做,那就是采用检验技术对当前结点对应的样本集合进行检验,若是该样本集合的样本数量已小于事先指定的最小容许值,那么中止该结点的继续生长,并将该结点变为叶子结点,不然能够继续扩展该结点。

后剪枝则是经过在彻底生长的树上剪去分枝实现的,经过删除节点的分支来剪去树节点,可使用的后剪枝方法有多种,好比:代价复杂性剪枝、最小偏差剪枝、悲观偏差剪枝等等。后剪枝操做是一个边修剪边检验的过程,通常规则标准是:在决策树的不断剪枝操做过程当中,将原样本集合或新数据集合做为测试数据,检验决策树对测试数据的预测精度,并计算出相应的错误率,若是剪掉某个子树后的决策树对测试数据的预测精度或其余测度不下降,那么剪掉该子树。

关于后剪枝的具体理论能够参考“数据挖掘十大经典算法--CART: 分类与回归树”剪枝部分。
(4)依旧是结合上边的图形进行解释说明

上面的训练集有4个属性,即属性集合A={OUTLOOK, TEMPERATURE, HUMIDITY, WINDY};而类标签有2个,即类标签集合C={Yes, No},分别表示适合户外运动和不适合户外运动,实际上是一个二分类问题。
咱们已经计算过信息增益,这里直接列出来,以下所示:
数据集D包含14个训练样本,其中属于类别“Yes”的有9个,属于类别“No”的有5个,则计算其信息熵:

1 Info(D) = -9/14 * log2(9/14) - 5/14 * log2(5/14) = 0.940

下面对属性集中每一个属性分别计算信息熵,以下所示:

1 Info(OUTLOOK) = 5/14 * [- 2/5 * log2(2/5) – 3/5 * log2(3/5)] + 4/14 * [ - 4/4 * log2(4/4) - 0/4 * log2(0/4)] + 5/14 * [ - 3/5 * log2(3/5) – 2/5 * log2(2/5)] = 0.694
2 Info(TEMPERATURE) = 4/14 * [- 2/4 * log2(2/4) – 2/4 * log2(2/4)] + 6/14 * [ - 4/6 * log2(4/6) - 2/6 * log2(2/6)] + 4/14 * [ - 3/4 * log2(3/4) – 1/4 * log2(1/4)] = 0.911
3 Info(HUMIDITY) = 7/14 * [- 3/7 * log2(3/7) – 4/7 * log2(4/7)] + 7/14 * [ - 6/7 * log2(6/7) - 1/7 * log2(1/7)] = 0.789
4 Info(WINDY) = 6/14 * [- 3/6 * log2(3/6) – 3/6 * log2(3/6)] + 8/14 * [ - 6/8 * log2(6/8) - 2/8 * log2(2/8)] = 0.892

根据上面的数据,咱们能够计算选择第一个根结点所依赖的信息增益值,计算以下所示:

1 Gain(OUTLOOK) = Info(D) - Info(OUTLOOK) = 0.940 - 0.694 = 0.246
2 Gain(TEMPERATURE) = Info(D) - Info(TEMPERATURE) = 0.940 - 0.911 = 0.029
3 Gain(HUMIDITY) = Info(D) - Info(HUMIDITY) = 0.940 - 0.789 = 0.151
4 Gain(WINDY) = Info(D) - Info(WINDY) = 0.940 - 0.892 = 0.048

接下来,咱们计算分裂信息度量H(V):

    - OUTLOOK属性

属性OUTLOOK有3个取值,其中Sunny有5个样本、Rainy有5个样本、Overcast有4个样本,则

1 H(OUTLOOK) = - 5/14 * log2(5/14) - 5/14 * log2(5/14) - 4/14 * log2(4/14) = 1.577406282852345
    - TEMPERATURE属性

属性TEMPERATURE有3个取值,其中Hot有4个样本、Mild有6个样本、Cool有4个样本,则

1 H(TEMPERATURE) = - 4/14 * log2(4/14) - 6/14 * log2(6/14) - 4/14 * log2(4/14) = 1.5566567074628228
    - HUMIDITY属性

属性HUMIDITY有2个取值,其中Normal有7个样本、High有7个样本,则

1 H(HUMIDITY) = - 7/14 * log2(7/14) - 7/14 * log2(7/14) = 1.0
    - WINDY属性

属性WINDY有2个取值,其中True有6个样本、False有8个样本,则

1 H(WINDY) = - 6/14 * log2(6/14) - 8/14 * log2(8/14) = 0.9852281360342516

根据上面计算结果,咱们能够计算信息增益率,以下所示:

1 IGR(OUTLOOK) = Info(OUTLOOK) / H(OUTLOOK) = 0.246/1.577406282852345 = 0.15595221261270145
2 IGR(TEMPERATURE) = Info(TEMPERATURE) / H(TEMPERATURE) = 0.029 / 1.5566567074628228 = 0.018629669509642094
3 IGR(HUMIDITY) = Info(HUMIDITY) / H(HUMIDITY) = 0.151/1.0 = 0.151
4 IGR(WINDY) = Info(WINDY) / H(WINDY) = 0.048/0.9852281360342516 = 0.048719680492692784

根据计算获得的信息增益率进行选择属性集中的属性做为决策树结点,对该结点进行分裂。

4:CART算法

(1)简述

       建立分类树递归过程当中,CART每次都选择当前数据集中具备最小Gini信息增益的特征做为结点划分决策树。ID3算法和C4.5算法虽然在对训练样本集的学习中能够尽量多地挖掘信息,但其生成的决策树分支、规模较大,CART算法的二分法能够简化决策树的规模,提升生成决策树的效率。下面是CART算法与C4.5相比的主要差异

        1:CART只能为二叉树,而C4.5能够为多叉树
        2:CART中的输入变量和输出变量能够是分类型也能够是数值型,而C4.5的输出变量只能是分类型

        3:CART 使用GINI系数做为变量的不纯度量,而C4.5采用信息增益率(ID3使用信息增益做为属性选择标准

        4:若是目标变量是标称的,而且具备两个以上的类别,则CART可能考虑将目标类别合并成两个超类别

        5:若是目标变量是连续的,则CART算法找出一组基于树的回归方法来预测目标变量

        6:对于缺失值得处理方法不一样,CART采用代理测试来估计测试的输出值,而C4.5直接将其分配到该分支中几率最大的分类

        7:对决策树的剪枝方法不一样,CART采用代价复杂度模型,经过交叉验证来估计对预测样本集的误分类损失,产生最小交叉验证误分类估计树。而C4.5启发式的调整在训练集样本上估计出的偏差率,使用调整的偏差率,以找出评分函数最大化的树


(2) CART的信息论基础和算法过程

        CART与C4.5的不一样之处是节点分裂创建在GINI指数这个概念上,GINI指数主要是度量数据划分或训练数据集D的不纯度为主。GINI值越小,代表样本的纯净度越高(即该样本只属于同一类的几率越高)。衡量出数据集某个特征全部取值的Gini指数后,就能够获得该特征的Gini Split info,也就是GiniGain。不考虑剪枝状况下,分类决策树递归建立过程当中就是每次选择GiniGain最小的节点作分叉点,直至子数据集都属于同一类或者全部特征用光了。

由于CART二分的特性,当训练数据具备两个以上的类别,CART需考虑将目标类别合并成两个超类别,这个过程称为双化。

        一、Gini指数的概念:

GINI指数是一种不等性度量,一般用来度量收入不平衡,能够用来度量任何不均匀分布,是介于0~1之间的数,0-彻底相等,1-彻底不相等。分类度量时,整体内包含的类别越杂乱,GINI指数就越大(跟熵的概念很类似)。

对于一个数据集T,其Gini计算方式为:

                                                           

 (n表示类别数,pj表数据集样本不一样类别的几率)

       二、GiniGain

           衡量出某个特征全部取值的Gini指数就能够获得Gini Split Info:

                                                      

           i表示特征的第i个取值

           ID3算法中的信息增益类似,这个能够称为是Gini信息增益--Gini Gain。对于CART,i=(1,2),获得在Binary Split状况下的Gini信息增益:                                                     

       三、属性选择

            (1)分类树的属性选择

                对于CART分类树的属性选择,针对属性类型分为分类型和数值型,方法有所不一样

                    a、对于分类型属性,因为CART只能创建二叉树,对于取多个值的属性变量,须要将多类别合并成两个类别,造成“超类”,而后计算两“超类”下样本测试输出取值的差别性

                   b、对于数值型属性,处理方法以下

                  (1) 对特征的取值进行升序排序

                  (2) 两个特征取值之间的中点做为可能的分裂点,将数据集分红两部分,计算每一个可能的分裂点的GiniGain。优化算法就是只计算分类属性发生改变的那些特征取值

                  (3)选择GiniGain最小的分裂点做为该特征的最佳分裂点(注意,若修正则此处需对最佳分裂点的Gini Gain减去log2(N-1)/|D|(N是连续特征的取值个数,D是训练数据数目)

                实现连续特征数据集划分的Python程序为(采用Numpy matrix,连续特征取值就能够省略排序这一步了):

<span style="font-size:18px;">def binSplitDataSet(dataSet, feature, value):   
    mat0 = dataSet[nonzero(dataSet[:,feature] > value)[0],:][0]   
    mat1 = dataSet[nonzero(dataSet[:,feature] <= value)[0],:][0]   
    return mat0,mat1  </span>

                理想的分组是将两组样本测试输出的取值的差别性总和达到最小,即纯度最大,也就是使两组输出变量取值的差别性降低最快“纯度”增长最快

           (2)回归树的属性选择

                回归树肯定当前最佳分组变量的策略与分类树相同,主要不一样在于度量节点测试输出值差别性的指标有所不一样,因为回归树的测试输出为数值型,所以方差是最理想的指标其定义为:

                                   


        4,对离散分布、且取值数目>=3的特征的处理:

             正是由于CART树是二叉树,因此对于样本的有N>=3个取值的离散特征的处理时也只能有两个分支,这就要经过组合人为的建立二取值序列并取GiniGain最小者做为树分叉决策点。如某特征值具备['young','middle','old']三个取值,那么二分序列会有以下3种可能性(空集和满集在CART分类中没有意义):

                 [(('young',), ('middle', 'old')), (('middle',), ('young', 'old')), (('old',), ('young', 'middle'))]

            采用CART算法,就须要分别计算按照上述List中的二分序列作分叉时的Gini指数,而后选取产生最小的GINIGain的二分序列作该特征的分叉二值序列参与树构建的递归。若是某特征取值有4个,那么二分序列组合就有7种,5个取值就有15种组合,建立多值离散特征二分序列组合可采用Python的itertools包,程序以下:

<span style="font-size:18px;">from itertools import *   
import pdb   
def featuresplit(features):   
    count = len(features)   
    featureind = range(count)   
    featureind.pop(0) #get value 1~(count-1)  
    combiList = []   
    for i in featureind:   
        com = list(combinations(features, len(features[0:i])))   
        combiList.extend(com)   
    combiLen = len(combiList)   
    featuresplitGroup = zip(combiList[0:combiLen/2], combiList[combiLen-1:combiLen/2-1:-1])   
    return featuresplitGroup   
if __name__ == '__main__':   
    test= range(3)   
    splitGroup = featuresplit(test)   
    print 'splitGroup', len(splitGroup), splitGroup   
    test= range(4)   
    splitGroup = featuresplit(test)   
    print 'splitGroup', len(splitGroup),splitGroup   
    test= range(5)   
    splitGroup = featuresplit(test)   
    print 'splitGroup', len(splitGroup),splitGroup   
    test= ['young','middle','old']   
    splitGroup = featuresplit(test)   
    print 'splitGroup', len(splitGroup),splitGroup</span>

        所以CART不适用于离散特征有多个取值可能的场景。此时,若定要使用CART,则最好预先人为的将离散特征的取值缩减。

        那么对于二分后的左右分支,若是特征取值tuple中元素多于2个,该特征是否还要继续参与当前子数据集的二分呢?

        我认为须要,所以该特征继续参与分类决策树递归,直至左右分支上该特征的取值都是惟一的(即再也不包含该特征)。那么离散特征的datasplit函数就应该:若是按照当前分支特征分叉后,分支上特征取值tuple>=2,则分支子数据集保留该特征,该tuple继续参与上的树构建的递归;不然分支子数据集删除该特征。

 
def splitDataSet(dataSet, axis, valueTuple):   
    '''return dataset satisfy condition dataSet[i][axis] == valueTuple, 
    and remove dataSet[i][axis] if len(valueTuple)==1'''  
    retDataSet = []   
    length = len(valueTuple)   
    if length ==1:   
      for featVec in dataSet:   
        if featVec[axis] == valueTuple[0]:   
            reducedFeatVec = featVec[:axis]     #chop out axis used for splitting  
            reducedFeatVec.extend(featVec[axis+1:])   
            retDataSet.append(reducedFeatVec)   
    else:   
      for featVec in dataSet:   
        if featVec[axis] in valueTuple:   
            retDataSet.append(featVec)   
    return retDataSet

        5,CART的剪枝

       分析分类回归树的递归建树过程,不难发现它实质上存在着一个数据过分拟合问题。在决策树构造时,因为训练数据中的噪音或孤立点,许多分枝反映的是训练数据中的异常,使用这样的断定树对类别未知的数据进行分类,分类的准确性不高。所以试图检测和减去这样的分支,检测和减去这些分支的过程被称为树剪枝。树剪枝方法用于处理过度适应数据问题。一般,这种方法使用统计度量,减去最不可靠的分支,这将致使较快的分类,提升树独立于训练数据正确分类的能力。决策树经常使用的剪枝经常使用的简直方法有两种:预剪枝(Pre-Pruning)和后剪枝(Post-Pruning)。预剪枝是根据一些原则及早的中止树增加,如树的深度达到用户所要的深度、节点中样本个数少于用户指定个数、不纯度指标降低的最大幅度小于用户指定的幅度等;后剪枝则是经过在彻底生长的树上剪去分枝实现的,经过删除节点的分支来剪去树节点,可使用的后剪枝方法有多种,好比:代价复杂性剪枝、最小偏差剪枝、悲观偏差剪枝等等。

CART常采用过后剪枝方法,构建决策树过程当中的第二个关键就是用独立的验证数据集对训练集生长的树进行剪枝。

关于后剪枝的具体理论能够参考“数据挖掘十大经典算法--CART: 分类与回归树”剪枝部分。


三种决策树算法的Python实现

       1:ID3算法

#coding=utf-8
'''
'''
from math import log
import operator

def createDataSet():
    dataSet =[[1,1,'yes'],
              [1,1,'yes'],
              [1,0,'no'],
              [0,1,'no'],
              [0,1,'no']]
    labels = ['no surfacing','flippers'] #分类的属性
    return dataSet,labels

#计算给定数据的香农熵
def calcShannonEnt(dataSet):
    numEntries = len(dataSet)
    labelCounts = {}
    for featVec in dataSet:
        currentLabel = featVec[-1] #得到标签
        #构造存放标签的字典
        if currentLabel not in labelCounts.keys():
            labelCounts[currentLabel]=0
        labelCounts[currentLabel]+=1 #对应的标签数目+1
    #计算香农熵
    shannonEnt = 0.0
    for key in labelCounts:
        prob = float(labelCounts[key])/numEntries
        shannonEnt -=prob*log(prob,2)
    return shannonEnt

#划分数据集,三个参数为带划分的数据集,划分数据集的特征,特征的返回值
def splitDataSet(dataSet,axis,value):  
    retDataSet = []
    for featVec in dataSet:
        if featVec[axis] ==value:
            #将相同数据集特征的抽取出来
            reducedFeatVec = featVec[:axis]
            reducedFeatVec.extend(featVec[axis+1:])
            retDataSet.append(reducedFeatVec)
    return retDataSet #返回一个列表
        
#选择最好的数据集划分方式
def chooseBestFeatureToSplit(dataSet):
    numFeature = len(dataSet[0])-1
    baseEntropy = calcShannonEnt(dataSet)
    bestInfoGain = 0.0
    beatFeature = -1
    for i in range(numFeature):
        featureList = [example[i] for example in dataSet] #获取第i个特征全部的可能取值
        uniqueVals = set(featureList)  #从列表中建立集合,获得不重复的全部可能取值ֵ
        newEntropy = 0.0
        for value in uniqueVals:
            subDataSet = splitDataSet(dataSet,i,value)   #以i为数据集特征,value为返回值,划分数据集
            prob = len(subDataSet)/float(len(dataSet))   #数据集特征为i的所占的比例
            newEntropy +=prob * calcShannonEnt(subDataSet)   #计算每种数据集的信息熵
        infoGain = baseEntropy- newEntropy
        #计算最好的信息增益,增益越大说明所占决策权越大
        if (infoGain > bestInfoGain):
            bestInfoGain = infoGain
            bestFeature = i
    return bestFeature

#递归构建决策树
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)#排序,True升序
    return sortedClassCount[0][0]  #返回出现次数最多的

 #建立树的函数代码
def createTree(dataSet,labels):     
    classList = [example[-1]  for example in dataSet]
    if classList.count(classList[0])==len(classList):#类别彻底相同则中止划分
        return classList[0]
    if len(dataSet[0]) ==1:             #遍历完全部特征值时返回出现次数最多的
        return majorityCnt(classList)
    bestFeat = chooseBestFeatureToSplit(dataSet)   #选择最好的数据集划分方式
    bestFeatLabel = labels[bestFeat]   #获得对应的标签值
    myTree = {bestFeatLabel:{}}
    del(labels[bestFeat])      #清空labels[bestFeat],在下一次使用时清零
    featValues = [example[bestFeat] for example in dataSet] 
    uniqueVals = set(featValues)
    for value in uniqueVals:
        subLabels =labels[:]
        #递归调用建立决策树函数
        myTree[bestFeatLabel][value]=createTree(splitDataSet(dataSet,bestFeat,value),subLabels)
    return myTree  

if __name__=="__main__":
    dataSet,labels = createDataSet()
    print createTree(dataSet,labels)

     2:C4.5算法

待续..................

    3:CART算法
待续...............


吐槽一下:博主为了整理这篇博客也是整了好几天,有总结不到位或者错的地方请你们指正,还有C4.5和CART算法的Python代码会在后续更新,哪位网友若是有现成的代码的话也能够留言让博主参考一下,谢谢

=====================================================================

《机器学习实战》系列博客是博主阅读《机器学习实战》这本书的笔记也包含一些其余python实现的机器学习算法
                                          算法实现均采用python

github 源码同步:https://github.com/Thinkgamer/Machine-Learning-With-Python

=====================================================================


                                          算法实现均采用python