美文网首页
【机器学习】决策树(构造篇)

【机器学习】决策树(构造篇)

作者: Geekero | 来源:发表于2020-08-30 10:16 被阅读0次

下一篇为:【机器学习】决策树(Matplotlib可视化+项目实战)

  • 最经常使用的机器学习算法
  • k近邻法最大缺点是无法给出数据的内在含义,决策树主要优势是数据形式非常容易理解

机器根据数据集创建规则的过程,就是机器学习的过程

一、决策树的构造

  • 优点:计算复杂度不高,输出结果易理解,对中间值缺失不敏感,可以处理不相关特征数据
  • 缺点:可能会产生过度匹配问题
  • 适用数据类型:数值型、标称型

根据数学上的信息论,评估每个特征,找到决定性的特征,划分出最好的结果。原始数据集会被划分成几个数据集,这些子数据集会分布在第一个决策点的所有分支上。

如果某个分支下的数据属于同一类型,则此子数据集已经被正确划分数据分类,无需进一步对数据集进行分割。

如果子数据集的数据不属于同一类型,则需要重新划分数据子集的过程,知道所有具有相同类型对的数据均在同一个数据子集内。

创建分支的伪代码函数

def CreateBranch():
    检查数据集中每个子项是否属于统一分类
    if True: 
        返回类标签
    else:
        寻找划分数据集的最好特征
        划分数据集
        创建分支节点
        for 每一个划分的子集:
            递归回调函数CreateBranch()自身,并且增加返回结果到分支节点中
        return 分支节点    

决策树的一般流程

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

这里使用ID3算法划分数据集,每次划分数据集时,只选择一个特征属性。

信息增益

划分数据的大原则是:将无序的数据变得更加有序。使用信息论度量信息。划分数据之前之后发生的变化叫信息增益。信息增益是熵的减少或者数据无序度的减少

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

计算信息增益(information gain):
集合信息的度量方式:香农熵(信息熵)简称熵(entropy),也就是信息的期望值

信息熵是消除不确定性所需信息量的度量,即未知事件可能含有的信息量。通俗的讲信息熵是用来衡量信息量的大小。

  • 若不确定性越大,则信息量越大,熵越大。
  • 若不确定性越小,则信息量越小,熵越小。

信息量的定义:如果待分类的事务可能划分在多个分类之中,则符号xi的信息定义为:


其中p(xi)是选择该分类的概率

信息熵:计算所有类别所有可能值包含的信息期望值:


其中n是分类的数目

代码

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)
    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

运行

>>> import trees
>>> myDat, labels = trees.createDataSet()
>>> myDat
[[1, 1, 'yes'], [1, 1, 'yes'], [1, 0, 'no'], [0, 1, 'no'], [0, 1, 'no']]
>>> trees.calcShannonEnt(myDat)
0.9709505944546686

熵越高,则混合的数据也越多,增加第三个名为maybe的分类,测试熵的变化

>>> myDat[0][-1]='maybe'
>>> myDat
[[1, 1, 'maybe'], [1, 1, 'yes'], [1, 0, 'no'], [0, 1, 'no'], [0, 1, 'no']]
>>> trees.calcShannonEnt(myDat)
[1, 1, 'maybe']
[1, 1, 'yes']
[1, 0, 'no']
[0, 1, 'no']
[0, 1, 'no']
1.3709505944546687

得到熵以后,可以按照获取最大信息增益的方法划分数据集

另一张度量集合无序程度的方法是基尼不纯度(Gini impurity):从一个数据集合中随机选取子项,度量其错误分类到其他分组里的概率

划分数据集

对每个特征划分数据集的结果计算一次信息熵,然后判断按照那个特征划分数据集时最好的划分方式

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

运行:

>>> import trees
>>> myDat, labels = trees.createDataSet()
>>> myDat
[[1, 1, 'yes'], [1, 1, 'yes'], [1, 0, 'no'], [0, 1, 'no'], [0, 1, 'no']]
>>> trees.splitDataSet(myDat, 0, 0)
[[1, 'no'], [1, 'no']]

遍历整个数据集,循环计算信息熵和splitDataSet()函数,找到最好的特征划分方法

熵计算将会告诉我们如何划分数据集是最好的数据组织方式

选取特征,划分数据集,计算得到最好的划分数据集的特征

输入数据:

  • 必须是由列表元素组成的列表,而且所有的列表元素都要具有相同的数据长度
  • 数据最后一列是当前实例的类别标签
  • 无需限定list中的数据类型,既可以是数字也可以是字符串
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)
    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]   #所有第i个特征值或所有可能存在的值
        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

运行:

>>> from importlib import reload
>>> reload(trees)
<module 'trees' from 'D:\\Data\\File\\ML\\trees\\trees.py'>
>>> myDat, labels = trees.createDataSet()
>>> trees.chooseBestFeatureToSplit(myDat)
0
>>> myDat
[[1, 1, 'yes'], [1, 1, 'yes'], [1, 0, 'no'], [0, 1, 'no'], [0, 1, 'no']]

递归构建决策树

工作原理:

  • 得到原始数据集

  • 基于最好的属性值划分数据集,由于特征值可能多于2个,所以可能存在大于2个分支的数据集划分

  • 第一次划分之后,数据将被向下传递到树分支的下一个节点,在这个节点上,我们可以再次划分数据,因此可以采用递归的原则处理数据集

  • 递归结束条件是:程序遍历完所有划分数据集的属性,或者每个分支下的所有实例都具有相同的分类。如果所有实例具有相同的分类,则得到一个叶子节点或者终止块。任何到达叶子节点的数据必然属于叶子节点的分类

  • 第一个结束条件使得算法可以终止,甚至可以设置算法可以划分的最大分组数目

  • 其他决策树算法:如C4.5,CART,这些算法运行时并不总是在每次划分分组时都会消耗特征。

  • 如果数据集已经处理完所有属性,但是类标签依然不是唯一的,此时需要决定如何定义该叶子节点,通常采用多数表决的方法决定该叶子的分类

投票表决代码:

代码开头加上 import operator

def majorityCnt(classList): #分类名称的列表
    classCount = {}
    for vote in classList: #创建键值为classList为唯一值的数据字典
        if vote not in classList.keys(): classCount[vote] = 0
        classCount[vote] += 1
    sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=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:{}} #用字典存储树的信息
    labels = labels[:]
    del(labels[bestFeat]) 
    featValues = [example[bestFeat] for example in dataSet] #得到列表包含的所有属性值
    uniqueVals = set(featValues)
    #遍历当前特征包含所有属性值,在每个数据集划分上递归调用函数createTree()
    for value in uniqueVals: 
        subLabels = labels[:] #复制类标签
        myTree[bestFeatLabel][value] = createTree(splitDataSet(dataSet, bestFeat, value), subLabels)
    return myTree

运行:

>>> reload(trees)
<module 'trees' from 'D:\\Data\\File\\ML\\trees\\trees.py'>
>>> myDat, labels = trees.createDataSet()
>>> myTree = trees.createTree(myDat, labels)
>>> myTree
{'no surfacing': {0: 'no', 1: {'flippers': {0: 'no', 1: 'yes'}}}}

这棵树有3个叶子节点,2个判断节点

学习自

信息熵(香农熵)、条件熵、信息增益的简单了解
《机器学习实战》

相关文章

网友评论

      本文标题:【机器学习】决策树(构造篇)

      本文链接:https://www.haomeiwen.com/subject/dypyrktx.html