机器学习——决策树算法

决策树

决策树是附加概率结果的一个树状的决策图,是直观的运用统计概率分析的图法。机器学习中决策树是一个预测模型,它表示对象属性和对象值之间的一种映射,树中的每一个节点表示对象属性的判断条件,其分支表示符合节点条件的对象。树的叶子节点表示对象所属的预测结果。(==自己理解各位大神勿喷:==决策树主要作用于预测,根据一定的信息数据推断结果,比如:根据一定量的数据【男生,女生,声音粗细,头发长短等】推断此人是男生还是女生。)

信息熵

信息是一种抽象的概念,那么如何对信息进行一个量化的操作呢?1948年,香农提出了“信息熵”的概念。一条信息的信息量大小和它的不确定性有直接的关系,要搞清楚一件非常不确定的事情,或者说我们对一件事情一无所知,就需要了解大量的信息,信息量的度量就等于不确定性的多少。

举个栗子,NBA总决赛的夺冠球队,假设你对NBA球队一无所知,你需要猜多少次?(假设每个球队的夺冠几率都是一样的)这里我们可以给进入季后赛的NBA球队进行编号(NBA季后赛会选出16支球队),然后使用二分法进行猜测(猜测冠军队伍在1-8号球队之间,是的话,在进行二分;不是的话就在9-16号球队之间),这样我们要猜测的次数最多是4次(2^4=16嘛)。

信息熵使用比特(bit)来衡量信息的多少,计算公式如下:
-(P1log2 P1+P2log2 P2+…P16*log2 P16)

计算NBA季后赛总冠军的夺冠球队的信息熵值,含义是每一个球队的夺冠概率乘以,以2为底这个队夺冠的对数。P1、P2…PN表示哪一支球队的夺冠概率,假设每一个球队夺冠的概率都相等的话,那么这里算出的信息熵值就是4,当然这种情况是不太可能存在的,因为每一个球队的实力不一样。

变量的不确定越大,熵的值也就越大。

决策树的优点:

直观,便于理解,小规模数据集有效

决策树的缺点:

处理连续变量不好;类别较多时,错误增加的比较快;可规模性一般。

使用决策树做预测需要以下过程:

1,收集数据:可以使用任何方法。比如想构建一个相亲系统,我们可以从媒婆那里,或者通过参访相亲对象获取数据。根据他们考虑的因素和最终的选择结果,就可以得到一些供我们利用的数据了。
2,准备数据:收集完的数据,我们要进行整理,将这些所有收集的信息按照一定规则整理出来,并排版,方便我们进行后续处理。
3,分析数据:可以使用任何方法,决策树构造完成之后,我们可以检查决策树图形是否符合预期。
4,训练算法:这个过程也就是构造决策树,同样也可以说是决策树学习,就是构造一个决策树的数据结构。
5,测试算法:使用经验树计算错误率。当错误率达到了可接收范围,这个决策树就可以投放使用了。
6,使用算法:此步骤可以使用适用于任何监督学习算法,而使用决策树可以更好地理解数据的内在含义。

创建决策树算法案例

案例1: 判定鱼类和非鱼类
根据以下 2 个特征,将动物分成两类:鱼类和非鱼类。
特征:

  • 不浮出水面是否可以生存
  • 是否有脚蹼

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

def calcShannonEnt(dataSet):
    # 求list的长度,表示计算参与训练的数据量
    numEntries = len(dataSet)
    # 计算分类标签label出现的次数
    labelCounts = {}
    # the the number of unique elements and their occurrence
    for featVec in dataSet:
        # 将当前实例的标签存储,即每一行数据的最后一个数据代表的是标签
        currentLabel = featVec[-1]
        # 为所有可能的分类创建字典,如果当前的键值不存在,则扩展字典并将当前键值加入字典。每个键值都记录了当前类别出现的次数。
        if currentLabel not in labelCounts.keys():
            labelCounts[currentLabel] = 0
        labelCounts[currentLabel] += 1

    # 对于 label 标签的占比,求出 label 标签的香农熵
    shannonEnt = 0.0
    for key in labelCounts:
        # 使用所有类标签的发生频率计算类别出现的概率。
        prob = float(labelCounts[key])/numEntries
        # 计算香农熵,以 2 为底求对数
        shannonEnt -= prob * log(prob, 2)
    return shannonEnt

选择最好的数据集划分方式

def chooseBestFeatureToSplit(dataSet):
    """chooseBestFeatureToSplit(选择最好的特征)

    Args:
        dataSet 数据集
    Returns:
        bestFeature 最优的特征列
    """
    # 求第一行有多少列的 Feature, 最后一列是label列嘛
    numFeatures = len(dataSet[0]) - 1
    # 数据集的原始信息熵
    baseEntropy = calcShannonEnt(dataSet)
    # 最优的信息增益值, 和最优的Featurn编号
    bestInfoGain, bestFeature = 0.0, -1
    # iterate over all the features
    for i in range(numFeatures):
        # create a list of all the examples of this feature
        # 获取对应的feature下的所有数据
        featList = [example[i] for example in dataSet]
        # get a set of unique values
        # 获取剔重后的集合,使用set对list数据进行去重
        uniqueVals = set(featList)
        # 创建一个临时的信息熵
        newEntropy = 0.0
        # 遍历某一列的value集合,计算该列的信息熵 
        # 遍历当前特征中的所有唯一属性值,对每个唯一属性值划分一次数据集,计算数据集的新熵值,并对所有唯一特征值得到的熵求和。
        for value in uniqueVals:
            subDataSet = splitDataSet(dataSet, i, value)
            # 计算概率
            prob = len(subDataSet)/float(len(dataSet))
            # 计算信息熵
            newEntropy += prob * calcShannonEnt(subDataSet)
        # gain[信息增益]: 划分数据集前后的信息变化, 获取信息熵最大的值
        # 信息增益是熵的减少或者是数据无序度的减少。最后,比较所有特征中的信息增益,返回最好特征划分的索引值。
        infoGain = baseEntropy - newEntropy
        print('infoGain=', infoGain, 'bestFeature=', i, baseEntropy, newEntropy)
        if (infoGain > bestInfoGain):
            bestInfoGain = infoGain
            bestFeature = i
    return bestFeature

创建树的函数代码如下:

def createTree(dataSet, labels):
    classList = [example[-1] for example in dataSet]
    # 如果数据集的最后一列的第一个值出现的次数=整个集合的数量,也就说只有一个类别,就只直接返回结果就行
    # 第一个停止条件:所有的类标签完全相同,则直接返回该类标签。
    # count() 函数是统计括号中的值在list中出现的次数
    if classList.count(classList[0]) == len(classList):
        return classList[0]
    # 如果数据集只有1列,那么最初出现label次数最多的一类,作为结果
    # 第二个停止条件:使用完了所有特征,仍然不能将数据集划分成仅包含唯一类别的分组。
    if len(dataSet[0]) == 1:
        return majorityCnt(classList)

    # 选择最优的列,得到最优列对应的label含义
    bestFeat = chooseBestFeatureToSplit(dataSet)
    # 获取label的名称
    bestFeatLabel = labels[bestFeat]
    # 初始化myTree
    myTree = {bestFeatLabel: {}}
    # 注:labels列表是可变对象,在PYTHON函数中作为参数时传址引用,能够被全局修改
    # 所以这行代码导致函数外的同名变量被删除了元素,造成例句无法执行,提示'no surfacing' is not in list
    del(labels[bestFeat])
    # 取出最优列,然后它的branch做分类
    featValues = [example[bestFeat] for example in dataSet]
    uniqueVals = set(featValues)
    for value in uniqueVals:
        # 求出剩余的标签label
        subLabels = labels[:]
        # 遍历当前选择特征包含的所有属性值,在每个数据集划分上递归调用函数createTree()
        myTree[bestFeatLabel][value] = createTree(splitDataSet(dataSet, bestFeat, value), subLabels)
        # print 'myTree', value, myTree
    return myTree
发布了19 篇原创文章 · 获赞 1 · 访问量 1250

猜你喜欢

转载自blog.csdn.net/weixin_43093289/article/details/83656145