欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

(学习笔记)机器学习:决策树

程序员文章站 2022-03-30 16:33:21
...

决策树

简介:决策树的原理与游戏 ‘20个问题’ 类似,用户输入一系列数据,然后给出结论。

下面给出5数据,后面程序测试数据以此为例

以 ‘能否不浮出水面生存’ 和 ‘是否有脚蹼’ 两个特性判断 该生物是否是鱼


------No surfacing?--------Flippers?-----------Fish?--------

  1. ----Yes -----------------Yes --------------Yes ---------
  2. ----Yes -----------------Yes --------------Yes --------
  3. ----Yes -----------------No ---------------No --------
  4. ----No ------------------Yes --------------No --------
  5. ----No ------------------No ---------------No ----------

下图为流程图形式的决策树

(学习笔记)机器学习:决策树

决策树的特性

优点:计算复杂度不高,对中间值的缺失不敏感,可以处理不相关特征数据。

缺点:可能产生过度匹配问题。

适用数据类型:数值型,标称型。

一些补充概念

  1. 信息增益:在划分数据集之前之后,信息发生的变化。

  2. 信息增益的用处:知道如何计算信息增益,就可以计算每个特征值划分数据集获得的信息增益,从而知道获得最高信息增益的特征。

  3. 熵(香农熵):定义为信息的期望值。

#熵的公式

        H(X)=−∑p(xi)log(p(xi))    (i=1,2,…,n)

其中X 表示的是随机变量,随机变量的取值为(x1,x2,…,xn) ,p({x_i}) 表示事件xi发生的概率,且有∑p(xi)=1 。信息熵的单位为bit。

Python计算给定数据集的香农熵

from math import log

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) #log base 2
    return shannonEnt

可以创建函数,自输入数据测试一下上面的代码

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

划分数据集

通过度量划分数据集的熵,可以判断当前是否正确的划分了数据集

#按照给定特征划分数据集
#三个参数分别代表:‘带划分的数据集’,‘划分数据集的特征’,‘需要返回的特征的值’
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

使用前面两个函数,构造函数来选择最好的划分方式

def chooseBestFeatureToSplit(dataSet):
    numFeatures = len(dataSet[0]) - 1     
    baseEntropy = calcShannonEnt(dataSet)
    bestInfoGain = 0.0; bestFeature = -1
    for i in range(numFeatures):      
        featList = [example[i] for example in dataSet]    
        uniqueVals = set(featList)      
        newEntropy = 0.0
        for value in uniqueVals:       #计算每种划分的信息熵
            subDataSet = splitDataSet(dataSet, i, value)
            prob = len(subDataSet)/float(len(dataSet))
            newEntropy += prob * calcShannonEnt(subDataSet)     
        infoGain = baseEntropy - newEntropy    
        if (infoGain > bestInfoGain):       #选择最好的划分方式
            bestInfoGain = infoGain         
            bestFeature = i
    return bestFeature                    

创建树

#参数分别是 数据集 和 标签列表
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])
    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 

-#代码中两个if语句是递归的两个停止条件,分别为 ‘所有标签完全相同’ 和 ‘遍历完所有特征后仍然不能将数据集划分为仅包含唯一类别的分组’

#由于第二个退出递归条件无法简单的返回唯一的类标签,使用majorityCnt函数返回出现次数最多的类别

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)
    return sortedClassCount[0][0]

《Machine Learning in Action》 - Peter Harrington