美文网首页
第4章 决策树

第4章 决策树

作者: Aptitude | 来源:发表于2018-09-30 12:27 被阅读0次

1. 引言

决策树(decision tree)学习的目的是产生一课泛化能力强的树,在非叶节点进行属性测试,每个叶结点对应了一个判定测试序列。其基本流程遵循简单且直观的“分而治之”(divide-and-conquer)的策略。

2. 算法流程

决策树算法流程
根据算法流程图可知,决策树的生成就是一个递归的过程,三种情形会导致递归返回。其中,最重要的一个步骤是从属性集A中选择一个最优划分属性,下面介绍如何选择最优划分属性。

3. 划分选择

3.1信息增益

在进行介绍之前,首先说明一下熵的概念。

  • 热力学熵:熵是一种不可逆性。温度只能自发的从高到低,熵增意味着所能使用的能量的减少。从一种有序的状态到一种无序的状态,无法利用的能量越来越多。老子有一句话,“天之道,损有余而补不足”。
  • 信息熵:反应了对一件事物的了解程度。了解的越少,熵越大。
  • 构象熵:反映了对一件东西的不确定性。越不确定,熵越大。
    进入正题……
    信息熵(information entropy)是度量样本集合纯度最常用的一种指标。
    信息熵和信息增益
  • Ent(D)的值越小,则D的纯度越高。
  • 信息增益的值越大,意味着使用属性a进行划分获得的“纯度提升”越大。则选择最优属性时计算出信息增益,然后选择出增益最大的那一个属性作为这个结点的划分属性。

3.2 增益率

信息增益对取值较多的属性有所偏好,这样导致的结果是分支过多,而每个分支结点的样本数较少。因此提出了增益率的概念,C4.5决策树算法使用增益率进行度量。

增益率
增益率可能对取值较少的属性友好。所以,作为一个权衡,使用一个
启发式:先从候选划分属性中找出信息增益高于平均水平的属性,再从中选择增益率最高的。

3.3 基尼指数

CART决策树使用基尼指数(Gini index)来选择划分属性。

基尼值和基尼指数
Gini(D)表示从数据集D中随机抽取两个样本,其类别标记不一致的概率。Gini(D)越小,数据集的纯度越高。
在进行选择时,选择基尼指数最小的作为最优划分属性。

4. 防过拟合——剪枝处理

  • 预剪枝(prepruning):指在决策树生成过程中,对每个结点在划分前先进进行估计,若当前结点的划分不能带来决策树泛化性能的提升,则停止划分并将当前结点标记为叶结点。
    预剪枝虽然降低了过拟合的风险,但带来了欠拟合的可能。基于“贪心”本质禁止展开分支,可能会导致暂时不能提高泛化性能但对之后有帮助的展开无法继续。
  • 后剪枝(postpruning):先从训练集中训练出一棵完整的决策树,然后自底向上地对非叶结点进行考察,若将该结点对应的子树替换为叶结点能带来决策树泛化性能的提升,则将该子树替换为叶结点。
    后剪枝带来的欠拟合风险很小,泛化性能优于预剪枝。但是,由于后剪枝是在决策树生成之后进行操作,因此训练时间开销比未剪枝决策树和预剪枝决策树大得多。
    采用“留出法”(留出一部分数据集用来验证能否带来泛化性能的提升),用信息增益进行最优划分属性的选择。

5. 连续与缺失值

5.1 连续值

简言之,就是寻找连续属性的最佳分割点,使取该点时获得的信息熵是最大的。


连续属性的处理

5.2 缺失值的划分

  1. 在属性值缺失的情况下进行划分属性的选择
    在进行最优划分属性的选择时,增加未缺失样本的比重,用未缺失样本计算类、属性的比重。


    属性值缺失时的处理
  2. 划分时属性值缺失
    将该样本划分到所有子结点中,但是所占的比重不同,这个比重根据完整值样本中的比重决定。


    属性值缺失的划分

6. 多变量决策树

决策树的分类边界由若干与坐标轴平行的分段组成。

决策树分类边界
每次划分只对应一个属性的选择会有很大的开销,多面量决策树(multivariate decision tree)进行了简化。非叶结点不止针对一个属性,而是对属性进行线性组合,构建一个线性组合分类器。比如通过OCI贪心的寻找到每个属性的最有权值,在局部优化的基础上对分类边界进行随机扰动以找到更好的边界。或者使用最小二乘法感知机树等构造神经网络的方法。
多变量决策树
另外,决策树的学习也可以使用增量学习,接收到新样本之后在已学得的样本上进行调整,主要机制是通过调整分支路径上的划分属性次序对树部分进行重构。

7. 基于决策树进行西瓜好坏的分类

  • 数据来源:西瓜书上的西瓜数据

A.加载数据集

def loadExcel(dataPath):
    #返回的数据类型为dataFrame,导入的数据是存在标题行的
    ori_data = pd.read_excel(dataPath)
    #转化为numpy的展示方式,转化之后第一行消失
    ori_data = ori_data.values
    #增加标签栏
    label = ["编号","色泽","根蒂","敲声","纹理","脐部","触感","密度","含糖率","好瓜"]
    label_exist = [0,0,0,0,0,0,0,0,0,0]
    temp_data = []
    for i in range(len(ori_data)):
        temp_data.append([])
        for j in range(len(label)):
            temp_data[i].append(ori_data[i][j])
    #将数据转化到列表中
    ori_data = temp_data
    return ori_data, label,label_exist
数据集

B.创建树模型

这是主函数

if __name__ == "__main__":
    train_path = "/home/stardream/DDML/MachineLearning/dataSet/watermelon3.0.xlsx"
    #加载表格中的数据
    dataSet, labels,l_exist = loadExcel(train_path)
    #创建树模型
    decisionTree = createTree(dataSet,labels,l_exist)
    print(decisionTree)

C.createTree()

这是中间涉及的相关函数。

def mostClass(result):
    #创建一个字典
    className = {}
    for name in result:
        #字典中的键值不存在,增加一个新的
        if name not in className.keys():
            className[name] = 0
        className[name] += 1
    #依据键值对进行排序
    sortedClass = sorted(className.iteritems(), reverse = True)
    #返回最多的类别
    return sortedClass[0][0]

def calEnt(ori_data):
    result = {}
    #得带对应类别的数目
    for data in ori_data:
        if data[-1] not in result.keys():
            result[data[-1]] = 0
        result[data[-1]]  += 1
    Ent = 0.0
    #依据公式进行熵的计算
    for key in result:
        Pk = float(result[key]/len(ori_data))
        Ent -= Pk* math.log(Pk,2)
    return Ent

#若使用属性划分,则划分出来的子集整理为小数据集
def spiltSet(value, i, ori_data, seq):
    sub_set = []
    if i==7 or i==8:
        if seq == 0:
            for j in ori_data:
                if j[i] < value:
                    temp = j[:i]
                    temp.extend(j[i + 1:])
                    sub_set.append(temp)
        elif seq == 1:
            for j in ori_data:
                if j[i] >= value:
                    temp = j[:i]
                    temp.extend(j[i + 1:])
                    sub_set.append(temp)
    else:
        # 对应属性下的数据集
        for j in ori_data:
            if j[i] == value:
                temp = j[:i]
                temp.extend(j[i + 1:])
                sub_set.append(temp)

    return sub_set

def continue_value(sum_en,index,ori_data):
    conGain = 0.0
    classEnt = 0.0
    bestGain =0.0
    conFlag = 0
    feature = [example[index] for example in ori_data]
    sorted(feature)
    for i in range(len(feature)-1):
        midVal = float((feature[i]+feature[i+1])/2.0)
        for j in range(2):
            subSet = spiltSet(midVal,index,ori_data,j)
            # 总增益
            classEnt -= float(calEnt(subSet) * len(subSet) / len(ori_data))
        conGain = sum_en + classEnt
        if bestGain < conGain:
            bestGain = conGain
            conFlag = midVal
    return conGain, conFlag

def gain_choose(ori_data):
    #计算熵
    sum_enropy = calEnt(ori_data)
    temp_enropy = 0.0
    flag2 = -1
    #跳过编号,从第1个开始。因为编号的增益是最大的,使用编号之后算法停止,然而类别太多
    #不考虑最后一列的好瓜列表
    for i in range(1,len(ori_data[0])-1):
        feature = [example[i] for example in ori_data]
        feature = set(feature)
        classEnt = 0.0
        if i==7 or i==8:
            Gain_data, conFlag = continue_value(sum_enropy,i,ori_data)
        else:
            for value in feature:
                 #将属性的每一个值进行计算对应的增益
                 subSet = spiltSet(value, i, ori_data,-1)
                 #总增益
                 classEnt -= float(calEnt(subSet)*len(subSet)/len(ori_data))
            Gain_data = sum_enropy + classEnt
        #选出最大的增益
        if Gain_data>temp_enropy:
            flag = i;
            temp_enropy = Gain_data
            print("2flag")
            print (flag)
            print(flag2)
        if flag ==7 or flag == 8:
            flag2 = conFlag
    #返回最大增益属性
    return flag,flag2

#构建决策树
def createTree(ori_data,label,lEx):
    subLabels = []
    print(label)
    #每一组数据的结果保存在temp_result中
    temp_result = [result[-1] for result in ori_data]
    #如果只有一种结果,那么直接返回这一种结果,无需继续构建决策树
    if len(set(temp_result)) == 1:
        return temp_result [0]
    #若每组数据中的属性值都相同,则无须继续构建,直接返回所占比重最大的结果;或者没有属性值是无须构建
    #取除结果以外的数据进行对比
    temp_result2 = [result[:len(label)-1] for result in ori_data]
    flag = 1
    for i in range(len(temp_result2)):
        for j in range(len(temp_result2[0])):
            if temp_result2[i][j] != temp_result2[i+1][j]:
                flag=0
                break;
        if flag == 0:
            break;
    if flag == 1 or len(ori_data[0]) == 1:
        return mostClass(temp_result)
    #计算增益,返回增益最大的对应的属性
    best_Feature,flag2 = gain_choose(ori_data)
    bestLabel = label[best_Feature]
    decision_tree = {bestLabel:{}}
    lEx[best_Feature] = 1
    #del(label[best_Feature])
    print(bestLabel,flag2)
    if best_Feature == 7 or best_Feature==8:
        for j in range(2):
            for p in range(len(lEx)):
                if lEx[p] == 0:
                    subLabels.append(label[p])
            #递归进行划分
            decision_tree[bestLabel][flag2] = createTree(spiltSet(flag2, best_Feature, ori_data, j), subLabels, lEx)
    else:
        featValues = [example[best_Feature] for example in ori_data]
        # 得到属性下的各个值
        uniqueVals = set(featValues)
        for value in uniqueVals:
            for p in range(len(lEx)):
                if lEx[p] == 0:
                    subLabels.append(label[p])
            #递归进行划分
            decision_tree[bestLabel][value] = createTree(spiltSet(value, best_Feature, ori_data,-1), subLabels, lEx)
    return decision_tree

数据集在这里

编号  色泽  根蒂  敲声  纹理  脐部  触感  密度  含糖率 好瓜    
1   青绿  蜷缩  浊响  清晰  凹陷  硬滑  0.697   0.46    是
2   乌黑  蜷缩  沉闷  清晰  凹陷  硬滑  0.774   0.376   是
3   乌黑  蜷缩  浊响  清晰  凹陷  硬滑  0.634   0.264   是
4   青绿  蜷缩  沉闷  清晰  凹陷  硬滑  0.608   0.318   是
5   浅白  蜷缩  浊响  清晰  凹陷  硬滑  0.556   0.215   是
6   青绿  稍蜷  浊响  清晰  稍凹  软粘  0.403   0.237   是
7   乌黑  稍蜷  浊响  稍糊  稍凹  软粘  0.481   0.149   是
8   乌黑  稍蜷  浊响  清晰  稍凹  硬滑  0.437   0.211   是
9   乌黑  稍蜷  沉闷  稍糊  稍凹  硬滑  0.666   0.091   否
10  青绿  硬挺  清脆  清晰  平坦  软粘  0.243   0.267   否
11  浅白  硬挺  清脆  模糊  平坦  硬滑  0.245   0.057   否
12  浅白  蜷缩  浊响  模糊  平坦  软粘  0.343   0.099   否
13  青绿  稍蜷  浊响  稍糊  凹陷  硬滑  0.639   0.161   否
14  浅白  稍蜷  沉闷  稍糊  凹陷  硬滑  0.657   0.198   否
15  乌黑  稍蜷  浊响  清晰  稍凹  软粘  0.36    0.37    否
16  浅白  蜷缩  浊响  模糊  平坦  硬滑  0.593   0.042   否
17  青绿  蜷缩  沉闷  稍糊  稍凹  硬滑  0.719   0.103   否

代码放在(决策树)[https://github.com/RuiDream/simpleAl],很遗憾运行不正确,原因是加入了连续值的处理,而没有处理好。如果把连续值的那一部分去了对离散值来说是对的,本想改一改,一个国庆假期间隔太久,改不动了。意思到就行,以后不能写这样的半吊子代码了。

相关文章

  • 相亲可以用决策树?

    2019年日更第225天 决策、管理、思考 01决策树:如何用决策树来选择相亲对象 什么是决策树?决策树就是一种把...

  • 【机器学习实战】第3章 决策树(Decision Tree)

    第3章 决策树 决策树 概述 决策树(Decision Tree)算法主要用来处理分类问题,是最经常使用的数据挖掘...

  • 机器学习实战(笔记):第 3 章 决策树

    第 3 章 决策树 [TOC] 本章内容 决策树简介 在数据集中度量一致性 使用递归构造决策树 使用 Matplo...

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

    本文来自之前在Udacity上自学机器学习的系列笔记。这是第6篇,介绍了监督学习中的决策树模型。 决策树 决策树是...

  • 读书笔记:决策工具

    2019年日更第133天 决策树:如何用决策树选择相亲对象 管理就是决策。 决策树就是一种把决策节点画成树的复制决...

  • 机器学习6-决策树

    一. 决策树概述 1.1 什么是决策树 决策树输入: 测试集决策树输出: 分类规则(决策树) 1.2 决策树算法概...

  • 决策树

    1、决策树 决策树学习通常包括3个步骤: 特征选择。 决策树生成。 决策树剪枝。 决策树的学习目标是:根据给定的训...

  • 决策树

    决策树 决策树模型与学习 特征选择 决策树的生成 决策树的剪枝 CART 算法 决策树模型呈树形结构,在分类问题中...

  • 机器学习实战 - ApacheCN

    第 1 章: 基础知识第 2 章: K近邻算法第 3 章: 决策树算法第 4 章: 朴素贝叶斯第 5 章: 逻辑斯...

  • 决策树算法总结

    目录 一、决策树算法思想 二、决策树学习本质 三、总结 一、决策树(decision tree)算法思想: 决策树...

网友评论

      本文标题:第4章 决策树

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