《统计学习方法》李航_学习笔记_第5章_决策树

第5章 决策树

《统计学习方法》李航 第五章 决策树
《机器学习实战》 第3章 决策树

决策树:
1.树形结构(内部节点表示一个特征或属性,叶节点表示一个类)
2.if-then规则的集合(决策树上所有根节点到叶节点路径的集合)互斥且完备
3.定义在特征空间与类空间上的条件概率分布。(将特征空间划分为互不相交的单元,并在每个单元定义一个类的概率分布,将节点实例归为条件概率最大的一类)

优点:模型具有可读性、分类速度快、可同时处理类别和数值(离散化处理)数据

决策树学习
· 本质:从训练集中归纳出一组分类规则,使它与训练数据矛盾较小,且具有良好的泛化能力。(训练集估计条件概率模型)
· 策略:损失函数最小化(损失函数通常为:正则化的极大似然函数)
· 算法:递归地选择最优特征,并根据该特征对训练数据集进行分割,使得对各个子数据集有一个最好的分类过程
<启发式算法>
(1)特征选择
(2)决策树的生成 <模型的局部选择,只考虑局部最优>
(3)决策树的剪枝 <模型的全局选择,全局最优>

  • 特征选择
    根据特征的分类能力选择最优特征
    准则:信息增益(ID3)、信息增益率(C4.5)


  • 随机变量不确定性的度量
    设X是一个取有限个值的离散随机变量,其概率分布为
    P ( X = x i ) = p i , i = 1 , 2 , . . . , n P(X=x_i)=p_i,i=1,2,...,n P(X=xi)=pi,i=1,2,...,n
    则随机变量X的熵定义为
    H ( p ) = H ( X ) = − ∑ i = 1 n p i l o g p i H(p)=H(X)=-\sum_{i=1}^np_ilog p_i H(p)=H(X)=i=1npilogpi

· 对同一个随机变量,当其概率分布为均匀分布时,不确定性最大,熵最大
· 对有相同概率分布的不同随机变量,取值最多的随机变量,熵最大

经验熵:熵中的概率由数据估计(特别是极大似然估计)得到

信息增益:得知特征X的信息而使得类Y的信息不确定性减少的程度
偏向于选择取值较多的特征 (->可添加罚项)
信息增益的算法:
(1)数据集D的经验熵H(D)
H ( D ) = − ∑ k = 1 K ∣ C k ∣ ∣ D ∣ l o g ∣ C k ∣ ∣ D ∣ H(D)=-\sum_{k=1}^K\frac{|C_k|}{|D|}log \frac{|C_k|}{|D|} H(D)=k=1KDCklogDCk
(2)计算特征A对数据集D的经验条件熵H(D|A)
H ( D ∣ A ) = − ∑ i = 1 n ∣ D i ∣ ∣ D ∣ H ( D i ) H(D|A)=-\sum_{i=1}^n\frac{|D_i|}{|D|}H(D_i) H(DA)=i=1nDDiH(Di)
(3)计算信息增益
g ( D , A ) = H ( D ) − H ( D ∣ A ) g(D,A)=H(D)-H(D|A) g(D,A)=H(D)H(DA)

ID3 相当于用极大似然法进行概率模型的估计

Python 代码


from math import log
import operator

#计算信息熵
def calcShannonEnt(dataSet):
    #获取数据集的行数
    numEntries=len(dataSet)
    #设置字典的数据结构
    labelCounts={}
    #提取数据集每一行的特征向量
    for featVec in dataSet:
        #获取特征向量最后一列的标签
        currentLabel=featVec[-1]  #-1表示输出数组的最后一个元素
        #若标签不在字典的关键字中
        if currentLabel not in labelCounts.keys():
            #将该标签-0键值存入字典
            labelCounts[currentLabel]=0
        #若存在,则键值+1
        labelCounts[currentLabel]+=1
    #初始化香农熵为0
    shannonEnt=0.0
    for key in labelCounts:
        #计算各个类别出现的概率
        prob=float(labelCounts[key])/numEntries
        #计算各个类别信息的期望值          
        shannonEnt-=prob*log(prob,2)
    return shannonEnt
   
#创建数据集       
def createDataSet():
    dataSet=[[1,1,'yes'],
             [1,1,'yes'],
             [1,0,'no'],
             [0,1,'no'],
             [0,1,'no']]
    labels=['no surfacing','flippers']
    return dataSet,labels

#按照给定特征划分数据集
#axis:划分数据集的特征
#value:特征的取值
def splitDataSet(dataSet,axis,value):
    #python语言传递参数列表时,传递的是列表的引用
    #为了不修改原始数据集,创建一个新的列表对象进行操作
    retDataSet=[]
    #提取数据集每行的特征向量
    for featVec in dataSet:
        #针对axis特征不同的取值,将数据集划分为不同的分支
       if featVec[axis]==value:
           #将原始数据集去掉当前划分数据的特征列
           reducedFeatVec=featVec[:axis]
           reducedFeatVec.extend(featVec[axis+1:])
           #注意extend和append的区别
           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):
        #得到特征i的特征值列表
        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
        #对字典中的键对应的值所在的列进行从大到小的排序 
        #classCount.iteritems()列表对象
        #key=operator.itemgetter(1)获取列表对象的第一个域的值
        #reverse=True 降序
        sortedClassCount=sorted(classCount.iteritems(),\
                                key=operator.itemgetter(1),reverse=True)
        #返回出现次数最多的类标签
        return sortedClassCount[0][0]

#递归方式构建决策树
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:{}}
    #复制当前特征标签列表,防止改变原始列表的内容
    subLabels=labels[:]   
    #删除属性列表中当前分类数据集特征
    del(subLabels[bestFeat])
    featValues=[example[bestFeat]for example in dataSet]
    uniqueValues=set(featValues)
    for value in uniqueValues:
        myTree[bestFeatLabel][value]=createTree(splitDataSet\
              (dataSet,bestFeat,value),subLabels)
    return myTree

     
#使用决策树的分类函数
def classify(inputTree,featLabels,testVec):
    #获得第一个用于划分数据集的特征属性
    # firstStr=inputTree.keys()[0]  #python2
    firstSides=list(inputTree.keys())
    firstStr=firstSides[0]
    #获得第一个节点的子节点
    secondDict=inputTree[firstStr]
    #找到第一个特征属性在特征列表的中位置
    featIndex=featLabels.index(firstStr)
    for key in secondDict.keys():
        if testVec[featIndex]==key:
            #判断secondDict[key]是否为字典(判断当前节点是否是叶子节点)
            if type(secondDict[key]).__name__=='dict':
                classLabel=classify(secondDict[key],featLabels,testVec)
            else:
                classLabel=secondDict[key]
    return classLabel
  
# 决策树的序列化  
def storeTree(inputTree,filename):  
    # 导入pyton模块  
    import pickle  
    # 以写的方式打开文件  
    fw = open(filename,'w')  
    # 决策树序列化  
    pickle.dump(inputTree,fw)          
# 读取序列化的树          
def grabTree(filename):  
    import pickle  
    fr = open(filename)  
    # 返回读到的树  
    return pickle.load(fr)  


if __name__=='__main__':
    myData,labels=createDataSet()
    '''myData=[['sunny','hot','high','false','no'],
            ['sunny','hot','high','true','no'],
            ['overcast','hot','high','false','yes'],
            ['rainy','mild','high','false','yes'],
            ['rainy','cool','normal','false','yes'],
            ['rainy','cool','normal','true','no'],
            ['overcast','cool','normal','true','yes'],
            ['sunny','mild','high','false','no'],
            ['sunny','cool','normal','false','yes'],
            ['rainy','mild','normal','false','yes'],
            ['sunny','mild','normal','true','yes'],
            ['overcast','mild','high','true','yes'],
            ['overcast','hot','normal','false','yes'],
            ['rainy','mild','high','true','no']]
    labels=['outlook','temperature','humidity','windy']'''
    print(labels)
    myTree=createTree(myData,labels)
    print(myTree)
    print(classify(myTree,labels,[1,0]))
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值