机器学习实战—决策树(python3+spyder)

机器学习实战—决策树(python3+spyder)

本博客记录自己在学习《机器学习实战》决策树的内容,采用python3和spyder:


决策树代码块

计算数据集香农熵代码

# -*- coding: utf-8 -*-
"""
Created on Tue Aug 15 19:54:38 2017

@author: rocky
"""

#机器学习实战第三章 决策树程序
#首先计算整个数据集的香农熵,主要是意义是划分数据集  将无序的数据变得更加有序
from math import log

def calcShannonEnt(dataSet):
    numEntries=len(dataSet) #数据实例总数,即训练数的数目
    labelCounts={}  #创建一个数据字典 以下表明它的键值是最后一列的数值
    for featVec in dataSet:
        currentLabel=featVec[-1]  
        #如果当前键值不存在 扩展字典并将当前键值加入字典,每个键值记录当前类别出现的次数
        if currentLabel not in labelCounts.keys():
            labelCounts[currentLabel]=0
        labelCounts[currentLabel]+=1
    #currentLabel作为键,labelCounts[currentLabel]表示键对应的值,对于下面的简单数据集,labelcounts最终的结果是{'yes':2,'no':3}
    #适用所有类标签的发生频率计算类别出现的概率,利用这个概率计算香农熵
    shannonEnt=0.0
    for key in labelCounts:
        prob=float(labelCounts[key])/numEntries
        shannonEnt-=prob*log(prob,2)
    return shannonEnt

简单数据集代码

上面是求解香农熵的程序,我们可以自己加一个数据集来检测程序的生成,有下列代码:

#加入一个简单数据集
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

对上述数据集而言对上述数据集而言,我们在以第一列或最后一列作为键值时,因为两种分布是一致的,即都是3+2的分布,所以最终的熵都是一样的(0.9709505944546686),但是在以第二列作为键值时,分布是4+1,熵就变不同(0.7219280948873623),以下就可以看出。
在命令窗口输入以下命令测试(以最后一列为键值):

dataSet,labels=creatDataSet()

dataSet
Out[3]: [[1, 1, 'yes'], [1, 1, 'yes'], [1, 0, 'no'], [0, 1, 'no'], [0, 1, 'no']]

calcShannonEnt(dataSet)
Out[4]: (0.9709505944546686, {'no': 3, 'yes': 2})

(以第一列作为键值)在命令行输入以下命令测试

dataSet,labels=creatDataSet()

dataSet
Out[7]: [[1, 1, 'yes'], [1, 1, 'yes'], [1, 0, 'no'], [0, 1, 'no'], [0, 1, 'no']]

calcShannonEnt(dataSet)
Out[8]: (0.9709505944546686, {0: 2, 1: 3})

(以第二列作为键值)在命令行输入以下命令测试

dataSet,labels=creatDataSet()

dataSet
Out[11]: [[1, 1, 'yes'], [1, 1, 'yes'], [1, 0, 'no'], [0, 1, 'no'], [0, 1, 'no']]

calcShannonEnt(dataSet)
Out[12]: (0.7219280948873623, {0: 1, 1: 4})

以上是如何度量数据集的无序程度,下面是划分数据集,我们将对每个特征划分数据集的结果计算一次信息熵,然后判断按照哪个特征划分数数据集是最好的划分方式。例如一堆散点图,我们需要划一条线将它们分成两部分,以下就是相关的代码。

数据集划分代码

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

对上述程序的解释:dataset是数据集,axis是划分数据集的特征(即第几列代表的那个特征),value表示返回值,即代表特征所赋的值。上述程序的意思是将数据集中第axis+1列中满足特征值+value的那一行数据找出来保存。例如,splitDataSet(dataSet,0,1)就是找出dataSet中第1(0+1)列都为1的那些数据,结果是[[1, ‘yes’], [1, ‘yes’], [0, ‘no’]],即前三行数据。这样就把数据分成了两部分。

寻找最好的划分方式代码

循环计算信息熵和splitdataset函数,找到最好的特征划分方式

def chooseBestFeatureToSplit(dataSet):
    numFeatures=len(dataSet[0])-1
    #计算原始数据集信息熵,以便与划分后的数据集进行比较
    baseEntropy=calcShannonEnt(dataSet)
    #假设信息熵差值初值为0,最好的划分特征为最后一列
    bestInfoGain=0.0;bestFeature=-1
    for i in range(numFeatures):#循环特征
        featList=[example[i] for example in dataSet]
        #set()用于删除重复元素
        uniqueVals=set(featList)
        #uniqueVals得到了每一种(i)特征中的取值如(0,1)或者(yes,no)
        newEntropy=0.0
        #新的分类方式的信息熵
        for value in uniqueVals:#遍历第i个特征的取值
            #按照一种特征进行数据集划分,并计算当下的信息熵
            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 createTree(dataSet,labels):
    #取出dataset中的所有类标签,对于原数据集就是['yes', 'yes', 'no', 'no', 'no'],实际上是对于每个分支而言。
    classList=[example[-1] for example in dataSet]
    #如果该分支下全是相同的类别,即都是yes或者都是no,就返回这种分类(类别)
    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)
    for value in uniqueVals:#遍历当前选择特征的所有属性值
        #复制类标签,并将其存储在新列表变量sublabels中   
        subLabels=labels[:]
        #递归调用
        myTree[bestFeatLabel][value]=createTree(splitDataSet(dataSet,bestFeat,value),subLabels)
    return myTree
#构建决策树程序

你可能感兴趣的:(机器学习,python)