决策树创建

决策树:

优点: 计算复杂度不高,输出结果易于理解,对中间值的缺失不敏感,可处理不相关特征数据。
缺点: 易过拟合

ID3、C4.5、CART树

ID3:
特征选择准则:信息增益
缺失值处理:没有考虑
优缺点:

  1. 不能处理连续数据,只能连续离散化处理
  2. 采用信息增益易偏向取值较多的特征(不如信息增益率)
  3. 缺失值不好处理
  4. 没有剪枝,易过拟合

C4.5:
特征选择准则:信息增益率
缺失值处理:可处理
优缺点:

  1. 产生的规则易于理解,准确率高,实现简单
  2. 对数据多次顺序扫描和排序,效率低
  3. 适合小规模数据集,需将数据放到内存中

CART:
特征选择准则:基尼系数

决策树一般流程

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

以下示例来自于《机器学习实战》,使用 ID3 算法:

ID3 通过计算信息增益来得出哪个特征值用于划分数据集(信息增益最高的特征值就是最好的选择)
信息增益与信息熵:
决策树创建_第1张图片
决策树创建_第2张图片

用Python计算信息熵

from math import log

def createDataSet():                 ## 自定义创建数据集
    dataSet = [[1, 1, 'yes'],
               [1, 1, 'yes'],
               [1, 0, 'no'],
               [0, 1, 'no'],
               [0, 1, 'no']]
    labels = ['no surfacing','flippers']
    #change to discrete values
    return dataSet, labels

def calcShannonEnt(dataSet):
    numEntries = len(dataSet)        ## 计算数据集实例数
    labelCounts = {}
    for featVec in dataSet: #the the number of unique elements and their occurance(为所有可能分类创建字典)
        currentLabel = featVec[-1]       ## 键值为最后一列的数值
        if currentLabel not in labelCounts.keys(): labelCounts[currentLabel] = 0
        labelCounts[currentLabel] += 1    ## 记录每个类别的出现次数
    ##print(labelCounts)    ## 加入一行查看labelCounts字典
    shannonEnt = 0.0
    for key in labelCounts:
        prob = float(labelCounts[key])/numEntries
        shannonEnt -= prob * log(prob,2) #log base 2
    return shannonEnt

Mydata, labels = createDataSet()     ## 调用
calcShannonEnt(Mydata)

输出:

Out[18]: 0.9709505944546686

查看labelCounts字典

{'yes': 2, 'no': 3}

熵越高,数据越乱
我们可以在数据集中添加更多分类观察熵的变化:

Mydata[0][-1] = 'maybe'
Mydata

查看修改后Mydata

[[1, 1, 'maybe'], [1, 1, 'yes'], [1, 0, 'no'], [0, 1, 'no'], [0, 1, 'no']]

重新计算

calcShannonEnt(Mydata)

分类由两个变为三个,熵也随之变大

1.3709505944546687

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

划分数据集

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

按照给定特征划分数据集
def splitDataSet(dataSet, axis, value):
    retDataSet = []              ## 新建列表(Python在函数传递中是列表引用,在函数中的操作会直接影响原列表)
    for featVec in dataSet:
        if featVec[axis] == value:
            reducedFeatVec = featVec[:axis]     #chop out axis used for splitting(切掉用于分割的轴)(python包头不包尾)
            reducedFeatVec.extend(featVec[axis+1:])
            retDataSet.append(reducedFeatVec)   ## 用append添加列表形成没有使用过的特征的新的数据集
    return retDataSet

调用splitDataSet()函数:

splitDataSet(Mydata, 0, 1)

输出:

[[1, 'yes'], [1, 'yes'], [0, 'no']]

来看一下当前Mydata:
决策树创建_第3张图片
splitDataSet(dataSet, axis, value)
splitDataSet(Mydata, 0, 1)返回的是第0个位置的特征值属性为1的集合。

接下里,将遍历整个数据集,循环计算香农熵和splitDataSet()函数,找到最好的特征划分方式。熵计算将会告诉我们如何划分数据集是最好的数据组织方式。

## 选择最好的数据集划分方式
def chooseBestFeatureToSplit(dataSet):
    numFeatures = len(dataSet[0]) - 1      #the last column is used for the labels(最后一列用于标签)
    baseEntropy = calcShannonEnt(dataSet)  ## 计算香农熵
    bestInfoGain = 0.0; bestFeature = -1
    for i in range(numFeatures):        #iterate over all the features(迭代所有功能)
        featList = [example[i] for example in dataSet]#create a list of all the examples of this feature(建个列表把第i个特征值和所有可能值存进去)(相当于按特征值的一列存)
#        print("featList = {}".format(featList))
        uniqueVals = set(featList)       #get a set of unique values(取特征值存在的所有情况的唯一元素值)
        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     #calculate the info gain(计算信息增益); ie reduction in entropy
        if (infoGain > bestInfoGain):       #compare this to the best gain so far(与目前最好的相比)
            bestInfoGain = infoGain         #if better than current best, set to best
            bestFeature = i
    return bestFeature                      #returns an integer(返回整数)(返回最好特征划分的索引值)

chooseBestFeatureToSplit(Mydata)
Out: 0

思路大概是:先算出当前结点的香农熵,然后遍历所有特征值,把遍历当前的特征值 i 的所有值存到新的List(特征值所对应的那一列的所有数值),并提取出唯一值(比如11122,取1,2),嵌套遍历每一个唯一值并用唯一值划分(例如:就是找到该特征值全是1的个数,然后去算熵),累加每个唯一值的信息熵,跳出嵌套循环,求得当前特征值 i 的信息增益,并于当前最大信息增益做判断,保存最大值,并保存当前特征值的序号,最后跳出所有特征值的遍历,返回最大信息增益的特征值位置。

递归构建决策树

## 创建树
def createTree(dataSet,labels):     ## 输入数据集和标签列表(虽然算法本身并不需要标签这个变量,只是为了给出数据明确的定义)
    classList = [example[-1] for example in dataSet]    ## 包含了所有类别标签
    if classList.count(classList[0]) == len(classList):   ## 若当前数据集所有标签都一致,则停止划分
        return classList[0]                  #stop splitting when all of the classes are equal
    if len(dataSet[0]) == 1: #stop splitting when there are no more features in dataSet
        return majorityCnt(classList)    ## 若使用完了所有的特征但仍没有划分完成,调用majorityCnt()函数,返回出现次数最多的类别
    ## 开始创建树
    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[:]       #copy all of labels, so trees don't mess up existing labels
        myTree[bestFeatLabel][value] = createTree(splitDataSet(dataSet, bestFeat, value),subLabels)
    return myTree  

调用

mytree = createTree(Mydata, labels)
mytree

输出:

{'no surfacing': {0: 'no', 1: {'flippers': {0: 'no', 1: 'yes'}}}}

包含了好多嵌套字典。。。。。。
嵌套过程大概是这样的:
决策树创建_第4张图片

在Python中使用Matplotlib注解绘制树形图

你可能感兴趣的:(Python,python,机器学习,决策树,随机森林)