机器学习—决策树(ID3算法)及其python实现

一、决策树(ID3算法)及其python实现

总结一下前一阵对于决策树的学习。为了从算法的层次理解并熟练运用机器学习的多种算法,我首先从用Python编写算法入手:

(一)决策树的概念

决策树属于机器学习中的监督学习,也就是说,其数据集需要人工对feature与label标定,比如表1,天气的各种属性与天气好坏的关系表:

项目Outlook(Feature1)Temperature(Feature2)Humidity(Feature3)Windy(Feature4)Good_State?(Label)
1sunnyhothighFALSEno
2sunnyhothighTRUEno
3sunnyhothighTRUEno
4overcasthothighFALSEyes
5rainymildhighFALSEyes
6rainycoolnormalFALSEyes
7rainycoolnormalTRUEno
8overcastcoolnormalTRUEyes
9sunnymildhighFALSEno
10sunnycoolnormalFALSEyes
11rainymildnormalFALSEyes
12sunnymildnormalTRUEyes
13overcastmildhighTRUEyes
14overcasthotnormalFALSEyes
15rainymildhighTRUEno

我们需要决策树做的就是找到一种分类器根据天气的四个feature判断出其所属State的标签。
下面介绍决策树:它从一组无次序、无规则的实例中推理出以决策树表示的分类规则[1]。采用自顶向下的递归方式,在决策树的内部节点(feature)进行属性的比较(比如根据feature2比较天气的温度高低),并根据不同属性值判断从该节点向下的分支,在决策树的叶节点得到结论。
如下图1,我们可以根据此决策树来表示决策逻辑。
这里写图片描述

(二)生成决策树的算法
  • ID3 (Iterative Dichotomiser 3) (基于信息论)
  • C4.5(对ID3算法的改进,增加了对决策树的修剪等)
  • 随机森林(Random Forest)
  • 分类及回归树(Classification And Regression Tree, CART) (基于最小GNIN指数)
  • 多元自适应回归样条(MARS)
  • 梯度推进机(Gradient Boosting Machine, GBM)
(三)ID3算法介绍
1、过程
 (1)出发点:哪一个属性将在树的根节点被测试?--> 分类能力最好的属性
 (2)对于这个属性的每个可能值产生一个分支,然后将训练样例分配到样例属性值所对应的分支之下
 (3)对每个节点不断测试对其而言分类能力最好的属性
 (4)重复4过程直到所有属性被测试
2、如何选择分类能力最好的属性?(ID3算法中利用信息论中的熵与信息增益)
(1)熵
   高中物理我们学过热力学第二定律:在孤立系统中,体系与环境没有能量交换,体系总是自发地像混乱度增大的方向变化,总使整个系统的熵值增大。熵就是表示分子状态混乱程度的物理量。
   在信息论中,香农用熵的概念来描述信源的不确定度。我们可以用熵来表示数据集的纯度,与热熵类似,信息熵越小,数据集越纯净,即越多的数据具有相同的类别。
信息熵定义为:Entropy(D) = -Σp(i)*log(p(i))
   其中p(i)是数据集D中标签label取值为i的数据所占比例(概率)。在这里定义0log0=0,这里的log以2为底。

至于为什么信息熵要定义为如上形式,可以参考《Pattern Recognition and Machine Learning》[2]

(2)信息增益
   熵:表示随机变量的不确定性。
   条件熵:在一个条件下,随机变量的不确定性。(类似条件概率的理解)
   信息增益即为 **熵 - 条件熵** ,即在一个条件下,信息不确定性减少的程度。

详细的解释可以参考这篇博客[3]

于是信息增益定义为:Gain(D,A) = Entropy(D) - Σ( |D(v)|/|D| * Entropy(D(v)))
   其中,v属于某个Feature(属性)A的所有可能值的集合,D(v)是数据集D中FeatureA值为v的子集。Entropy(D)是D未用FeatureA分割之前的熵,Entropy(D(v))是D用FeatureA值为v分割之后的熵。
   FeatureA的每一个可能取值都有一个熵(Entropy(D(v))),该熵的权重是取该FeatureA的可能值所占数据在所有数据集D中的比例(|D(v)|/|D|)。
(3)选择分类能力最好的节点(Feature)
   选择信息增益最大的属性A作为当前节点的决策Feature。
   原因:熵刻画了数据集的纯度,数据集越混乱,熵就越大。熵越小,数据集越纯净,越多的数据具有相同的类别。当熵为0时,数据集中的数据都相等。
   FeatureA的信息增益就是按A来划分数据集时,数据集能比原来纯净多少。通过不断地划分,得到尽可能纯的节点,相当于降低数据集的熵。
   当决策树划分到叶子节点时,其熵大多为0(除了部分叶子节点中标签还未一致,但数据集中所有Feature均已划分完毕)。
3、伪代码及例题
   如果上述讲解仍觉得不甚理解,可以去找一道关于决策树的例题,手工计算信息熵及信息增益便会有更深的理解。
   至于伪代码及具体的从算法层面的编程流程,推荐《人工智能导论》鲍军鹏、张选平编著。
4、Python实现(具体代码及注释已经附上)
        # -*- coding: utf-8 -*-
"""
Created on Sun Nov 19 19:58:10 2017

@author: Lesley
"""
from math import log
#------------------------数据集-------------------------------
#四个因变量(属性) -> feature : "Outlook","Temperature","Humidity","Windy"
#标签 -> 是否出门 -> 'no','yes'
#-------------------------------------------------------------
def createDataSet():
    dataSet=[["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']
            ]
    feature = ["Outlook","Temperature","Humidity","Windy"]
    return dataSet, feature

#-------------计算数据集整体及各个feature的不同值的熵------------
def Entropy(dataSet):
    sample_num = len(dataSet)    
    label_category = {}
    for sample in dataSet:
        if sample[-1] not in label_category:
            label_category[sample[-1]] = 1
        else:
            label_category[sample[-1]] += 1  #各类别的数量
    #print (label_category)
    entropy = 0
    for i in label_category:
        temp = label_category[i]/sample_num
        entropy -= temp * log(temp,2)
    return entropy

#-------------根据不同feature的不同值筛选对应的数据集------------
def Extractdata(dataSet, axis, value):       #筛选出的是第axis个feature中值为value的sample集合 且此集合中不含第axis个featur对应的数据
    extDataSet = []
    for sample in dataSet:
        if sample[axis] == value:
            extSample = sample[:axis]        #复制第axis个featur之前的数据
            extSample.extend(sample[axis+1:])#复制第axis个featur之后的数据
            extDataSet.append(extSample)     #删除第axis个featur对应的数据
    return extDataSet

#--------对于每一节点得到最大的信息增益及对应的feature-----------
def BestFeature(dataSet):
    feature_num = len(dataSet[0]) - 1     #数据集的最后一项是标签
    entropy = Entropy(dataSet)
    bestEntropy = 0
    bestInfoGain = 0
    bestFeature = -1
    for i in range(feature_num):
        featuresample = [example[i] for example in dataSet]
        uniqueVals = set(featuresample)   #去除list中的重复元素
        newEntropy = 0
        for value in uniqueVals:
            extDataSet = Extractdata(dataSet, i, value)
            prob = len(extDataSet) / float(len(dataSet))  #当前feature中各个值所占比重
            newEntropy += prob * Entropy(extDataSet)      #Entropy(extDataSet) 得到的是第i个feature中的第j个值的熵

        gain = entropy - newEntropy       #信息增益越大越好 -> 熵越小,数据越纯净

        if gain > bestInfoGain:
            bestInfoGain = gain
            bestFeature = i
            bestEntropy = entropy - gain
    return bestInfoGain, bestFeature, bestEntropy

#--------------------------多数表决---------------------------
def majorityCnt(classList):  
    classCount = {}
    for vote in classList:
        if vote not in classCount.keys():
            classCount[vote] = 0
        classCount[vote] += 1
    return max(classCount)     #classlist中出现最多的元素      

#----------------------------建树-----------------------------
def CreateTree(dataSet,feature):
    classList = [example[-1] for example in dataSet]  #标签项
    best_infogain, best_feature, best_entropy= BestFeature(dataSet)  #最大信息增益增益值与其所对应的feature的顺序

    #--------------------停止划分的条件------------------------
    #数据集中的最后一项:标签为同一个值(类别相同)   best_entropy-> 最大熵为0
    if classList.count(classList[0]) ==len(classList): 
        #print(best_entropy)
        return classList[0]

    #最大信息熵小于等于0
    if best_infogain < 0 or best_infogain == 0:
        return majorityCnt(classList)

    #所有特征已经用完 -> 剩余标签
    if len(dataSet[0]) == 1:
        return majorityCnt(classList)          #剩余的list中所有feature已被用完,但标签中仍没有一致,这是采用多数表决
    #---------------------------------------------------------

    best_feature_name = feature[best_feature]  #最大信息增益对应的feature的名称
    myTree = {best_feature_name:{}}            #根节点 -> 某一个feature

    del(feature[best_feature])                 #删去已经处理过的feature属性
    featuresample = [example[best_feature] for example in dataSet]
    uniqueVals = set(featuresample)            #对于每一个feature的各个值

    for value in uniqueVals:
        subfeature = feature[:]#复制,使进一步的调用函数不会对原feature产生影响。 python中函数传入的list参数若修改会影响其原始值
        myTree[best_feature_name][value] = CreateTree( Extractdata(dataSet, best_feature, value),subfeature)
    return myTree

def main():
    data, feature = createDataSet()
    myTree = CreateTree(data,feature)
    print (myTree)

if __name__=='__main__':
    main()
5、运行结果
{'Outlook': {'rainy': {'Windy': {'FALSE': 'yes', 'TRUE': 'no'}}, 'overcast': 'yes', 'sunny': {'Humidity': {'high': 'no', 'normal': 'yes'}}}}

即生成了图1中所得到的决策树。

6、问题
    不知道大家有没有发现上述算法中存在的问题。当随着决策树的生长,其深度在不断增加,这时就会出现过拟合问题。过拟合会导致数据的预测效果不好。而如何防止过拟合及欠拟合的问题呢?我们下次再讲。
  • 2
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值