机器学习:IDE3决策树(原理+python实现)

为君一笑 提交于 2020-02-19 12:08:45

不管对决策树的知识了解有多少,这次通过一个简单的例子来就能够了解它的原理和明白实现的方法。

实际场景

对于一个眼科医生而言,当面对病人是否需要佩戴隐形眼镜时,只需要通过几个问题就能够判断出病人是需要带软,硬还是不能带隐形眼镜。因此我们的数据集就是病人的四个特征(‘age’, ‘prescript’, ‘astigmatic’, 'tearRate’)的组合以及对应的结论(‘soft’,‘hard’,‘no lenses’),获取数据点击这里,提取码tvj6
先加载数据,如下:

# 加载数据
def loadData():
    bigString = open(r'路径\lenses.txt', 'r')
    dataMat = []
    for line in bigString:
        curLine = line.strip().split('\t')
        dataMat.append(curLine)
    return dataMat

那么接下来看看关于这个问题的决策树是怎样的一个结构。
在这里插入图片描述
可以看出,医生通过不断地问一些问题,最后对病人做出结论,那么决策树的构建过程,实际上就是不断寻找最佳特征的过程,使得数据集每一个样本都能够在决策树上体现出来,对于IDE3决策树而言,指的就是用IDE3方法来筛选出最佳特征,关于IDE的计算方法,这里不再详细去讲,把重点更多地放在决策树的实现上。因为是用python实现,那么首先一个问题就是,决策树在python里面是一个怎样的数据结构?

  • 拥有多个节点的决策树在python里面是一个拥有多层的字典结构,相当于字典里面套字典。

那么该如何构建?
先搭外层再搭里层。构建的顺序可以理解为,在最开始的时候,根据一个特征进行划分,形成了左右两个分支,然后每个分支各自筛选特征再构建分支,以此类推,重复这个过程直到无法形成分支。这个过程反映在python里面就成为了:

  • 先创建一个字典,其键为父节点,值为左右两个子节点,如果子节点能够继续划分那么这个值就变成了字典结构,以此类推,决策树在python里面成为了内嵌式的多层字典结构
    其具体的实现方式如下:
# 输入数据和特征标签,数据格式为多行多列,行指数据个数,列是数据特征,最后一列是数据的类别
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: {}}
    # 删除
    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

对于决策树构建函数中其他涉及的函数,具体如下:

# 计算IDE
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
    shannonEnt = 0.0
    for key in labelCounts:
        prob = float(labelCounts[key]) / numEntries
        shannonEnt -= prob * log(prob, 2)  # log base 2
    return shannonEnt

# 将根据特征和特征值数据集划分
def splitDataSet(dataSet, axis, value):
    retDataSet = []
    for featVec in dataSet:
        if featVec[axis] == value:
            reducedFeatVec = featVec[:axis]  # chop out axis used for splitting
            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]
        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
def majorityCnt(classList):
    classCount = {}
    for vote in classList:
        if vote not in classCount.keys(): classCount[vote] = 0
        classCount[vote] += 1
    sortedClassCount = sorted(classCount.iteritems(), key=operator.itemgetter(1), reverse=True)
    return sortedClassCount[0][0]

根据数据集进行构造决策树,输出决策树的结构:
在这里插入图片描述
当决策树构建好以后,可以输入四个特征,来决策出结论,其实现方式如下:

def classify(inputTree, featLabels, testVec):
    firstStr = inputTree.keys()[0]
    secondDict = inputTree[firstStr]
    featIndex = featLabels.index(firstStr)
    key = testVec[featIndex]
    valueOfFeat = secondDict[key]
    if isinstance(valueOfFeat, dict):
        classLabel = classify(valueOfFeat, featLabels, testVec)
    else:
        classLabel = valueOfFeat
    return classLabel

总结

这一次我们重点在于IDE决策树的构造上,了解决策树在python是一个嵌套式的多层字典结构,通过递归来实现对决策树的构造,至于IDE的计算公式并未讲解,有时间再补充吧。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!