分类决策树简介及ID3算法实现

一、定义:
分类决策树模型是一种对实例进行分类的树形结构。

决策树由结点(node) 和有向边(directed edge)组成。结点分为内部结点(internal node)和叶结点(leaf node)。

内部结点表示一个特征或属性;叶结点表示一个类。

二、重要概念:

1、熵 (Entropy):

表示随机变量不确定性的度量。熵越大,随机变量的不确定性就越大。

设: X 是取有限值的离散随机变量,其概率分布是:

P(X=xi)=pi i=1, 2n

则:随机变量 X 的熵定义为: H(X)=i=1npilog pi

2、条件熵

条件熵 H(Y|X) 表示在已知随机变量 X 的条件下,随机变量Y 的不确定性;即随机变量 X 给定的条件下,变量Y 的条件熵。

H(Y|X)=i=1npiH(Y|X=xi)

3、经验熵和经验条件熵

当熵和条件熵中的概率由数据估计(特别是极大似然估计)得到时,所对应的熵和条件熵分别称为经验熵(Empirical entropy)和经验条件熵(empirical conditional entropy)。

4、信息增益

训练集合 D 的经验熵H(D) 与特征 A 给定条件下的经验条件熵H(D|A) 之差:

g(D,A)=H(D)H(D|A)

5、信息增益比

信息增益 g(D,A) 与训练数据集 D 关于特征A 的值的熵 HA(D) 之比,即: gr(D,A)=g(D,A)HA(D)

其中, HA(D)=i=1n|Di||D|log2|Di||D|

ID3 算法选择划分特征的依据是信息增益,倾向于选择特征值较多的特征。
C4.5 算法选择划分特征的依据是信息增益比。

三、算法分析:

清楚决策树停止划分的情况:
1、当前数据集的属性值为空;
2、当前所有样本的类别相同;
3、信息增益小于一定的值。(预剪枝中使用)

算法实现主要包括两大模块:最优划分特征选择和决策树的生成。

最优划分特征选择部分

1、计算信息熵:
输入:数据集(含标签)
输出:信息熵
Entropy=pilog pi

2、划分数据集
输入:数据集、属性(特征)、指定特征值 value
输出:具有该指定特征值的所有数据集
重难点:数据切分,选出指定的数据!首先找到所有属性的值是 value 的样本,然后去除该属性。组成返回数据集!!!在决策树生成部分的步骤e中很重要。

3、选择最佳特征
输入:数据集(含类别标签)、所有属性值
输出:最佳特征
a、计算数据集总的信息熵 Entropy
b、分别遍历每一个属性(特征)(一个属性可能有多个特征值),计算每个属性下的信息增益(包含多个特征值下的信息熵)Em
c、寻找最大的(Entropy - Em),此时即是最大的信息增益,选择的特征既是最佳分类特征。

4、投票表决
输入:数据集
输出:类别标签
统计该数据集下每个类别出现的次数,排序,返回出现次数最多的类别。

决策树的生成(该函数是一个递归的过程)CreateTree

输入:数据集、特征
输出:字典型数据——决策树
a、判断是否满足停止划分的条件
若当前数据集的属性值为空,则投票表决当前样本中最多的类别
若当前所有的样本类别相同,则返回当前数据的类别。

b、寻找当前数据的最佳划分特征
c、将最佳特征作为关键字,保存到字典中
d、从当前的属性集合中删除该最佳特征
e、遍历该最佳划分特征的所有属性值feat,循环调用函数 CreateTree(输入参数为:最佳特征值为feat的所有数据集,去除最佳特征的属性集合)

四、代码注意:
1、生成的决策树用字典保存,并且每个关键字的值是一个字典;
2、生成的决策树可以用 pickle 序列化对象保存;
3、 ID3 算法适用于标称型数据,在函数的输入、输出中,数据类型为 list

五、代码:

#-*- coding:utf-8 -*-
import numpy as np
from numpy import *
import pandas as pd
from math import *
import operator
import pickle              # 使用该模块实现对决策树的保存

# 数据导入
def loadData(fileName):
    dataSet = []
    fr = open(fileName)
    for featVector in fr.readlines():
        lineVector = featVector.strip().split('\t')
        dataSet.append(lineVector)
    return dataSet

def calcuEntropy(myData):     # 计算信息熵
    numSample = len(myData)
    myClassCount = {}
    for featVector in myData:
        theKey = featVector[-1]
        if theKey not in myClassCount:
            myClassCount[theKey] = 0
        myClassCount[theKey] += 1
    myEntropy = 0
    for Keys in myClassCount.keys():
        Px = float(myClassCount[Keys])/numSample
        myEntropy -= Px*log(Px,2)            # 需要导入 math 库
    return myEntropy

# 划分数据集:返回划分好的数据集
def splitDataSet(dataX,FeatureNumber,value):  # 输入:数据集、第i 个特征、该属性的值
    retMat = []
    for featVect in dataX:
        if featVect[FeatureNumber] == value:
            x1 = featVect[:FeatureNumber]
            x2 = featVect[FeatureNumber+1:]
            x1.extend(x2)
            retMat.append(x1)
    #print"retMat", retMat
    return retMat

 # 计算最优特征:计算每个特征下的信息熵,信息熵最大的既是最优特征,返回的数字 i 代表第 i 个特征
def GetBestFeature(dataM):
    BestFeat = -1; LargestInformGain = -1      # 最佳特征、最大信息增益
    theEntropy = calcuEntropy(dataM)
    FeatNumber = len(dataM[0])-1
    for i in range(FeatNumber):
        FeatList = [example[i] for example in dataM]  # 统计每个特征有几个特征值
        FeatUnique = set(FeatList)              # 每个特征中的特征值,计算每个特征值下的信息增益
        NewEntropy = 0.0
        for j in FeatUnique:
            retMat = splitDataSet(dataM,i,j)     # 得到满足条件的数据
            Prob =  len(retMat)/float(len(dataM))
            NewEntropy -= Prob*calcuEntropy(retMat)         # 注意:这里是子数据集的概率x 该数据集的熵
        informGain = theEntropy + NewEntropy
        if informGain > LargestInformGain:
            LargestInformGain = informGain
            BestFeat = i
    return BestFeat

def RoleOfVote(dataM):           # 投票规则:少数服从多数
    lables = [example[-1] for example in dataM]
    lablesCount = {}
    for i in lables:
        if i not in lablesCount.keys():
            lablesCount[i] = 0
        lablesCount[i] += 1
    theSort = sorted(lablesCount.iteritems(),key =operator.itemgetter(1),reverse=True)
    return theSort[0][0]                # 返回出现次数最多的类别标签


# 决策树生成: 首先,判断是否满足停止划分的条件:1、所有的类别标签相同  2、属性值为空
def CreateTree(dataSet,label):
    allLabels = [example[-1] for example in dataSet]
    #print "调用几次"
    if allLabels.count(allLabels[0])==len(allLabels):
        return allLabels[0]
    if(len(dataSet[0])==1):                # 没有属性可以划分时,采用投票规则
        return RoleOfVote(dataSet)
    featNumber = GetBestFeature(dataSet)      # 返回最佳划分的编号
    #print featNumber
    bestFeature = label[featNumber]
    myTree = {bestFeature:{}}
    del(label[featNumber])
    featValue =  [example[featNumber] for example in dataSet]
    uniqueFeatValues = set(featValue)
    for i in uniqueFeatValues:
        subLabels = label[:]
        myTree[bestFeature][i] = CreateTree(splitDataSet(dataSet, featNumber, i), subLabels)
    return myTree

# 对输入样本进行分类
def SampleClassify(inputTree, featLabels, testVec):            # 输入树、属性标签、测试向量
    firstStr = inputTree.keys()[0]                           # 决策树的第一个关键词(第一个划分的属性)
    secondDict = inputTree[firstStr]                          # 决策树每个关键字对应的值也是字典
    featIndex = featLabels.index(firstStr)                  # 得到该特征在属性标签中的编号 K
    key = testVec[featIndex]                                # 得到当前测试向量中第 K 号特征值
    valueOfFeat = secondDict[key]
    if isinstance(valueOfFeat, dict):
        classLabel = SampleClassify(valueOfFeat, featLabels, testVec)
    else:
        classLabel = valueOfFeat
    return classLabel

# 将生成的树保存
def storeTree(inputTree, filename):
    fw = open(filename, 'w')
    pickle.dump(inputTree, fw)
    fw.close()

# 加载保存的树
def loadTree(filename):
    fr = open(filename)
    return pickle.load(fr)

if __name__=="__main__":
    print "hello world"
    dataSet = loadData('lenses.txt')
    labels = ['age','prescript','astigmatic','tearRate']
    slabels = labels[:]              # pyhton 函数中的参数是按照引用方式传递的,为防止labels 改变,复制类标签带入
    myTree = CreateTree(dataSet,slabels)
    print labels
    #storeTree(myTree,"xu")              # 保存决策树
    test = dataSet[0]
    #print test[:-1]
    print"预测结果是:", SampleClassify(myTree,labels,test[:-1])
    print"真是标签是:",test[-1]

参考:
《机器学习实战》
《统计学习方法》 李航

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值