如何实现并应用决策树算法?

本文对决策树算法进行简单的总结和梳理,并对著名的决策树算法ID3(Iterative Dichotomiser 迭代二分器)进行实现,实现采用Python语言,一句老梗,“人生苦短,我用Python”,Python确实能够省很多语言方面的事,从而可以让我们专注于问题和解决问题的逻辑。

根据不同的数据,我实现了三个版本的ID3算法,复杂度逐步提升:

1.纯标称值无缺失数据集

2.连续值和标称值混合且无缺失数据集

3.连续值和标称值混合,有缺失数据集

第一个算法参考了《机器学习实战》的大部分代码,第二、三个算法基于前面的实现进行模块的增加。

决策树简介

决策树算法不用说大家应该都知道,是机器学习的一个著名算法,由澳大利亚著名计算机科学家Rose Quinlan发表。

决策树是一种监督学习的分类算法,目的是学习出一颗决策树,该树中间节点是数据特征,叶子节点是类别,实际分类时根据树的结构,一步一步根据当前数据特征取值选择进入哪一颗子树,直到走到叶子节点,叶子节点的类别就是此决策树对此数据的学习结果。下图就是一颗简单的决策树:

此决策树用来判断一个具有纹理,触感,密度的西瓜是否是“好瓜”。

当有这样一个西瓜,纹理清晰,密度为0.333,触感硬滑,那么要你判断是否是一个“好瓜”,这时如果通过决策树来判断,显然可以一直顺着纹理->清晰->密度<=0.382->否,即此瓜不是“好瓜”,一次决策就这样完成了。正因为决策树决策很方便,并且准确率也较高,所以常常被用来做分类器,也是“机器学习十大算法”之一C4.5的基本思想。

学习出一颗决策树首要考虑一个问题,即 根据数据集构建当前树应该选择哪种属性作为树根,即划分标准?

考虑最好的情况,一开始选择某个特征,就把数据集划分成功,即在该特征上取某个值的全是一类。

考虑最坏的情况,不断选择特征,划分后的数据集总是杂乱无章,就二分类任务来说,总是有正类有负类,一直到特征全部用完了,划分的数据集合还是有正有负,这时只能用投票法,正类多就选正类作为叶子,否则选负类。

所以得出了一般结论: 随着划分的进行,我们希望选择一个特征,使得子节点包含的样本尽可能属于同一类别,即“纯度”越高越好。

基于“纯度”的标准不同,有三种算法:

1.ID3算法(Iterative Dichotomiser 迭代二分器),也是本文要实现的算法,基于信息增益即信息熵来度量纯度

2.C4.5算法(Classifier 4.5),ID3 的后继算法,也是昆兰提出

3.CART算法(Classification And Regression Tree),基于基尼指数度量纯度。

ID3算法简介

信息熵是信息论中的一个重要概念,也叫“香农熵”,香农先生的事迹相比很多人都听过,一个人开创了一门理论,牛的不行。香农理论中一个很重要的特征就是”熵“,即”信息内容的不确定性“,香农在进行信息的定量计算的时候,明确地把信息量定义为随机不定性程度的减少。这就表明了他对信息的理解:信息是用来减少随机不定性的东西。或者表达为香农逆定义:信息是确定性的增加。这也印证了决策树以熵作为划分选择的度量标准的正确性,即我们想更快速地从数据中获得更多信息,我们就应该快速降低不确定性,即减少”熵“。

信息熵定义为:

D表示数据集,类别总数为|Y|,pk表示D中第k类样本所占的比例。根据其定义,Ent的值越小,信息纯度越高。Ent的范围是[0,log|Y|]

下面要选择某个属性进行划分,要依次考虑每个属性,假设当前考虑属性a,a的取值有|V|种,那么我们希望取a作为划分属性,划分到|V|个子节点后,所有子节点的信息熵之和即划分后的信息熵能够有很大的减小,减小的最多的那个属性就是我们选择的属性。

划分后的信息熵定义为:

所以用属性a对样本集D进行划分的信息增益就是原来的信息熵减去划分后的信息熵:

ID3算法就是这样每次选择一个属性对样本集进行划分,知道两种情况使这个过程停止:

(1)某个子节点样本全部属于一类

(2)属性都用完了,这时候如果子节点样本还是不一致,那么只好少数服从多数了

(图片来自网络)

ID3算法实现(纯标称值)

如果样本全部是标称值即离散值的话,会比较简单。

代码:

from math import log
from operator import itemgetter
def createDataSet():            #创建数据集
    dataSet = [[1,1,‘yes‘],
               [1,1,‘yes‘],
               [1,0,‘no‘],
               [0,1,‘no‘],
               [0,1,‘no‘]]
    featname = [‘no surfacing‘, ‘flippers‘]
    return dataSet,featname
def filetoDataSet(filename):
    fr = open(filename,‘r‘)
    all_lines = fr.readlines()
    featname = all_lines[0].strip().split(‘,‘)[1:-1]
    print(featname)
    dataSet = []
    for line in all_lines[1:]:
        line = line.strip()
        lis = line.split(‘,‘)[1:]
        dataSet.append(lis)
    fr.close()
    return dataSet,featname
def calcEnt(dataSet):           #计算香农熵
    numEntries = len(dataSet)
    labelCounts = {}
    for featVec in dataSet:
        label = featVec[-1]
        if label not in labelCounts.keys():
            labelCounts[label] = 0
        labelCounts[label] += 1
    Ent = 0.0
    for key in labelCounts.keys():
        p_i = float(labelCounts[key]/numEntries)
        Ent -= p_i * log(p_i,2)
    return Ent
def splitDataSet(dataSet, axis, value):   #划分数据集,找出第axis个属性为value的数据
    returnSet = []
    for featVec in dataSet:
        if featVec[axis] == value:
            retVec = featVec[:axis]
            retVec.extend(featVec[axis+1:])
            returnSet.append(retVec)
    return returnSet
def chooseBestFeat(dataSet):
    numFeat = len(dataSet[0])-1
    Entropy = calcEnt(dataSet)
    DataSetlen = float(len(dataSet))
    bestGain = 0.0
    bestFeat = -1
    for i in range(numFeat):
        allvalue = [featVec[i] for featVec in dataSet]
        specvalue = set(allvalue)
        nowEntropy = 0.0
        for v in specvalue:
            Dv = splitDataSet(dataSet,i,v)
            p = len(Dv)/DataSetlen
            nowEntropy += p * calcEnt(Dv)
        if Entropy - nowEntropy > bestGain:
            bestGain = Entropy - nowEntropy
            bestFeat = i
    return bestFeat
def Vote(classList):
    classdic = {}
    for vote in classList:
        if vote not in classdic.keys():
            classdic[vote] = 0
        classdic[vote] += 1
    sortedclassDic = sorted(classdic.items(),key=itemgetter(1),reverse=True)
    return sortedclassDic[0][0]
def createDecisionTree(dataSet,featnames):
    featname = featnames[:]              ################
    classlist = [featvec[-1] for featvec in dataSet]  #此节点的分类情况
    if classlist.count(classlist[0]) == len(classlist):  #全部属于一类
        return classlist[0]
    if len(dataSet[0]) == 1:         #分完了,没有属性了
        return Vote(classlist)       #少数服从多数
    # 选择一个最优特征进行划分
    bestFeat = chooseBestFeat(dataSet)
    bestFeatname = featname[bestFeat]
    del(featname[bestFeat])     #防止下标不准
    DecisionTree = {bestFeatname:{}}
    # 创建分支,先找出所有属性值,即分支数
    allvalue = [vec[bestFeat] for vec in dataSet]
    specvalue = sorted(list(set(allvalue)))  #使有一定顺序
    for v in specvalue:
        copyfeatname = featname[:]
        DecisionTree[bestFeatname][v] = createDecisionTree(splitDataSet(dataSet,bestFeat,v),copyfeatname)
    return DecisionTree
if __name__ == ‘__main__‘:
    filename = "D:\\MLinAction\\Data\\西瓜2.0.txt"
    DataSet,featname = filetoDataSet(filename)
    #print(DataSet)
    #print(featname)
    Tree = createDecisionTree(DataSet,featname)
    print(Tree)

解释一下几个函数:

filetoDataSet(filename)  将文件中的数据整理成数据集

calcEnt(dataSet)     计算香农熵

splitDataSet(dataSet, axis, value)     划分数据集,选择出第axis个属性的取值为value的所有数据集,即D^v,并去掉第axis个属性,因为不需要了

chooseBestFeat(dataSet)      根据信息增益,选择一个最好的属性

Vote(classList)        如果属性用完,类别仍不一致,投票决定

createDecisionTree(dataSet,featnames)     递归创建决策树

--------------------------------------------------------------------------------

用西瓜数据集2.0对算法进行测试,西瓜数据集见 西瓜数据集2.0,输出如下:

[‘色泽‘, ‘根蒂‘, ‘敲声‘, ‘纹理‘, ‘脐部‘, ‘触感‘]
{‘纹理‘: {‘清晰‘: {‘根蒂‘: {‘蜷缩‘: ‘是‘, ‘硬挺‘: ‘否‘, ‘稍蜷‘: {‘色泽‘: {‘青绿‘: ‘是‘, ‘乌黑‘: {‘触感‘: {‘硬滑‘: ‘是‘, ‘软粘‘: ‘否‘}}}}}}, ‘稍糊‘: {‘触感‘: {‘硬滑‘: ‘否‘, ‘软粘‘: ‘是‘}}, ‘模糊‘: ‘否‘}}

为了能够体现决策树的优越性即决策方便,这里基于matplotlib模块编写可视化函数treePlot,对生成的决策树进行可视化,可视化结果如下:

由于数据太少,没有设置测试数据以验证其准确度,但是我后面会根据乳腺癌的例子进行准确度的测试的,下面进入下一部分:

有连续值的情况

有连续值的情况如 西瓜数据集3.0 

一个属性有很多种取值,我们肯定不能每个取值都做一个分支,这时候需要对连续属性进行离散化,有几种方法供选择,其中两种是:

1.对每一类别的数据集的连续值取平均值,再取各类的平均值的平均值作为划分点,将连续属性化为两类变成离散属性

2.C4.5采用的二分法,排序离散属性,取每两个的中点作为划分点的候选点,计算以每个划分点划分数据集的信息增益,取最大的那个划分点将连续属性化为两类变成离散属性,用该属性进行划分的信息增益就是刚刚计算的最大信息增益。公式如下:

这里采用第二种,并在学习前对连续属性进行离散化。增加处理的代码如下:

def splitDataSet_for_dec(dataSet, axis, value, small):
    returnSet = []
    for featVec in dataSet:
        if (small and featVec[axis] <= value) or ((not small) and featVec[axis] > value):
            retVec = featVec[:axis]
            retVec.extend(featVec[axis+1:])
            returnSet.append(retVec)
    return returnSet
def DataSetPredo(filename,decreteindex):
    dataSet,featname = filetoDataSet(filename)
    Entropy = calcEnt(dataSet)
    DataSetlen = len(dataSet)
    for index in decreteindex:     #对每一个是连续值的属性下标
        for i in range(DataSetlen):
            dataSet[i][index] = float(dataSet[i][index])
        allvalue = [vec[index] for vec in dataSet]
        sortedallvalue = sorted(allvalue)
        T = []
        for i in range(len(allvalue)-1):        #划分点集合
            T.append(float(sortedallvalue[i]+sortedallvalue[i+1])/2.0)
        bestGain = 0.0
        bestpt = -1.0
        for pt in T:          #对每个划分点
            nowent = 0.0
            for small in range(2):   #化为正类负类
                Dt = splitDataSet_for_dec(dataSet, index, pt, small)
                p = len(Dt) / float(DataSetlen)
                nowent += p * calcEnt(Dt)
            if Entropy - nowent > bestGain:
                bestGain = Entropy-nowent
                bestpt = pt
        featname[index] = str(featname[index]+"<="+"%.3f"%bestpt)
        for i in range(DataSetlen):
            dataSet[i][index] = "是" if dataSet[i][index] <= bestpt else "否"
    return dataSet,featname

主要是预处理函数DataSetPredo,对数据集提前离散化,然后再进行学习,学习代码类似。输出的决策树如下:

有缺失值的情况

数据有缺失值是常见的情况,我们不好直接抛弃这些数据,因为这样会损失大量数据,不划算,但是缺失值我们也无法判断它的取值。怎么办呢,办法还是有的。

考虑两个问题:

1.有缺失值时如何进行划分选择

2.已选择划分属性,有缺失值的样本划不划分,如何划分?

问题1:有缺失值时如何进行划分选择

基本思想是进行最优属性选择时,先只考虑无缺失值样本,然后再乘以相应比例,得到在整个样本集上的大致情况。连带考虑到第二个问题的话,考虑给每一个样本一个权重,此时每个样本不再总是被看成一个独立样本,这样有利于第二个问题的解决:即若样本在属性a上的值缺失,那么将其看成是所有值都取,只不过取每个值的权重不一样,每个值的权重参考该值在无缺失值样本中的比例,简单地说,比如在无缺失值样本集中,属性a取去两个值1和2,并且取1的权重和占整个权重和1/3,而取2的权重和占2/3,那么依据该属性对样本集进行划分时,遇到该属性上有缺失值的样本,那么我们认为该样本取值2的可能性更大,于是将该样本的权重乘以2/3归到取值为2的样本集中继续进行划分构造决策树,而乘1/3划到取值为1的样本集中继续构造。不知道我说清楚没有。

公式如下:

其中,D~表示数据集D在属性a上无缺失值的样本,根据它来判断a属性的优劣,rho(即‘lou‘)表示属性a的无缺失值样本占所有样本的比例,p~_k表示无缺失值样本中第k类所占的比例,r~_v表示无缺失值样本在属性a上取值为v的样本所占的比例。

在划分样本时,如果有缺失值,则将样本划分到所有子节点,在属性a取值v的子节点上的权重为r~_v * 原来的权重。

更详细的解读参考《机器学习》P86-87。

根据权重法修改后的ID3算法实现如下:

from math import log
from operator import itemgetter

def filetoDataSet(filename):
    fr = open(filename,‘r‘)
    all_lines = fr.readlines()
    featname = all_lines[0].strip().split(‘,‘)[1:-1]
    dataSet = []
    for line in all_lines[1:]:
        line = line.strip()
        lis = line.split(‘,‘)[1:]
        if lis[-1] == ‘2‘:
            lis[-1] = ‘良‘
        else:
            lis[-1] = ‘恶‘
        dataSet.append(lis)
    fr.close()
    return dataSet,featname

def calcEnt(dataSet, weight):           #计算权重香农熵
    labelCounts = {}
    i = 0
    for featVec in dataSet:
        label = featVec[-1]
        if label not in labelCounts.keys():
            labelCounts[label] = 0
        labelCounts[label] += weight[i]
        i += 1
    Ent = 0.0
    for key in labelCounts.keys():
        p_i = float(labelCounts[key]/sum(weight))
        Ent -= p_i * log(p_i,2)
    return Ent

def splitDataSet(dataSet, weight, axis, value, countmissvalue):   #划分数据集,找出第axis个属性为value的数据
    returnSet = []
    returnweight = []
    i = 0
    for featVec in dataSet:
        if featVec[axis] == ‘?‘ and (not countmissvalue):
            continue
        if countmissvalue and featVec[axis] == ‘?‘:
            retVec = featVec[:axis]
            retVec.extend(featVec[axis+1:])
            returnSet.append(retVec)
        if featVec[axis] == value:
            retVec = featVec[:axis]
            retVec.extend(featVec[axis+1:])
            returnSet.append(retVec)
            returnweight.append(weight[i])
        i += 1
    return returnSet,returnweight

def splitDataSet_for_dec(dataSet, axis, value, small, countmissvalue):
    returnSet = []
    for featVec in dataSet:
        if featVec[axis] == ‘?‘ and (not countmissvalue):
            continue
        if countmissvalue and featVec[axis] == ‘?‘:
            retVec = featVec[:axis]
            retVec.extend(featVec[axis+1:])
            returnSet.append(retVec)
        if (small and featVec[axis] <= value) or ((not small) and featVec[axis] > value):
            retVec = featVec[:axis]
            retVec.extend(featVec[axis+1:])
            returnSet.append(retVec)
    return returnSet

def DataSetPredo(filename,decreteindex):     #首先运行,权重不变为1
    dataSet,featname = filetoDataSet(filename)
    DataSetlen = len(dataSet)
    Entropy = calcEnt(dataSet,[1 for i in range(DataSetlen)])
    for index in decreteindex:     #对每一个是连续值的属性下标
        UnmissDatalen = 0
        for i in range(DataSetlen):      #字符串转浮点数
            if dataSet[i][index] != ‘?‘:
                UnmissDatalen += 1
                dataSet[i][index] = int(dataSet[i][index])
        allvalue = [vec[index] for vec in dataSet if vec[index] != ‘?‘]
        sortedallvalue = sorted(allvalue)
        T = []
        for i in range(len(allvalue)-1):        #划分点集合
            T.append(int(sortedallvalue[i]+sortedallvalue[i+1])/2.0)
        bestGain = 0.0
        bestpt = -1.0
        for pt in T:          #对每个划分点
            nowent = 0.0
            for small in range(2):   #化为正类(1)负类(0)
                Dt = splitDataSet_for_dec(dataSet, index, pt, small, False)
                p = len(Dt) / float(UnmissDatalen)
                nowent += p * calcEnt(Dt,[1.0 for i in range(len(Dt))])
            if Entropy - nowent > bestGain:
                bestGain = Entropy-nowent
                bestpt = pt
        featname[index] = str(featname[index]+"<="+"%d"%bestpt)
        for i in range(DataSetlen):
            if dataSet[i][index] != ‘?‘:
                dataSet[i][index] = "是" if dataSet[i][index] <= bestpt else "否"
    return dataSet,featname

def getUnmissDataSet(dataSet, weight, axis):
    returnSet = []
    returnweight = []
    tag = []
    i = 0
    for featVec in dataSet:
        if featVec[axis] == ‘?‘:
            tag.append(i)
        else:
            retVec = featVec[:axis]
            retVec.extend(featVec[axis+1:])
            returnSet.append(retVec)
        i += 1
    for i in range(len(weight)):
        if i not in tag:
            returnweight.append(weight[i])
    return returnSet,returnweight

def printlis(lis):
    for li in lis:
        print(li)

def chooseBestFeat(dataSet,weight,featname):
    numFeat = len(dataSet[0])-1
    DataSetWeight = sum(weight)
    bestGain = 0.0
    bestFeat = -1
    for i in range(numFeat):
        UnmissDataSet,Unmissweight = getUnmissDataSet(dataSet, weight, i)   #无缺失值数据集及其权重
        Entropy = calcEnt(UnmissDataSet,Unmissweight)      #Ent(D~)
        allvalue = [featVec[i] for featVec in dataSet if featVec[i] != ‘?‘]
        UnmissSumWeight = sum(Unmissweight)
        lou = UnmissSumWeight / DataSetWeight        #lou
        specvalue = set(allvalue)
        nowEntropy = 0.0
        for v in specvalue:      #该属性的几种取值
            Dv,weightVec_v = splitDataSet(dataSet,Unmissweight,i,v,False)   #返回 此属性为v的所有样本 以及 每个样本的权重
            p = sum(weightVec_v) / UnmissSumWeight          #r~_v = D~_v / D~
            nowEntropy += p * calcEnt(Dv,weightVec_v)
        if lou*(Entropy - nowEntropy) > bestGain:
            bestGain = Entropy - nowEntropy
            bestFeat = i
    return bestFeat

def Vote(classList,weight):
    classdic = {}
    i = 0
    for vote in classList:
        if vote not in classdic.keys():
            classdic[vote] = 0
        classdic[vote] += weight[i]
        i += 1
    sortedclassDic = sorted(classdic.items(),key=itemgetter(1),reverse=True)
    return sortedclassDic[0][0]

def splitDataSet_adjustWeight(dataSet,weight,axis,value,r_v):
    returnSet = []
    returnweight = []
    i = 0
    for featVec in dataSet:
        if featVec[axis] == ‘?‘:
            retVec = featVec[:axis]
            retVec.extend(featVec[axis+1:])
            returnSet.append(retVec)
            returnweight.append(weight[i] * r_v)
        elif featVec[axis] == value:
            retVec = featVec[:axis]
            retVec.extend(featVec[axis+1:])
            returnSet.append(retVec)
            returnweight.append(weight[i])
        i += 1
    return returnSet,returnweight

def createDecisionTree(dataSet,weight,featnames):
    featname = featnames[:]              ################
    classlist = [featvec[-1] for featvec in dataSet]  #此节点的分类情况
    if classlist.count(classlist[0]) == len(classlist):  #全部属于一类
        return classlist[0]
    if len(dataSet[0]) == 1:         #分完了,没有属性了
        return Vote(classlist,weight)       #少数服从多数
    # 选择一个最优特征进行划分
    bestFeat = chooseBestFeat(dataSet,weight,featname)
    bestFeatname = featname[bestFeat]
    del(featname[bestFeat])     #防止下标不准
    DecisionTree = {bestFeatname:{}}
    # 创建分支,先找出所有属性值,即分支数
    allvalue = [vec[bestFeat] for vec in dataSet if vec[bestFeat] != ‘?‘]
    specvalue = sorted(list(set(allvalue)))  #使有一定顺序
    UnmissDataSet,Unmissweight = getUnmissDataSet(dataSet, weight, bestFeat)   #无缺失值数据集及其权重
    UnmissSumWeight = sum(Unmissweight)      # D~
    for v in specvalue:
        copyfeatname = featname[:]
        Dv,weightVec_v = splitDataSet(dataSet,Unmissweight,bestFeat,v,False)   #返回 此属性为v的所有样本 以及 每个样本的权重
        r_v = sum(weightVec_v) / UnmissSumWeight          #r~_v = D~_v / D~
        sondataSet,sonweight = splitDataSet_adjustWeight(dataSet,weight,bestFeat,v,r_v)
        DecisionTree[bestFeatname][v] = createDecisionTree(sondataSet,sonweight,copyfeatname)
    return DecisionTree

if __name__ == ‘__main__‘:
    filename = "D:\\MLinAction\\Data\\breastcancer.txt"
    DataSet,featname = DataSetPredo(filename,[0,1,2,3,4,5,6,7,8])
    Tree = createDecisionTree(DataSet,[1.0 for i in range(len(DataSet))],featname)
    print(Tree)

有缺失值的情况如 西瓜数据集2.0alpha

实验结果:

在乳腺癌数据集上的测试与表现

有了算法,我们当然想做一定的测试看一看算法的表现。这里我选择了威斯康辛女性乳腺癌的数据。

数据总共有9列,每一列分别代表,以逗号分割

1 Sample code number (病人ID)
2 Clump Thickness 肿块厚度
3 Uniformity of Cell Size 细胞大小的均匀性
4 Uniformity of Cell Shape 细胞形状的均匀性
5 Marginal Adhesion 边缘粘
6 Single Epithelial Cell Size 单上皮细胞的大小
7 Bare Nuclei 裸核
8 Bland Chromatin 乏味染色体
9 Normal Nucleoli 正常核
10 Mitoses 有丝分裂
11 Class: 2 for benign, 4 formalignant(恶性或良性分类)

[from Toby]

总共700条左右的数据,选取最后80条作为测试集,前面作为训练集,进行学习。

使用分类器的代码如下:

import treesID3 as id3
import treePlot as tpl
import pickle

def classify(Tree, featnames, X):
    classLabel = "未知"
    root = list(Tree.keys())[0]
    firstGen = Tree[root]
    featindex = featnames.index(root)  #根节点的属性下标
    for key in firstGen.keys():   #根属性的取值,取哪个就走往哪颗子树
        if X[featindex] == key:
            if type(firstGen[key]) == type({}):
                classLabel = classify(firstGen[key],featnames,X)
            else:
                classLabel = firstGen[key]
    return classLabel

def StoreTree(Tree,filename):
    fw = open(filename,‘wb‘)
    pickle.dump(Tree,fw)
    fw.close()

def ReadTree(filename):
    fr = open(filename,‘rb‘)
    return pickle.load(fr)

if __name__ == ‘__main__‘:
    filename = "D:\\MLinAction\\Data\\breastcancer.txt"
    dataSet,featnames = id3.DataSetPredo(filename,[0,1,2,3,4,5,6,7,8])
    Tree = id3.createDecisionTree(dataSet[:620],[1.0 for i in range(len(dataSet))],featnames)
    tpl.createPlot(Tree)
    storetree = "D:\\MLinAction\\Data\\decTree.dect"
    StoreTree(Tree,storetree)
    #Tree = ReadTree(storetree)
    i = 1
    cnt = 0
    for lis in dataSet[620:]:
        judge = classify(Tree,featnames,lis[:-1])
        shouldbe = lis[-1]
        if judge == shouldbe:
            cnt += 1
        print("Test %d was classified %s, it‘s class is %s %s" %(i,judge,shouldbe,"=====" if judge==shouldbe else ""))
        i += 1
    print("The Tree‘s Accuracy is %.3f" % (cnt / float(i)))

训练出的决策树如下:

最终的正确率可以看到:

正确率约为96%左右,算是不差的分类器了。

我的乳腺癌数据见:http://7xt9qk.com2.z0.glb.clouddn.com/breastcancer.txt

至此,决策树算法ID3的实现完毕,下面考虑基于基尼指数和信息增益率进行划分选择,以及考虑实现剪枝过程,因为我们可以看到上面训练出的决策树还存在着很多冗余分支,是因为实现过程中,由于数据量太大,每个分支都不完全纯净,所以会创建往下的分支,但是分支投票的结果又是一致的,而且数据量再大,特征数再多的话,决策树会非常大非常复杂,所以剪枝一般是必做的一步。剪枝分为先剪枝和后剪枝,如果细说的话可以写很多了。

此文亦可见:这里
参考资料:《机器学习》《机器学习实战》通过本次实战也发现了这两本书中的一些错误之处。

lz初学机器学习不久,如有错漏之处请多包涵指出或者各位有什么想法或意见欢迎评论去告诉我:)

时间: 2024-10-22 23:09:22

如何实现并应用决策树算法?的相关文章

scikit-learn决策树算法类库使用小结

参考:http://www.cnblogs.com/pinard/p/6056319.html 之前对决策树的算法原理做了总结,包括决策树算法原理(上)和决策树算法原理(下).今天就从实践的角度来介绍决策树算法,主要是讲解使用scikit-learn来跑决策树算法,结果的可视化以及一些参数调参的关键点. 1. scikit-learn决策树算法类库介绍 scikit-learn决策树算法类库内部实现是使用了调优过的CART树算法,既可以做分类,又可以做回归.分类决策树的类对应的是Decision

Spark机器学习(6):决策树算法

1. 决策树基本知识 决策树就是通过一系列规则对数据进行分类的一种算法,可以分为分类树和回归树两类,分类树处理离散变量的,回归树是处理连续变量. 样本一般都有很多个特征,有的特征对分类起很大的作用,有的特征对分类作用很小,甚至没有作用.如决定是否对一个人贷款是,这个人的信用记录.收入等就是主要的判断依据,而性别.婚姻状况等等就是次要的判断依据.决策树构建的过程,就是根据特征的决定性程度,先使用决定性程度高的特征分类,再使用决定性程度低的特征分类,这样构建出一棵倒立的树,就是我们需要的决策树模型,

决策树算法

利用ID3算法来判断某天是否适合打网球. (1)类别属性信息熵的计算由于未分区前,训练数据集中共有14个实例, 其中有9个实例属于yes类(适合打网球的),5个实例属于no类(不适合打网球), 因此分区前类别属性的熵为: (2)非类别属性信息熵的计算,若先选择Outlook属性. (3)Outlook属性的信息增益为: (4)同理计算出其他3个非类别属性的信息增益,取最大的那个属性作为分裂节点,此例中最大的是Outlook,进而得到如下图所示: (5)上图中,针对sunny中的子训练数据集分支,

R(rattle)实现决策树算法

使用R实现决策树分析 决策树算法也是数据挖掘的核心算法之一,使用树形结构模型进行知识表达.非叶子节点为分支条件,叶子节点为决策条件. 分支算法主要使用的信息增益,这里不再详述.本篇案例主要也是使用weather数据集建立决策树模型,使用Rattle的model工具栏的Tree选项,rattle包的安装如下: > install.packages("rattle") 1.使用rattle建立决策树模型 在data工具栏加载weather数据集后,点击执行按钮,然后转到model工具

算法干货----决策树算法中的熵与信息增益

什么是决策树?为什么要用决策树? 决策树是一种二分树,或是多分数.对于大量数据的细分工作有很大帮助.在日常生活中,决策树的算法可谓是每天都在用.小到用户分类,大到辅助决策.实际上他的使用很多. 至于为什么要用到决策树,个人认为就是因为这种算法简单.代码实现起来主要就是IF-ELSE就可以实现.可这一算法的发展也是从ID3--->C4.5----->C5.0. 它的主要步骤就两个:1,建树 2,剪树 如何建树,就是如何分你的数据,按照哪种特性?比如人口统计数据,是按年龄,身高,体重,学历....

决策树算法原理

转载于:http://www.cnblogs.com/pinard/p/6050306.html (楼主总结的很好,就拿来主义了,不顾以后还是多像楼主学习) 决策树算法在机器学习中算是很经典的一个算法系列了.它既可以作为分类算法,也可以作为回归算法,同时也特别适合集成学习比如随机森林.本文就对决策树算法原理做一个总结,上篇对ID3, C4.5的算法思想做了总结,下篇重点对CART算法做一个详细的介绍.选择CART做重点介绍的原因是scikit-learn使用了优化版的CART算法作为其决策树算法

决策树算法总结

决策树算法实际就是一个不断分割训练数据集使其成为数据子集的过程.这种分类或回归模型成树形结构,这也是其被成为决策树算法的主要原因.决策树算法最主要的问题是如何分裂原始数据集使其数据集包含的类别纯度越来越高,于是前辈们引入了熵和信息增益等概念.下面来总结一下决策树算法的主要步骤: step1.计算目标属性(类别的熵): step2.选择一个属性分裂数据集,也即计算信息增益: step3.根据各属性的信息增益,选择最佳的分类属性,对数据集进行分裂: step4.判断剩余的数据集是否都属于同一类别,以

决策树算法对应的测试函数

华电北风吹 天津大学认知计算与应用重点实验室 修改日期:2015/8/15 决策树是一种特别简单的机器学习分类算法.决策树想法来源于人类的决策过程.举个最简单的例子,人类发现下雨的时候,往往会有刮东风,然后天色变暗.对应于决策树模型,预测天气模型中的刮东风和天色变暗就是我们收集的特征,是否下雨就是类别标签.构建的决策树如下图所示 决策树模型构建过程为,在特征集合中无放回的依次递归抽选特征作为决策树的节点--当前节点信息增益或者增益率最大,当前节点的值作为当前节点分支出来的有向边(实际上主要选择的

机器学习算法之决策树算法

该节主要是把<机器学习实战>书上第三章关于决策树的相关代码照样子实现了一遍.对其中一些内容作了些补充,对比ID3与C45区别,同时下载了一个大样本集实验决策树的准确率. 首先,对于决策树的原理,很多很好的博客值得一看: 从决策树学习谈到贝叶斯分类算法.EM.HMM 决策树算法总结 这两个已经详解了关于决策树的所有,慢慢品读吧. 下面是书上外加添加的部分程序,首先是tree.py里面的程序: import operator from math import log # calculate the