决策树(1)ID3

一、决策树

在1986年,机器学习研究者J.Ross Quinlan 开发了决策树算法,称为ID3(Iterative Dichotomiser,迭代的二分器)。这项工作扩展了E.B.Hunt,J.Marin 和P.T.Stone 的概念学习系统。Quinlan 后来提出了C4.5、C5.0算法。与ID3发明几乎同时,1984年统计学家L.Breiman,J.Friedman,R.Olshen 和C.Stone 出版了分类与回归树(CART),介绍了二叉决策树的产生。这两个基础算法激发了决策树归纳研究的旋风。

1.1 决策树概念

决策树模型是一种对实例进行分类或者回归的树型结构。决策树由结点和有向边组成,结点有两种类型:内部结点和叶结点,内部结点表示一个特征,叶结点表示一种类别或者目标值。
决策树(1)ID3_第1张图片
决策树包括分类决策树和回归决策树,分类决策树叶结点对应于一个类别,回归决策树叶结点对应一个连续值。决策树的思想是空间划分:用递归的方式把关于自变量的m维特征空间划分成不重叠的空间。
决策树(1)ID3_第2张图片

1.2 决策树学习目的

决策树学习的目的是根据给定的训练数据集,学习到一个决策树模型,使它能够对决策树进行正确的分类。决策树学习的本质上是从训练数据集中归纳出一组分类规则。与训练数据集不相矛盾的树可能有多颗,也可能一颗都没有。我们需要的是一颗与训练数据集矛盾较小的树,同时有很好的泛化能力。即:不仅对训练数据有很好的拟合,同时能对未知类型数据很好的预测。

二、ID3 算法

ID3 算法是较早提出的一种算法。之后在它基础上进行改进提出了C4.5,C5.0。它们都属于多分支决策树,与此同时还有一种二分支决策树CART,在生成决策树的其他细节上也有所不同。本文先介绍ID3,之后再介绍CART,最后比较它们的异同。
决策树的学习算法是一个递归地选择最优特征,并根据该特征对训练数据进行分割,使得对各个子数据集有一个最好的分类的过程。以上的方法构造的决策树可能对训练数据有较好的分类能力,但对未知的测试数据却未必有很好的分类能力,即有可能发生过拟合。我们需要对已生成的树自下而上的进行剪枝,使它具备很好的泛化能力。
由以上可以看到,ID3 决策树学习算法包括几个关键的元素:特征选择、决策树的构造、决策树的剪枝。

2.1 特征选择

特征选择在于选取对训练数据具有分类能力的特征,这样可以提高决策树的学习效率。ID3进行特征选择使用的规则是信息增益。

2.1.1 信息增益


对于这个训练数据,我们选择哪个特征来划分特征空间呢?
如果一个特征将训练数据划分为各个子集,使得各个子集在当前有一个最好的分类,那么就应该选择这个特征。信息增益能够很好表示这样一个规则。
信息增益:对于训练数据集D和特征A,经验熵H(D)表示对数据集D进行分类的不确定性;经验条件熵表示H(D|A)表示在特征A给定的条件下对数据集D进行分类的不确定性。那么H(D)与H(D|A)的差即是信息增益g(D,A)。它表示由于特征A使得对数据集D的分类不确定性减少的程度。

g(D,A)=H(D)H(D|A)

这里熵的定义是:随机变量不确定性的度量。
H(X)=i=0npilog(pi)

熵的特性是:熵越大,随机变量不确定性越大。
在上面例子中,经验熵为:
H(D)=615log615915log915

在特征“有工作”下的经验条件熵为:
H(D|A)=1015H(D1)+515H(D2)=1015(610log610410log410)+515(55log550)

则由经验熵与经验条件熵之差可以得到特征“有工作”下的信息增益。然后我们对训练数据集,计算每个特征的信息增益,并比较他们的大小,选择信息增益最大的特征。

2.1.2 求最优特征的实现

要选择DataSet 中的最优特征(信息增益最大的特征),需要三个函数:(i)求一个训练数据集的熵 (ii)求某一个特征的信息增益 (iii)遍历所有特征,找到信息增益最大的特征
(i)求熵

import numpy as np
#输入参数为训练数据集,输出为该数据集的熵
def entropy(dataSet):
    labelcount = dict()#为所有可能分类创建字典
    for data in dataSet:
        label = data[-1]
        labelcount[label]=labelcount.setdefault(label,0)+1
    ent=0#求熵
    for key in labelcount.keys():
        prob = labelcount[key]/float(len(dataSet))
        ent -= prob*np.log2(prob)
    return ent

(ii)求特征A的信息增益

"""包括两个部分:特征A根据特征值对数据集的划分得到各个子数据集;每个子数据集的熵乘以对应的概率之和。"""
#特征A对数据集的划分;输入为训练数据集、特征A的下标、需要返回特征的值;输出是依照该值得到的数据集的子集
def splitDataSet(dataSet,axis,value):
    reducedDateSet=list()
    for data in dataSet:
        if(data[axis]==value):
            reducedData=data[:axis]
            reducedData.extend(data[axis+1:])
            reducedDataSet.append(reducedData)
    return reducedDataSet
#求信息增益;输入为训练数据集、特征A的下标;输出是特征A的信息增益  
def infoGain(dataSet,axis):
    baseEntropy = entropy(dataSet)#经验熵
    featureValues = set([data[axis] for data in dataSet])#特征A取值的列表
    featuredEntropy =0#经验条件熵
    for key in featureValues:
        subDataSet = splitDataSet(dataSet,axis,key)
        subEntropy = entropy(subDataSet)
        prob = len(subDataSet)/float(len(dataSet))
        featuredEntropy += prob * subEntropy
    infogain = baseEnropy - featuredEntropy#特征A的信息增益
    return infogain

(iii)求最优特征,即所有特征中信息增益最大的特征

#输入是训练数据集;输出是所有特征中信息增益最大的特征下标,最优信息增益
def getBestFeature(dataSet):
    m,n = np.shape(dataSet)
    bestGain=0
    bestAxis=-1
    for i in xrange(n-1):#遍历所有特征
        infogain = infoGain(dataSet,i)
        if(infogain>bestGain):
            bestGain=infogain
            bestAxis = i
    return bestAxis,bestGain

2.2 ID3 决策树的构造

2.2.1 ID3算法思路

""" 输入参数:训练数据集、特征值集、信息增益阈值b"""
ID3(DataSet,featureList,b): 
    - 创建根节点T
    - if DataSet 所有类别相同,则标记T的类别为该类
    - if DataSet 中特征集合为空,则标记T的类别为DataSet中多数类的类别
    - 从 DataSet 中选择最优特征A(信息增益最大的特征)
    - if 最优特征的信息增益小于信息增益阈值b,则标记T的类别为DataSet 中多数类的类别
    - 根据特征A的每一个取值v,将DataSet 划分为不同的子集DS,对于每一个子集DS:
         - 创建节点C
         - 节点C=ID3(DataSet,featureList -A,b)
         - 将节点C添加到T的子节点上

2.2.2 ID3的实现

在2.1 节中我们已经得到了求最优特征的函数,现在仍需要两个函数:(i) 求DataSet 中多数类的类别;(ii) 实现决策树的生成过程
(i)求DataSet 中多数类的类别

#辅助函数,输入参数为数据集D中最后一列(类别列表)
def majority(labelValues):
    labelCount =dict()
    for label in labelValues:
        labelCount[label]=labelCount.setdefault(label,0)+1
    labelCountSort = sorted(labelCount.iteritems(),key = lambda x:x[1],reverse=True)
    return laelCountSort[0][0]

(ii)实现决策树的生成过程

#生成函数,输入为训练数据集、特征的属性值列表和信息增益阈值b
def createTree(dataSet,features,b):
    labelValues = [data[-1] for data in dataSet]
    if labelValues.count(labelValues[0])==len(labelValues): #全部为同一类别
        return labelValues[0]
    if len(dataSet[0])==1: #特征为空
        return majority(labelValues)
    bestAxis,bestGain = getBestFeature(dataSet)#最优特征下标,信息增益     
    if bestInfogain < b:
        return majority(labelValues)
    bestFeature = features[bestAxis]#最优特征值
    ID3Tree ={bestFeature:{}}#构造决策树
    del(features[bestAxis]) #对每一个子集,以{全部特征-特征A}为特征

    featureValues = set([data[bestAxis] for data in dataSet])
    for key in featureValues:
        subFeatures = features[:]
        subDataSet = splitDataSet(dataSet,bestAxis,value)
        ID3Tree[bestFeature][key] = createTree(subDataSet,subFeatures,b)
    return ID3Tree

2.3 决策树的剪枝

三、ID3 算法进行分类

上面已经实现了如何从数据集中构造树,然而在数据量比较大时,使用字典表示这棵树往往不够清晰,因此我们可以使用可视化的方式进行展示;决策树得到之后,就可以进行对未知样本集进行分类操作;另外,决策树是由数据集训练得到的,为了避免每次训练,我们可以将训练得到的决策树存储起来,方便以后直接使用训练完成的决策树。
因此,本节介绍三个方面内容:1、可视化 2、分类 3、存储

3.1 可视化

3.2 分类

3.2.1 分类过程

通过训练数据集构造决策树之后,我们就可以用它进行实际数据的分类。所需要的输入包括一颗训练完成的树(实际上是一个字典),一个未知的样本,和样本的属性集(和训练数据的属性集是一样的),分类的过程即是将这个未知的样本与已知的字典去对应,找到属于自己的类别。可见每次分类都是一条未知样本,若是一个样本集,我们需要在外面加一个循环。下面看具体实现。

3.2.2 分类的实现

由上面所说,我们写两个函数:(i)对一条未知样本的分类 (ii)对未知样本集的分类
(i)对一条未知样本的分类

"""输入是训练完成的树,未知样本,样本属性集,输出是未知样本的预测类别"""
"""为了便于理解代码,样本属性集即是['A or B','x or y'],训练的trees是{'A or B': {'A': {'x or y': {'y': 0, 'x': 1}}, 'B': 0}},下面对照这个树去理解代码含义""" 
def ID3Classify(tree,testData,features):
    firstFeature = tree.keys()[0]#这是用于分类的第一个属性
    secondDict = tree[firstFeature]#这是一个字典,包括该属性的几个分支
    featAxis = features.index(firstFeature)#通过属性集找到这是第几维度的属性
    key = testData[featAxis]#于是找到了testData 在这个维度的值
    if type(secondDict[key]).__name__=='dict':
        classLabel = ID3Classify(secondDict[key],testData,features)
    else:
        classLabel = secondDict[key]
    return classLabel

(ii)对未知样本集的分类

def ID3(trees,testDataSet,features):
    classLabels=list()
    for testData in testDataSet:
        classLabel = ID3Classify(trees,testData,features)
        classLabels.append(classLabel)
    return classLabels

3.3 存储

3.3.1 保存

把训练生成的树保存到本地文件中。

import pickle
def storeTree(inputTree,filename):
    fw = open(filename,'w')
    pickle.dump(inputTree,fw)
    fw.close() 

3.3.2 取出

把本地保存的树取出来使用

def grabTree(filename):
    fr = open(filename)
    return pickle.load(fr)

你可能感兴趣的:(决策树(1)ID3)