手撕《机器学习实战》3-----决策树算法

花了一天多时间,终于弄明白了决策树的完整代码,整个构树过程明白了。感觉蛮开心,爽歪歪。
下面写一下我的学习步骤,我们可以一起来~~
我尽量写明白每一步我的学习路径,用了哪些资料以及配合弄懂代码的方法。请多提提意见,谢谢各位,也欢迎粉我。

1. 首先我们用函数自己创建一个数据集

#创建数据集
def creatDataSet():
    dataSet = [[1,1,'yes'],
              [1,1,'yes'],
              [1,0,'no'],
              [0,1,'no'],
              [0,1,'no']]
    labels = ['no surfacing', 'flippers']
    return dataSet,labels
dataSet,labels=creatDataSet()
labels
['no surfacing', 'flippers']

2.计算给定数据集的香农熵

这部分是在用代码计算香农熵公式,即用代码写公式并计算结果

def calcShannonEnt(dataSet):
    #数据集行数
    numEntires = len(dataSet)  
    #创建空字典记录yes  or  no的次数
    labelCounts = {}   
    #开始用循环语句统计,该思路和本书KNN算法是一样的
    for featVec in dataSet:                            
        currentLabel = featVec[-1]                    
        if currentLabel not in labelCounts.keys():    
            labelCounts[currentLabel] = 0
        labelCounts[currentLabel] += 1    
    #初始值是0,然后根据公式,用循环语句开始计算
    shannonEnt = 0.0                                
    for key in labelCounts: 
        #公式计算部分
        prob = float(labelCounts[key]) / numEntires 
        from math import log
        shannonEnt -= prob * log(prob, 2)            
    return shannonEnt                      

print(calcShannonEnt(dataSet))
0.9709505944546686

3.计算信息增益(信息增益的公式可以手抄一遍,再看代码。便于理解)

这段代码其实挺难理解的,第一个函数我是通过把数据集手写到草稿纸上,逐行代码去理解的。就知道这个函数到底是在干什么了,然后数据集最终变成啥样。知道在干嘛后,很有可能你还不明白为什么要有这步操作?那么就要看第二个函数了,看完就明白了。
两个函数合在一起,是在计算信息增益。同样,数据集手写在草稿纸上,以及公式(一定得写公式,不然可能代码看着看着就糊涂了),一步一步根据代码计算。

#这个函数后面用的上,先看懂代码。后一个函数会让你看懂它的真正作用。
def splitDataSet(dataSet, axis, value):
    retDataSet = []
    for featVec in dataSet:
        if featVec[axis] == value:
            reducedFeatVec = featVec[:axis]     
            reducedFeatVec.extend(featVec[axis+1:])
            retDataSet.append(reducedFeatVec)
    return retDataSet


#print(splitDataSet(dataSet,1,0))
#该函数选取最优的特征,并返回最优特征的索引值
def chooseBestFeatureToSplit(dataSet):
    numFeatures = len(dataSet[0]) - 1                       #特征数量
    baseEntropy = calcShannonEnt(dataSet)                   #计算数据集的香农熵
    bestInfoGain = 0.0                                     #信息增益
    bestFeature = -1                                       #最优特征的索引值,-1是随便设置的值,便于后续更新值
    for i in range(numFeatures):                           #遍历所有特征
        featList = [example[i] for example in dataSet]     #获取dataSet的第i个所有特征
        uniqueVals = set(featList)                            #创建set集合{},元素不可重复
        newEntropy = 0.0                                      #经验条件熵
        for value in uniqueVals:                             #计算信息增益
            subDataSet = splitDataSet(dataSet, i, value)         #subDataSet划分后的子集
            prob = len(subDataSet) / float(len(dataSet))           #计算子集的概率
            newEntropy += prob * calcShannonEnt(subDataSet)     #根据公式计算经验条件熵
        infoGain = baseEntropy - newEntropy                     #信息增益
        #print("第%d个特征的增益为%.3f" % (i, infoGain))            #打印每个特征的信息增益
        if (infoGain > bestInfoGain):                             #计算信息增益
            bestInfoGain = infoGain                             #更新信息增益,找到最大的信息增益
            bestFeature = i                                     #记录信息增益最大的特征的索引值
    return bestFeature          

 print("最优特征索引值:" + str(chooseBestFeatureToSplit(dataSet)))
最优特征索引值:0

4.递归构建决策树,用了递归函数

递归函数这一块,其实我理解了很久,想来想去,最后终于明白了。可能我是菜鸡一枚~~。可以辅助西瓜书的决策树那部分例子,然后去看着树。边看树边计算公式边理解代码(三合一,不然很难理解),你会明白到底如何一步一步建造出整个树的。这部分代码是重中之重,因为整个决策树的建树过程就在此,你所看到的决策树,真正的形成过程全部展现无疑。决策树完全无黑盒子过程,它是一个白盒。所以咱们一定要真的弄明白,不然可惜了这个算法!!
真的,想出代码的本书作者实在是太牛掰了!

#这个函数是在下一个函数里面第二个停止条件时候,用上的
def majorityCnt(classList):
    classCount={}
    for vote in classList:                      #统计classList中每个元素出现的次数
        if vote not in classCount.keys():      
            classCount[vote] = 0
        classCount[vote] += 1
    sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True)
    return sortedClassCount[0][0]              #返回classList中出现次数最多的元素

def createTree(dataSet, labels):
    classList = [example[-1] for example in dataSet]     #取出所有分类标签,yes or no
    if classList.count(classList[0]) == len(classList):
        return classList[0]                   #如果类别完全相同,则停止继续划分。这是第一个停止条件
    if len(dataSet[0]) == 1:                  #如果特征都遍历完了,还是没有划分的很纯净,那么就取数量多一点的那个类别。这是第二个停止条件
        return majorityCnt(classList)         #stop splitting when there are no more features in dataSe
    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(dataSet,labels)
myTree

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

5.使用决策树分类,依旧用了一个递归函数

def classify(inputTree, featLabels, testVec):
    firstStr = next(iter(inputTree))                                                        #获取决策树结点
    secondDict = inputTree[firstStr]                                                        #下一个字典
    featIndex = featLabels.index(firstStr)                                               
    for key in secondDict.keys():
        if testVec[featIndex] == key:
            if type(secondDict[key]).__name__ == 'dict':
                classLabel = classify(secondDict[key], featLabels, testVec)
            else: 
                classLabel = secondDict[key]
    return classLabel
labels=['no surfacing', 'flippers']
print(classify(myTree, labels, [0,1]))
print(classify(myTree, labels, [0,0]))
print(classify(myTree, labels, [1,1]))
print(classify(myTree, labels, [1,0]))
no
no
yes
no

参考:
1.《西瓜书》决策树部分,公式,数据,树图等等。建议这部分都看吧
2. B站视频,【小甲鱼】零基础入门学习Python。我主要参考了23-25节视频,讲的递归函数。

猜你喜欢

转载自blog.csdn.net/qq_41205464/article/details/85011099