笔记|统计学习方法:决策树(一)

决策树的基本概念

决策树就是一棵树

  • 叶结点对应于决策结果,其他每个结点则对应于一个属性测试;
  • 每个结点包含的样本集合根据属性测试的结果被划分到子结点中;
  • 根结点包含样本全集,从根结点到每个叶子结点的路径对应了一个判定测试序列。
    示例:

决策树学习的关键在于如何选择最优的划分属性,所谓的最优划分属性,对于二元分类而言,就是尽量使划分的样本属于同一类别,即“纯度”最高的属性。那么如何来度量特征(features)的纯度,这时候就要用到“经验熵(information entropy)”。

经验熵

先来看看信息熵的定义:假如当前样本集D中第k类样本所占的比例为 p i ( k = 1 , 2 , 3 , ⋅ ⋅ ⋅ , ∣ I ∣ ) p_i(k=1,2,3,···,|I|) pi(k=1,2,3,,I)为类别的总数(对于二元分类来说,i=2)。则样本集的信息熵为:
p k = ∣ C k ∣ D p_k=\frac{|C_k|}{D} pk=DCk

H ( D ) = − ∑ k = 1 K p k l o g 2 p k H(D)=-\sum_{k=1}^Kp_klog_2p_k H(D)=k=1Kpklog2pk

H ( D ) = − ∑ k = 1 K ∣ C k ∣ ∣ D ∣ l o g 2 ∣ C k ∣ ∣ D ∣ H(D)=-\sum_{k=1}^K\frac{|C_k|}{|D|}log_2\frac{|C_k|}{|D|} H(D)=k=1KDCklog2DCk
H ( D ) H(D) H(D)的值越小,则D的纯度越高。
反应了信息增益的一个缺点:该属性能取得值越多,信息增益,越偏向这个

信息增益算法

  • 输入:训练数据集 D D D和特征 A A A
  • 输出:特征 A A A对训练数据 D D D的信息增益 g ( D , A ) g(D,A) g(D,A)

信息增益表示了得知特征X的信息而使得类Y的信息的不确定性性减少的程度。

经验条件熵

计算特征 A A A对数据集 D D D的经验条件熵 H ( D ∣ A ) H(D|A) H(DA)
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)

= − ∑ i = 1 n ∣ D i ∣ ∣ D ∣ ∑ k = 1 K ∣ D i k ∣ ∣ D i ∣ l o g 2 ∣ D i k ∣ ∣ D i ∣ =-\sum_{i=1}^n\frac{|D_i|}{|D|}\sum_{k=1}^K\frac{|D_{ik}|}{|D_i|}log_2\frac{|D_{ik}|}{|D_i|} =i=1nDDik=1KDiDiklog2DiDik

  • D i k D_{ik} Dik的意思是在某一特征下的特征样本中满足训练目标的个数
  • D i D_i Di指的是该特征的样本容量
信息增益为:

信息增益=经验熵-经验条件熵
g ( D , A ) = H ( D ) − H ( D ∣ A ) g(D,A)=H(D)-H(D|A) g(D,A)=H(D)H(DA)

一般而言,信息增益越大,则表示使用特征 对数据集划分所获得的“纯度提升”越大。所以信息增益可以用于决策树划分属性的选择,其实就是选择信息增益最大的属性,ID3算法就是采用的信息增益来划分属性。

信息增益比:

信息增益比 g R ( D , A ) g_R(D,A) gR(D,A)为信息增益 g ( D , A ) g(D,A) g(D,A)与训练数据集 D D D关于特征 A A A的值熵 H A ( D ) H_A(D) HA(D)值之比:
g R ( D , A ) = g ( D , A ) H A ( D ) g_R(D,A)=\frac{g(D,A)}{H_A(D)} gR(D,A)=HA(D)g(D,A)
其中:
H A ( D ) = − ∑ i = 1 n ∣ D i ∣ ∣ D ∣ l o g 2 ∣ D i ∣ ∣ D ∣ H_A(D)=-\sum_{i=1}^n\frac{|D_i|}{|D|}log_2\frac{|D_i|}{|D|} HA(D)=i=1nDDilog2DDi
n n n是特征 A A A取值的个数。

ID3算法决策树生成

  • 输入:数据集 D D D,特征集 A i A_i Ai,阈值 ε \varepsilon ε
  • 输出:决策树 T T T
  1. 先对特征集求信息增益,选取最大的作为根结点的特征。
  2. 看特征集对训练数据集D的划分成几个子集 D i D_i Di(例如该特征集会划分 是或否具有该特征)
  3. 若某个子集只具有同一个类的的样本点,成为一个叶节点
  4. 否则,对子集从剩下的特征中选择新的特征,求得信息增益 g ( D k , A r e m a i n ) g(D_{k},A_{remain}) g(Dk,Aremain)
  5. 如此递归操作,直到所有的特征分类,生成决策树 T T T

Python代码实现

from math import log
import operator

def calcShannonent(dataSet):                        #计算数据的熵(entropy)
    numEntries = len(dataSet)                       #数据条数
    labelCounts = {}
    for featVec in dataSet:
        currentLabel = featVec[-1]                  #每行数据的最后一个字(类别)
        if currentLabel not in labelCounts.keys():
            labelCounts[currentLabel] = 0
        labelCounts[currentLabel] += 1              #统计又多少个类以及每个类的数量
    shannonEnt = 0
    for key in labelCounts:
        prob = float(labelCounts[key])/numEntries  #计算单个类的熵值
        shannonEnt -= prob*log(prob, 2)             #累加每个类的熵值
    return shannonEnt

def createDataSet_temp():
    #创造实例数据
    labels = ['头发', '声音']

    with open('TreeGrowth_ID3.txt', 'r', encoding='UTF-8') as f:		#改了一个文件读写
        dataSet = [[] for i in range(9)]
        value = ['长', '短', '粗', '细', '男', '女']
        num_line = 0
        for line in f:
            # re.split(r'(\s{8}\[\')|(\', \')|(\'\],)|(\'\])', line)			#尝试使用正则划分,失败了
            for i in line:
                if (i in value):
                    dataSet[num_line].append(i)
            num_line += 1
        del(dataSet[0])
    '''
    dataSet = [
        ['长', '粗', '男'],
        ['短', '粗', '男'],
        ['短', '粗', '男'],
        ['长', '细', '女'],
        ['短', '细', '女'],
        ['短', '粗', '女'],
        ['长', '粗', '女'],
        ['长', '粗', '女']
    ] 
    '''
    return dataSet, labels

def splitDataSet(dataSet, axis, value):             #按某个特征分类后的数据
    retDataSet = []
    for featVec in dataSet:
        if featVec[axis] == value:                  #axis表示指定属性在label中的标号,value是该分类中属性的目标值
            reducedFeatVec = featVec[:axis]         #对于每一条记录,在分完类后,都要把之前使用过的属性删除
            reducedFeatVec.extend(featVec[axis+1:])
            retDataSet.append(reducedFeatVec)
    return retDataSet

def chooseBestFeatureToSplit(dataSet):              #选择最优的分类特征
    numFeatures = len(dataSet[0])-1                 #特征的个数
    baseEntropy = calcShannonent(dataSet)           #原始熵
    bestInfoGain = 0
    bestFeature = -1
    for i in range(numFeatures):                    #循环每一个特征
        featList = [example[i] for example in dataSet]#读取每一条记录取出其中第i个属性的值,并新建一个列表
        uniqueVals = set(featList)                  #set()创建一个无序不重复元素集,可进行关系测试,删除重复元素,进行交差并集运算
        newEntropy = 0
        for value in uniqueVals:                    #对于第i个属性值列表中每一个值
            subDataSet = splitDataSet(dataSet, i, value)    #subDataSet是去掉了第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):                         #多数表决排序,如:最后分类为2男1女则判断为男
    classCount = {}
    for vote in classList:
        if vote not in classCount.keys():
            classCount[vote] = 0
        classCount[vote] += 1
    sortedClassCount = sorted(classCount.items(),key=operator.itemgetter(1),reverse=True)
    #将items返回的可遍历键值对数组,根据键值对的第二个域(值),进行降序排列
    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)    #找到最优特征的标号
    if(bestFeat == -1):                             #自己添加的,不然报错
        return classList[0]
    bestFeatLabel = labels[bestFeat]                #找到最优特征
    myTree = {bestFeatLabel:{}}                     #分类结果以字典形式保存,得到树的当前层
    del(labels[bestFeat])                           #删除labels中当前最优特征
    featValues = [example[bestFeat] for example in dataSet]#将记录集中当前最优特征的值形成列表
    uniqueVals = set(featValues)                    #对值集去重
    for value in uniqueVals:                        #对于每个值
        subLabels = labels[:]                       #subLabels为去除当前最优特征的特征集
        myTree[bestFeatLabel][value]=createTree(splitDataSet(dataSet,bestFeat,value),subLabels)
        #使用递归,创造下一层树,~~~~~~~~~~~~~~~~~~~~~~
    return myTree

if __name__=='__main__':
    dataSet, labels = createDataSet_temp()          #创造示例数据
    print(createTree(dataSet, labels))              #输出决策树模型结果

参考博客:

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值