《机器学习实战》决策树(ID3算法)的分析与实现

============================================================================================
《机器学习实战》系列博客是博主阅读《机器学习实战》这本书的笔记,包含对其中算法的理解和算法的Python代码实现

另外博主这里有机器学习实战这本书的所有算法源代码和算法所用到的源文件,有需要的留言
============================================================================================



KNN算法请参考:http://blog.csdn.net/gamer_gyt/article/details/47418223


一、简介

        决策树是一个预测模型;他代表的是对象属性与对象值之间的一种映射关系。树中每个节点表示某个对象,而每个分叉路径则代表的某个可能的属性值,而每个叶结点则对应从根节点到该叶节点所经历的路径所表示的对象的值。决策树仅有单一输出,若欲有复数输出,可以建立独立的决策树以处理不同输出。 数据挖掘中决策树是一种经常要用到的技术,可以用于分析数据,同样也可以用来作预测
二、基本思想
       1)树以代表训练样本的单个结点开始。
       2)如果样本都在同一个类.则该结点成为树叶,并用该类标记。
       3)否则,算法选择最有分类能力的属性作为决策树的当前结点.
       4)根据当前决策结点属性取值的不同,将训练样本数据集tlI分为若干子集,每个取值形成一个分枝,有几个取值形成几个分枝。匀针对上一步得到的一个子集,重复进行先前  步骤,递4'I形成每个划分样本上的决策树。一旦一个属性出现在一个结点上,就不必在该结点的任何后代考虑它。
       5)递归划分步骤仅当下列条件之一成立时停止:
       ①给定结点的所有样本属于同一类。
       ②没有剩余属性可以用来进一步划分样本.在这种情况下.使用多数表决,将给定的结点转换成树叶,并以样本中元组个数最多的类别作为类别标记,同时也可以存放该结点样本的类别分布,
       ③如果某一分枝tc,没有满足该分支中已有分类的样本,则以样本的多数类创建一个树叶。
三、构造方法
       决策树构造的输入是一组带有类别标记的例子,构造的结果是一棵二叉树或多叉树。二叉树的内部节点(非叶子节点)一般表示为一个逻辑判断,如形式为a=aj的逻辑判断,其中a是属性,aj是该属性的所有取值:树的边是逻辑判断的分支结果。多叉树(ID3)的内部结点是属性,边是该属性的所有取值,有几个属性值就有几条边。树的叶子节点都是类别标记。
由于数据表示不当、有噪声或者由于决策树生成时产生重复的子树等原因,都会造成产生的决策树过大。因此,简化决策树是一个不可缺少的环节。寻找一棵最优决策树,主要应解决以下3个最优化问题:①生成最少数目的叶子节点;②生成的每个叶子节点的深度最小;③生成的决策树叶子节点最少且每个叶子节点的深度最小。
四、Python代码实现
调用:命令行进入该代码所在目录执行:import trees       dataSet,labels = trees.createDataSet()    trees.myTree()
#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
'''
测试结果
[[1, 'no'], [1, 'no']]
>>> dataSet
[[1, 1, 'yes'], [1, 1, 'yes'], [1, 0, 'no'], [0, 1, 'no'], [0, 1, 'no']]
>>> labels
['no surfacing', 'flippers']
'''
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 #该类标签下含有数据的个数
    shannonEnt = 0.0
    for key in labelCounts:
        prob = float(labelCounts[key])/numEntries  #同类标签出现的概率
        shannonEnt -=prob*log(prob,2)   #以2为底求对数
    return shannonEnt
'''
测试结果
>>> trees.calcShannonEnt(dataSet)
0.9709505944546686
'''
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 #返回一个列表
'''
测试结果
>>> trees.splitDataSet(dataSet,0,1)
[[1, 'yes'], [1, 'yes'], [0, 'no']] 
>>> trees.splitDataSet(dataSet,0,0)
[[1, 'no'], [1, 'no']]
'''          
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
'''
测试结果
>>> trees.chooseBestFeatureToSplit(dataSet)
0
说明:第0个特征是最好的用于划分数据集的特征
'''
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  
'''
测试结果
>>> reload(trees)

>>> dataSet,labels = trees.createDataSet()
>>> myTree = trees.createTree(dataSet,labels)
>>> myTree
{'no surfacing': {0: 'no', 1: {'flippers': {0: 'no', 1: 'yes'}}}}
'''


上一篇:Java 文件上传下载管理器(控制台)


下一篇:mysql 最基础的日常操作