zoukankan      html  css  js  c++  java
  • 决策树算法

    一、什么是决策树?

      决策树的原理:通过一系列问题进行if/else的推导,最终实现决策。

      我们经常用决策树处理分类问题,决策树是最经常使用的数据挖掘算法。K-近邻算法可以完成很多分类任务,但是最大的缺点是给出的数据没有内在意义,决策树的优势就在于数据形式非常容易理解。

    二、决策树的构造

      在一个数据集上哪个特征在划分数据分类时能起到决定性作用?所以,为了找到决定性的特征,划分出最好的结果,我们必须评估每个特征。

      找到那个决定性的特征后,原数据集就被划分为几个数据子集,这些数据子集会分布在第一个决策点的所有分支上,如果某个分支下的数据属于同一个类型,则可以划分数据类型,如果数据子集内的数据不属于一个类型,则需要重复划分数据子集的过程,直到所有具有相同类型的数据均在一个数据子集内。

      创建分支的伪代码函数createBranch()如下所示:

      检测数据集中的每个子项是否属于同一分类:

        If so return 类标签;

        Else

          寻找划分数据集的最好特征

          划分数据集

          创建分支节点

            for 每个划分的子集

              调用函数createBranch并增加返回结果到分支节点中

          return 分支节点

    上面的伪代码createBranch是一个递归函数,在倒数第二行直接调用了它自己。后面我们 将把上面的伪代码转换为Python代码,这里我们需要进一步了解算法是如何划分数据集的。

    决策树的一般流程
    (1) 收集数据:可以使用任何方法。
    (2) 准备数据:树构造算法只适用于标称型数据,因此数值型数据必须离散化。
    (3) 分析数据:可以使用任何方法,构造树完成之后,我们应该检查图形是否符合预期。
    (4) 训练算法:构造树的数据结构。
    (5) 测试算法:使用经验树计算错误率。
    (6) 使用算法:此步骤可以适用于任何监督学习算法,而使用决策树可以更好地理解数据
    的内在含义。

      

    三、举个例,学习决策树算法的原理过程

      表3-1的数据包含5个海洋动物,特征包括:不浮出水面是否可以生存,以及是否有脚蹼。我 们可以将这些动物分成两类:鱼类和非鱼类。现在我们想要决定依据第一个特征还是第二个特征 划分数据。在回答这个问题之前,我们必须采用量化的方法判断如何划分数据。下一小节将详细 讨论这个问题。

    表3-1 海洋生物数据
     不浮出水面是否可以生存 是否有脚蹼  属于鱼类
    1         是           是        是
    2         是           是        是
    3         是           否        否
    4         否           是        否
    5         否           是        否

      下面讲解采用量化判断如何划分数据:

    信息增益:计算每个特征值划分数据集获得的信息增益,获得信息增益最高的特征就是最好的选择。

    介绍信息增益之前,首先需要介绍一下熵的概念,这是一个物理学概念,表示“一个系统的混乱程度”。系统的不确定性越高,熵就越大。假设集合中的变量X={x1,x2…xn},它对应在集合的概率分别是P={p1,p2…pn}。那么这个集合的熵表示为:
    
    举一个的例子:对游戏活跃用户进行分层,分为高活跃、中活跃、低活跃,游戏A按照这个方式划分,用户比例分别为20%,30%,50%。游戏B按照这种方式划分,用户比例分别为5%,5%,90%。那么游戏A对于这种划分方式的熵为:
    
    
    同理游戏B对于这种划分方式的熵为:
    
    游戏A的熵比游戏B的熵大,所以游戏A的不确定性比游戏B高。用简单通俗的话来讲,游戏B要不就在上升期,要不就在衰退期,它的未来已经很确定了,所以熵低。而游戏A的未来有更多的不确定性,它的熵更高。
    --------------------- 
    作者:It_BeeCoder 
    来源:CSDN 
    原文:https://blog.csdn.net/it_beecoder/article/details/79554388 
    版权声明:本文为博主原创文章,转载请附上博文链接!

     

     程序清单1:计算给定数据集的信息熵

     1 #计算训练数据最后一列的信息熵
     2 def calcShannonEnt(dataSet):
     3     #获取训练数据集行数(记录数)
     4     numEntries = len(dataSet) 
     5     labelCounts = {}
     6     #使用for循环从上往下逐行读取每一条记录,统计训练数据集各种标签数量有多少
     7     for featVec in dataSet: 
     8         #取出当前这条记录的标签
     9         currentLabel = featVec[-1]
    10         #判断当前得到的这个标签currentLabel是否已经存在字典labelCounts的键(key)了
    11         #labelCounts.keys():字典调用keys()这个函数可以获取它所有的键(key)
    12         if(currentLabel not in labelCounts.keys()): 
    13             #如果字典labelCounts的所有键都没有当前这个标签,这个字典就新增一个键和值,键是当前这个标签,值是0
    14             labelCounts[currentLabel] = 0
    15         #如果字典labelCounts的所有键存在这个标签,那就把这个键对应的值加1
    16         labelCounts[currentLabel] += 1
    17     #声明一个变量保存最后一列的信息熵值,最后一列所有标签的熵值带入公式就可以求得该列的信息熵。
    18     shannonEnt = 0.0
    19     #for循环读取每一个字典的每一个键(key),也就是取得标签
    20     for key in labelCounts:
    21         #计算当前这个标签占的概率
    22         prob = float(labelCounts[key])/numEntries
    23         #将这个标签的概率带入熵的计算公式,最后就可以计算得到该列的信息熵了,
    24         #因为这是计算数据集标签列的信息熵,所有该信息熵就是该数据集的信息熵
    25         shannonEnt -= prob * log(prob,2) 
    26     #函数返回该训练数据集的信息熵
    27     return shannonEnt

    程序清单2:按照给定的特征划分数据集

     1 #这个函数是用来划分数据集的
     2 #dataSet:待划分的数据集
     3 #axis:这是一个特征的下标,就是根据这个特征来划分 待划分dataSet数据集的
     4 #value:下标axis对应的那个特征下的某个值,根据这个值来进一步确定数据集的划分
     5 def splitDataSet(dataSet, axis, value):
     6     #声明一个列表用来保存划分后得到的数据集
     7     retDataSet = []
     8     #使用for循环从上往下逐行读取记录
     9     for featVec in dataSet:
    10         #featVec:就是当前的一条记录
    11         #featVec[axis]:就是数据集第axis+1个特征的值,如:featVec[1]就表示:这是第二个特征的某个取值
    12         #判断这个取值是否等于axis个特征下划分数据集的值
    13         if(featVec[axis] == value):
    14             #如果相等就把第axis+1特征前面的数据都保存到这个列表reducedFeatVec
    15             reducedFeatVec = featVec[:axis]     
    16             #在retDataSet右边紧接着保存第axis+1特征后面的数据都保存到这个列表reducedFeatVec
    17             reducedFeatVec.extend(featVec[axis+1:])
    18             #这样就得到了一条新的数据记录,与原来featVec这条数据记录相比,它少了特征下标为axis的数据
    19             #将这条新的数据保存到列表retDataSet里面
    20             retDataSet.append(reducedFeatVec)
    21     #返回拆分后的数据集,与原来待划分的数据集dataSet相比,retDataSet它少了特征下标为axis这一列的数据
    22     return retDataSet

    程序清单3:选择最好的数据集划分方式

    函数chooseBestFeatureToSplit()使用了程序清单1和程序清单2的函数。

     1 #一般数据集都是有很多属性的,那该选取那个属性作为决策树的根节点呢?
     2 #这个函数就是确定哪个属性作为根节点的
     3 #选择信息增益最大的那一列的属性作为根节点
     4 
     5 #dataSet:当前传递过来的数据集
     6 def chooseBestFeatureToSplit(dataSet):
     7     #获取数据集有多少个属性,数据集最后一列是标签,不是数据集的变量属性,所以要减1
     8     numFeatures = len(dataSet[0]) - 1    
     9     #计算这个数据集dataSet的信息熵(也就是计算最后的标签列的熵值)
    10     baseEntropy = calcShannonEnt(dataSet)
    11     #定义一个变量保存信息增益,所谓的信息增益就是:上面数据集dataSet的信息熵baseEntropy 与 某个特征的信息熵的差值
    12     bestInfoGain = 0.0
    13     #定义一个变量保存最好的一个特征的下标,所谓最好特征,就是该特征与数据集dataSet的信息熵baseEntropy的差值(信息增益),
    14     #比其它特征与数据集dataSet的信息熵baseEntropy的差值(信息增益)都要大
    15     bestFeature = -1
    16     #从左到右逐个特征地读取(一列一列地读取原数据集的各种特征的值)
    17     for i in range(numFeatures):   
    18         #featList:就是下标为i的特征那一列的值
    19         #example:是数据集的一条记录
    20         #example[i]:取得这条数据记录下标为i的数值(取得第i+1个属性的值(下标从零开始))
    21         featList = [example[i] for example in dataSet]
    22         #将这列的数据去重,uniqueVals里面的元素都是唯一的
    23         uniqueVals = set(featList)      
    24         #newEntropy这个变量是用来保存这列数据信息熵的(也就是当前这个特征的信息熵)
    25         newEntropy = 0.0
    26         #逐个取出该列不同的值
    27         for value in uniqueVals:
    28             #然后调用划分数据集函数splitDataSet 来划分当前dataSet这个数据集
    29             #根据当前第i+1个特征(特征下标从零开始)和这个具体的特征值,就可以划分数据集了
    30             #(这里不是很理解的话,今晚再画图跟你说一下就可以了,看着代码处理好像挺抽象复杂的,其实并不是很复杂)
    31             #跟我们平时划分表格一样的思路和操作,暂时看不懂的地方不用太过去纠结的,顺其自然往下看就可以了,放心...有我在......
    32             subDataSet = splitDataSet(dataSet, i, value)
    33             #subDataSet :就是根据当前特征和当前特征的value划分得到的新的数据集
    34             #计算根据当前特征和当前特征的value 所占的概率
    35             prob = len(subDataSet)/float(len(dataSet))
    36             #calcShannonEnt(subDataSet) :计算新得到的这个数据集的信息熵
    37             #prob * calcShannonEnt(subDataSet) :给这个信息熵加权,这个权就是上面那个概率
    38             #然后下标为i这个特征下的所有值对应的信息熵加权值 都累积保存到变量newEntropy中
    39             newEntropy += prob * calcShannonEnt(subDataSet)   
    40         #结束循环后,newEntropy就是下标为i的特征的信息熵了
    41         #接下来计算该列(下标为i的特征列) 信息增益,也就是与划分之前数据集dataSet的信息熵的差值
    42         infoGain = baseEntropy - newEntropy  
    43         #计算出某列的信息增益infoGain后,判断这个信息增益是不是比预先定义的 bestInfoGain = 0.0信息增益要大
    44         if (infoGain > bestInfoGain):  
    45             #如果根据下标i的特征划分后得到的数据集信息增益比预先定义的要大,说明根据该特征来划分数据集是可行的
    46             #把当前这个大的信息增益替换掉原来预先定义的信息增益
    47             bestInfoGain = infoGain       
    48             #使用变量bestFeature记录下最大信息增益那个特征的下标
    49             bestFeature = i
    50     #返回计算得到信息增益最大的那个属性的下标,根据这个下标就可以找到那个特征作为根节点了
    51     return bestFeature 

    程序清单4:创建树的函数代码

     1 #这是一个根据训练数据集生成一棵决策树的函数
     2 #dataSet:训练数据集
     3 #labels:训练数据集每一列的属性
     4 def createTree(dataSet,labels):
     5     #获取数据集的标签列
     6     classList = [example[-1] for example in dataSet]
     7     #判断标签列表是不是只剩下一种类别
     8     if(classList.count(classList[0]) == len(classList)): 
     9         #如果是就返回这个标签,结束本次对函数createTree的调用,这个返回去的标签就作为决策树的一个叶子节点
    10         return classList[0]
    11     #判断数据集是不是只剩下一列数据
    12     if(len(dataSet[0]) == 1): 
    13         #是的话,那这列数据便是数据集最后的标签列,这时通过调用majorityCnt函数来统计,
    14         #结束本次对函数createTree的调用该返回那个类别标签
    15         return majorityCnt(classList)
    16     #调用chooseBestFeatureToSplit来获得当前数据集dataSet的最优划分特征下标
    17     bestFeat = chooseBestFeatureToSplit(dataSet)
    18     #根据这个下标找到对应的那个特征
    19     bestFeatLabel = labels[bestFeat]
    20     #使用字典保存一棵决策树,根节点就是上面的那个特征
    21     myTree = {bestFeatLabel:{}}
    22     #定义一个列表保存除去那个bestFeatLabel最优特征外的其它特征
    23     temp = []
    24     #使用for循环逐个地读取特征
    25     for i in labels:
    26         #判断如果这个特征i不是上面的那个最优特征,就保存到temp里面
    27         if(i != labels[bestFeat]):
    28             temp.append(i)
    29     #将除去那个bestFeatLabel最优特征外的其它特征作为剩下新的数据集特征
    30     labels = temp
    31     #取出最优特征bestFeatLabel那列的所有值
    32     featValues = [example[bestFeat] for example in dataSet]
    33     #将这列最优特征的值去重
    34     uniqueVals = set(featValues)
    35     #逐个取出该列不同的值
    36     for value in uniqueVals:
    37         #复制一份新的特征列表,记为:subLabels
    38         subLabels = labels[:]    
    39         #这棵树现在根节点就是字典的键bestFeatLabel,条件值是字典的值value
    40         #递归调用生成决策树这个createTree函数,一直重复上面的过程.....直到数据集只剩下标签列为止
    41         # splitDataSet(dataSet, bestFeat, value):把切分后的数据集当做新的数据集
    42         #subLabels:新数据集的特征列表
    43         #(这个递归过程今晚回去了再详细画图跟你说说...)
    44         myTree[bestFeatLabel][value] = createTree(splitDataSet(dataSet, bestFeat, value),subLabels)
    45     #返回一棵决策树
    46     return myTree 

    程序清单5:测试算法:使用决策树执行分类

     1 这个函数是使用决策树来对未知标签数据进行预测分类的,也就是通过遍历一棵决策树来取得未知标签的预测值
     2 #inputTree:一棵决策树
     3 #featLabels:未知标签的数据的列名(属性)
     4 #testVec:未知标签数据的值
     5 def classify(inputTree,featLabels,testVec):
     6     #获取根节点
     7     for i in inputTree.keys():
     8         firstStr = i
     9         break
    10     #根据根节点firstStr,获取根节点下的子树
    11     secondDict = inputTree[firstStr]
    12     #获取根节点这个特征所在特征列表featLabels的位置下标
    13     featIndex = featLabels.index(firstStr)
    14     #根据根节点的下标,获取对应未知标签数据中的下标对应的值
    15     key = testVec[featIndex]
    16     #把上面得到的那个值作为子树的键,得到根节点的子树下的内容valueOfFeat
    17     valueOfFeat = secondDict[key]
    18     #判断valueOfFeat是不是字典,如果不是说明它就是叶子节点了,也就是那个需要返回的标签了
    19     if isinstance(valueOfFeat, dict): 
    20         #如果子树下的内容还是字典,那就把valueOfFeat当成新的一棵树,继续调用classify这个函数,重复上面的过程
    21         classLabel = classify(valueOfFeat, featLabels, testVec)
    22     else: 
    23         #如果不是字典(子树),而是叶子节点(某个标签类别)的话,就直接返回这个标签就可以了
    24         #这个返回的标签就是:对未知标签数据的一个预测标签
    25         classLabel = valueOfFeat
    26     #返回的就是对未知标签数据预测的标签
    27     return classLabel
    如有问题请留言,谢谢!
  • 相关阅读:
    12 python json&pickle&shelve模块
    11 python shutil 模块
    10 python os&sys 模块
    9 random模块
    8 python time$datetime
    7 python 模块间相互导入
    6 unit3-文件操作&函数 review
    3 Python 函数介绍
    hibernate课程 初探单表映射2-6 session详解(下)
    hibernate课程 初探单表映射2-5 session详解(上)
  • 原文地址:https://www.cnblogs.com/yunsi/p/11070270.html
Copyright © 2011-2022 走看看