决策树 ID3及C4.5算法原理及代码实现

决策树

基本介绍:

    决策树(decision tree)是一种常见的机器学习方法,表示基于特征对实例进行分类的过程,代表了对象属性与对象值之间的映射关系。模型具有可读性,分类速度快的优点。学习时利用训练数据,根据损失函数最小化的原则建立决策树模型,预测时根据所得的决策树进行分类。决策树是一种树型结构,每个内部节点表示一个属性上的测试,每个分支代表一个测试输出,而每个叶子代表一种种类。如图所示:

决策树 ID3及C4.5算法原理及代码实现_第1张图片

 

决策树:

    用决策树分类即从根节点开始,对实例的特征进行测试,根据测试结果将实例分配到子节点中。如根据人头发的长或短将其分到两个子节点中。即将决策树有非此即彼的性质,并且所规定的集合应该是完备的,即每个实例都只能被一条路径或者规则所覆盖,也就是说一个人不可能头发即长又短,同时我们的数据中头发的类别应该只有两种(长、短),若是出现了第三种可能,则应该相应增加类别。

    同时,决策树也表示了一定的概率分布,即最终每个叶节点上的分类往往代表着属于该类的概率较大。

    决策树实际上代表着一种分类的规则,如何选择分类的特征来构造树则是问题的关键。因此我们需要找到一种方法来从诸多特征中选择最优的特征,如果一个特征有大于二个类,可能还要寻找最优候选值。

 

特征选择:

    一个良好的特征要对于数据具有较好的分类能力,若利用某个特征进行分类与随机分类的结果并没有很大差异,那么这样的特征对我们的分类并没有什么作用。但是我们如何从这么多的特征中选择合适的特征?在这里利用熵引入了信息增益和信息增益比的概念。

熵、条件熵、信息增益:

    在信息论中熵表示一种对信息的度量,是一种消除不确定性所需信息量的度量,也可看做对信息量的期望。

    信息量:$$ I(x_{i})=log(\frac{1}{p(x_{i})}) $$

    $ P(x_{i}) $表示离散随机变量的概率分布,概率越大表示事件所含的信息量较小。例如中国队进了世界杯,这是一件小概率事件,因此它发生了会给你带来很多的信息。而熵则是平均信息量,即:$$ H(p)=-\sum p_{i}log(p_{i}) $$

    熵越大,随机变量的不确定性就越大。

    再引入条件熵的概念,即在X给定下随机变量Y的条件熵:$$ H(Y|X)=\sum p_{i}H(Y|X=x_{i}) $$

    至此我们终于可以引入信息增益的概念,即信息论中的互信息量: $$ g(D,A)=H(D)-H(D|A) $$

    信息增益代表了得知X后使得Y不确定性减少的程度。即可表示特征A可使数据集的分类不确定性减少的程度,我们可以认为信息增益越大的特征能够具有更好的分类效果。

 

ID3算法:

    ID3算法的核心思想即在决策树的各个节点上使用信息增益选择特征。从根节点开始,计算所有特征的信息增益,选择信息增益最大的特征作为节点特征,再根据该特征的取值建立子节点,对子节点递归调用上述过程。即:

输入:训练数据集D,特征A,阈值beta :

输出:决策树T

(1)若D中所有实例属于同一类Ck则T为单节点树,并将Ck作为该节点的类标记,返回T

(2)若A为空集,则通过统计D中实例数最大的类作为该节点类标记(即对于某一节点而言,没有其它特征可以进行分辨了,则对将该节点的类设置为分到该节点数据最多属于的类中),返回T

(3)否则计算A中各特征对D的信息增益,选择信息增益最大的特征 Ag :$$ H(D)=-\sum \frac{\left | C_{k} \right |}{\left | D \right |}log(\frac{\left | C_{k} \right |}{\left | D \right |}) $$

$$ H(D|A)=-\sum \frac{\left | D_{i} \right |}{\left | D \right |}H(D_{i}) $$ 

信息增益:$$ g\left ( D,A \right )=H\left ( D \right )-H\left ( D|A \right ) $$

(4)若Ag的信息增益小于阈值,则将T作为单节点树,将D中实例数最大的类作为该节点的标记

(5)否则,对Ag每一个可能值 ai 将D划分为若干非空子集,将子集中实例数最大的类作为标记,构建子节点,返回树T

(6)对第i个子节点,以(5)中的子集作为训练集,A-Ag

作为特征集,递归调用(1)~(5),构建决策树。

    上述就是ID3算法的核心步骤,即用信息增益来选择分类特征。但是以信息增益作为划分的标准会带来一个问题,观察公式我们可以看出,由于对于数据集而言经验熵是给定的,我们想要获得大的信息增益就要使条件熵较小,当我们的特征有较多取值时条件熵就会相应较小,因此我们的算法会倾向于选择取值较多的特征值。为了解决这个问题,就有了C4.5算法。

C4.5算法:

信息增益比:

    信息增益与训练数据集D关于特征A的值的熵之比:$$ g_{r}(D,A)=\frac{g(D,A)}{H_{A}(D)} $$

    其中:$$ H_{A}(D)=\sum \frac{\left | D_{i} \right |}{\left | D \right |}log(\frac{\left | D_{i} \right |}{\left | D \right |}) $$

    C4.5算法即是将ID3算法中选择特征的标准用信息增益比代替

 

ID3算法Python实现如下:

 

from math import log
import operator

def create_data():#构建数据和标签
    dataSet=[['short', 'long hair', 'thin', 'female'],
             ['high', 'short hair', 'thin', 'male'],
             ['short', 'long hair', 'fat', 'female'],
             ['high', 'long hair', 'thin', 'female'],
             ['short', 'short hair', 'fat', 'male'],
             ['short', 'short hair', 'thin', 'female'],
             ['high', 'short hair', 'fat', 'male'],
             ['high', 'long hair', 'fat', 'male'],
             ['short', 'short hair', 'thin', 'male'],
             ['high', 'short hair', 'thin', 'female'],
             ['short', 'long hair', 'fat', 'female']]
    labels= ['stature', 'hair', 'weight', 'gender']
    return dataSet, labels

def cal_entropy(dataSet):
    num=len(dataSet)
    label_count={}
    for fea in dataSet:
        current_label=fea[-1]#统计每条数据的类
        if current_label not in label_count.keys():
            label_count[current_label]=0
        label_count[current_label]+=1 #计算每个类中有多少数据
    entropy=0.0
    for i in label_count:#计算经验熵
        Pi=float(label_count[i])/num
        entropy-=Pi*log(Pi,2)
    return entropy

def remove_feature(dataSet,axis,feature):#去除某个特征
    retdataset=[]
    for featVec in dataSet:
        if featVec[axis]==feature:
            reducedata=featVec[:axis]#某个特征前数据
            reducedata.extend(featVec[axis+1:])#某个特征后数据
            #去掉了axis
            retdataset.append(reducedata)
    return retdataset

def choose_best_feature(dataSet):
    entropy=cal_entropy(dataSet)
    feature_num=len(dataSet[0])-1
    max_mutual_info=0
    best_feature=-1
    for i in range(feature_num):
        feature_list=[example[i] for example in dataSet]
        feature_class=set(feature_list)#得到该特征的所有可能取值
        conditional_entropy=0
        for value in feature_class:
            retdataset=remove_feature(dataSet, i, value)
            Pi=len(retdataset)/float(len(dataSet))
            conditional_entropy+=Pi*cal_entropy(retdataset)#求条件熵
        mutual_info=entropy-conditional_entropy#互信息量
        if (mutual_info>max_mutual_info):
            max_mutual_info=mutual_info
            best_feature=i
    return best_feature

def majority_vote(class_list):
    class_count={}
    for vote in class_list:
        if vote not in class_count.keys():
            class_count[vote]=0
        class_count[vote]+=1
    sort_class_count=sorted(class_count.items(),key=operator.itemgetter(1),reverse=True)
    #排序来决定该节点的类
    return sort_class_count[0][0]


def create_tree(dataSet,labels):
    class_list=[example[-1] for example in dataSet]
    if class_list.count(class_list[0])==len(class_list):
        return class_list[0]
    if len(dataSet[0])==1:
        #节点已没有特征可以继续分解
        return majority_vote(class_list)
    best_feature=choose_best_feature(dataSet)
    best_feature_label=labels[best_feature]
    my_tree={best_feature_label:{}}
    del(labels[best_feature])
    #删掉已选择的特征
    feature=[example[best_feature] for example in dataSet]
    feature_class=set(feature)
    for value in feature_class:
        sublabels=labels[:]
        my_tree[best_feature_label][value]=create_tree(remove_feature(dataSet,best_feature,value),sublabels)
        #迭代生成决策树
    return my_tree


if __name__=='__main__':
    dataSet,labels=create_data()
    print(create_tree(dataSet,labels))

 

 

 

参考书籍:李航《统计学习方法》

你可能感兴趣的:(机器学习)