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

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

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

(一)决策树的概念

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

项目 Outlook(Feature1) Temperature(Feature2) Humidity(Feature3) Windy(Feature4) Good_State?(Label)
1 sunny hot high FALSE no
2 sunny hot high TRUE no
3 sunny hot high TRUE no
4 overcast hot high FALSE yes
5 rainy mild high FALSE yes
6 rainy cool normal FALSE yes
7 rainy cool normal TRUE no
8 overcast cool normal TRUE yes
9 sunny mild high FALSE no
10 sunny cool normal FALSE yes
11 rainy mild normal FALSE yes
12 sunny mild normal TRUE yes
13 overcast mild high TRUE yes
14 overcast hot normal FALSE yes
15 rainy mild high TRUE no

我们需要决策树做的就是找到一种分类器根据天气的四个feature判断出其所属State的标签。
下面介绍决策树:它从一组无次序、无规则的实例中推理出以决策树表示的分类规则[1]。采用自顶向下的递归方式,在决策树的内部节点(feature)进行属性的比较(比如根据feature2比较天气的温度高低),并根据不同属性值判断从该节点向下的分支,在决策树的叶节点得到结论。
如下图1,我们可以根据此决策树来表示决策逻辑。
机器学习—决策树(ID3算法)及其python实现_第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、问题
    不知道大家有没有发现上述算法中存在的问题。当随着决策树的生长,其深度在不断增加,这时就会出现过拟合问题。过拟合会导致数据的预测效果不好。而如何防止过拟合及欠拟合的问题呢?我们下次再讲。

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