zoukankan      html  css  js  c++  java
  • 决策树1 -- ID3_C4.5算法

    声明:

             1。本篇为个人对《2012.李航.统计学习方法.pdf》的学习总结,不得用作商用。欢迎转载,但请注明出处(即:本帖地址)。

             2,因为本人在学习初始时有非常多数学知识都已忘记,因此为了弄懂当中的内容查阅了非常多资料,所以里面应该会有引用其它帖子的小部分内容。假设原作者看到能够私信我,我会将您的帖子的地址付到以下。

             3。假设有内容错误或不准确欢迎大家指正。

             4。假设能帮到你,那真是太好了。

    简单介绍

             决策树是一种主要的分类和回归方法。这里总结的是其分类方法部分。

             决策树是一种对实例进行分类的树状结构,eg:

                                                            属于类1否?

                                                              /      

                                                            /          

                                                    属于且          不属于

                                             无法再分类       且能够继续分类

                                                                    那属于类2否?

                                                                         /   

                                                                       /        

                                                              属于且        不属于且

                                                       无法再分类     无法再分类

    特征选择

             经过上面的介绍可知:决策树就是用某个特征将样本集合进行分类。那么怎样选择特征就非常重要了。由于每一次分类我们都要选取个能将样本集合分类的最好特征。

             以下就介绍选取最优特征的方法。即:ID3算法和C4.5算法。

     

    熵、条件熵、信息增益、信息增益比

             就好像我们用皮肤的颜色来区分黄种人、白种人和黑种人一样,ID3和C4.5也须要一个统一的标准来对样本集合进行区分,而这个标准就是:熵、条件熵、信息增益和信息增益比。

             我们如果X为一个取有限个值的离散随机变量。且其概率分布为:

                       P(X=xi) = Pi       I =1, 2, …, n

                       即:pi= 某个类的数量xi / 样本集合总数

             于是熵、条件熵、信息增益和信息增益比的定义分别例如以下:

             熵:

                       

                       Ps1:在上式中。若Pi= 0,则定义0log0 = 0.

                       Ps2:通常上式中的对数以2或e为底。这是熵的单位各自是比特(bit)和纳特(nat)。

                       由定义可知,熵仅仅依赖于X中类的分布,与X即样本总数无关,所以也可将H(X)记作H(P),即

                       熵越大,随机变量的不确定性就越大。从定义可验证:0 <= H(P) <= log n

                       若随机变量仅取两个值0和1。那X的分布为:

                                P(X=1) = P;     P(X=0) = 1 - P;        0 <= P <= 1

                       那么熵为:

                                H(P)= -Plog2P + ( -(1-p)log2(1-P) )

             条件熵:

                       条件熵就是在随机变量X已确定的条件下,随机变量Y的条件概率分布的熵对X的数学期望:

                                

                       上述的X代表样本集合总数。Y代表特征

                       于是:

                            

                            Pj即“既属于熵中那个类xi又属于条件熵中这个类的元素的数量 / 属于熵中那个类xi的元素的数量”

             信息增益:

                       g(X,Y) = H(X) – H(X|Y)

             信息增益比:

                       gR(X,Y) = g(X, Y) / H(X)

             PS:“经验熵”和“经验条件熵”就是由数据预计(特别是极大似然预计)得到的“熵”和“条件熵”的概率。

             在掌握了这些后就能够開始算法了。

             决策树学习经常使用的算法有ID3,C4.5和CART。

                       PS:由于ID3和C4.5仅仅有树的生成,所以它们生成的树easy产生过拟合。

             首先是ID3。

    ID3算法

             描写叙述:

                       输入:

                                训练数据集D。特征集A,阈值ε。

                       输出:

                                决策树T。

                       过程:

                                1,若当前可用的D中的全部实例仅有一个类C,则将类C作为当前T的当前结点,返回T;

                                2,若A=Ф(即:没有可用特征。如:一開始就没有特征给你用或经过一定次数的分类后,特征已用过一遍),则将D中实例数最大的那个类作为T的当前结点。返回T。

                                3,若A≠Ф,则计算各特征的信息增益。选择信息增益最大的特征Ag

                4,若Ag的信息增益小于阈值ε,则用当前D中实例数最大的类作为该节点的类标记。返回T。

                5,否则,依据Ag中每个值ai将当前的D切割成若干个非空子集Di。将Di中实例数最大的类作为标记,构建子结点。由节点集子结点构成T,返回T;

                6。对第i个子结点,以Di为训练集,以ai为特征集,递归的调用1~5步。得到子树Ti。返回Ti

        样例:

                      对例如以下数据建立决策树:

                                                   (贷款申请样本数据表)

    ID

    年龄

    有工作

    有自己的房子

    信贷情况

    类别(是否能贷到款)

    1

    青年

    一般

    2

    青年

    3

    青年

    4

    青年

    一般

    5

    青年

    一般

    6

    中年

    一般

    7

    中年

    8

    中年

    9

    中年

    很好

    10

    中年

    很好

    11

    老年

    很好

    12

    老年

    13

    老年

    14

    老年

    很好

    15

    老年

    一般

                       解:

                                1,计算熵:

                                         由于该表的数据被分为两类:给予贷款,不给予贷款。

                                         所以:

                                2。计算全部的条件熵:

                                         我们用A1代表年龄。D1, D2,D3 代表中、青、老年。

                                         由于中青老年各五人。所以:

                                                  

                                         而对于每一个年龄段都有:“能够贷款的青中老年”和“不可贷款的青中老年”。

                                         所以。对于青年:

                                                   

                                         于是中年和老年同理,最后得:

                                                   H(T|A1)

                                                            

                                         同理。对于是否有工作(A2),是否有房子(A3),信贷情况(A4):

                                                   H(T|A2)= 0.647

                                                   H(T|A3)= 0.551

                                                   H(T|A4)= 0.608

                                3,计算信息增益

                                         g(T,A1)= H(T) – H(T|A1) = 0.971 – 0.888 = 0.083

                                         g(T,A2)= H(T) – H(T|A2) = 0.324

                                         g(T,A3)= H(T) – H(T|A3) = 0.420

                                         g(T,A4)= H(T) – H(T|A4) = 0.363

                                4,由于g(T,A3) 最大,所以选择“是否有房子”作为根节点的特征。于是这将数据集分成了两部分:T1(有房)和T2(无房)

                                         因为T1的全可贷款(全部的元素都属于一类),所以它成为一个叶子节点。

                                         而T2中既有能贷到款的也有贷不到的(全部的元素不属于一类),所以对于T2须要从特征A1(年龄)。A2(有无工作),A4(信贷情况)中选出一个新特征。

                                         到此形成例如以下决策树:

                                                                       A3:有房子吗

                                                                                   /   

                                                               T1:有房子       T2:无房子

                                                               全能贷到款   有的能贷到,有的不能

                                5,对T2这个数据集再次调用1~3步,计算信息增益(注意:需用当前的数据集T2又一次计算),得:

                                         g(T2,A1) = H(T2) – H(T2|A1) = 0.251

                                         g(T2,A2) = H(T2) – H(T2|A2) = 0.918

                                         g(T2,A4) = H(T2) – H(T2|A4) = 0.474

                                         于是选择A2(有无工作)来作为当前特征。

                                         到此形成例如以下决策树:

                                                                       A3:有房子吗

                                                                              /        

                                                               T1:有房子       T2:无房子。有工作吗?

                                                               全能贷到款          /            

                                                                                   有工作         无工作

                                                                            全能贷到款     全都贷不到

                                         由于到此。全部的子结点的元素全都仅仅属于一个类,所以到此以全然划分。

                                         终于决策树如上。

    C4.5算法

             C4.5算法就是将ID3第三步的信息增益换成信息增益比。其它不变。


    #-*-coding:utf-8-*-
    # LANG=en_US.UTF-8
    # ID3 和 ID4 算法
    # 文件名称:ID3_ID4.py
    #
    
    import sys
    import math
    import copy
    
    dict_all = {
            # 1: 青年;2:中年;3:老年
            '_age' : [
                    1, 1, 1, 1, 1,
                    2, 2, 2, 2, 2,
                    3, 3, 3, 3, 3,
                ],
    
            # 0:无工作;1:有工作
            '_work' : [
                    0, 0, 1, 1, 0,
                    0, 0, 1, 0, 0,
                    0, 0, 1, 1, 0,
                ],
    
            # 0:无房子;1:有房子
            '_house' : [
                    0, 0, 0, 1, 0,
                    0, 0, 1, 1, 1,
                    1, 1, 0, 0, 0,
                ],
    
            # 1:信贷情况一般;2:好;3:很好
            '_credit' : [
                    1, 2, 2, 1, 1,
                    1, 2, 2, 3, 3,
                    3, 2, 2, 3, 1,
                ],
        }
    
    # 0:未申请到贷款;1:申请到贷款
    _type = [
            0, 0, 1, 1, 0,
            0, 0, 1, 1, 1,
            1, 1, 1, 1, 0,
        ]
    
    # 二叉树结点
    class BinaryTreeNode( object ):
        def __init__( self, name=None, data=None, left=None, right=None, father=None ):
            self.name = name
            self.data = data
            self.left = left
            self.right = left
    
    # 二叉树遍历
    class BTree(object):
        def __init__(self,root=0):
            self.root = root
    
        # 中序遍历
        def inOrder(self,treenode):
            if treenode is None:
                return
    
            self.inOrder(treenode.left)
            print treenode.name, treenode.data
            self.inOrder(treenode.right)
    
    
    # 遍历类型,统计每一个类型的数量,将其保存到字典中
    # 如:对于 _type: 有9个类型1。6个类型0。

    # 于是返回:{'1': 9.0, '0': 6.0} # 參数:类型列表 def get_type_num( type_list ): type_dict = {} tmp_item = '' for item in type_list: item = str(item) if tmp_item != item: if item in type_dict.keys(): type_dict[item] += 1.0 else: type_dict[item] = 1.0 tmp_item = item else: type_dict[item] += 1.0 return type_dict # 获得熵 # 參数:类型列表 def get_entropy( type_list ): entropy = 0.0 len_type = len(type_list) type_dict = get_type_num( type_list ) # 计算熵 for key in type_dict.keys(): tmp_num = type_dict[key] / len_type entropy = entropy - tmp_num * math.log(tmp_num, 2) return float('%.3f' % entropy) # 获得条件熵 # 參数:特征列表,类型列表,序号列表 # 如: # 第一轮时以 _house 为特征进行筛选(筛选使用ID3或ID4。不是在此函数中),这是參数分别为:_house, _type, [0, 1, ..., 15] # 第一轮结束后:左子树的特征序号列表为:[3, 7, 8, 9, 10, 11],右子树的特征序号列表为:[0, 1, 2, 4, 5, 6, 12, 13, 14] # 于是第二轮在对右子树以 _work 为特征进行筛选时传入參数:_house, _type, [0, 1, 2, 4, 5, 6, 12, 13, 14] def get_conditional_entropy( value_list, type_list, num_list ): # 整理 value_list 以 num_list 为序号形成的新列表中的不同类别 # value_dict = {特征名 : 包括的类别列表} # eg:对于 _work # 其“原始内容”和“以 num_list(即:[0, 1, 2, 4, 5, 6, 12, 13, 14]) 为序号形成的新列表为”分别例如以下: # [0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0] # [0, 0, 1, 0, 0, 0, 1, 1, 0] # 新列表有3个类型1和6个类型2。于是该函数返回:{'1': [1, 1, 1], '0': [0, 0, 0, 0, 0, 0]} def get_value_type(): value_dict = {} tmp_type = '' tmp_item = '' for num in num_list: item = str( value_list[num] ) if tmp_item != item: if item in value_dict.keys(): value_dict[item].append(type_list[num]) else: value_dict[item] = [type_list[num],] tmp_item = item else: value_dict[item].append(type_list[num]) return value_dict value_dict = get_value_type() conditional_entropy = 0 for key in value_dict.keys(): tmp_num = float( '%.3f' % (float(len(value_dict[key]))/len(value_list)) ) conditional_entropy += float( '%.3f' % (tmp_num * get_entropy(value_dict[key])) ) return conditional_entropy # 获得信息增益 def get_information_gain( value_list, type_list, num_list ): return float( '%.3f' % (get_entropy( type_list ) - get_conditional_entropy( value_list, type_list, num_list )) ) # 获得信息增益比 def get_information_gain_ratio( value_list, type_list, num_list ): entropy = get_entropy( type_list ) information_gain = entropy - get_conditional_entropy( value_list, type_list, num_list ) return float( '%0.3f' % (information_gain/entropy) ) # ID3 算法 def ID3( data, type_list, threshold ): # 获得最大的信息增益 def get_max_information_gain( num_list ): step = 'continue' tmp_value = 0.0 feature_name = '' for key in data.keys(): information_gain = get_information_gain( data[key], type_list, num_list ) if information_gain > tmp_value: feature_name = key tmp_value = information_gain # 假设信息增益小于阈值,则告诉后面的程序。不用在迭代了,到此就可以 if information_gain < threshold: step = 'over' return feature_name, step # 进行分类 def classify( root, note_name, note_data, note_type ): # 将'特征可能值名字'追加到 root.name 中 # 将[样本序号的列表]合并到 root.data 中 root.name.append( note_name ) root.data.extend( note_data ) # note_type=='exit' 意味着当前的数据所有属于某一类。不用在分类了 if not data or note_type=='exit': return feature_name, step = get_max_information_gain( note_data ) # 依据特征的可能值将样本数据分成数个集合。并保存成“特征字典”。 # 字典结构为:{ '特征可能值名字': [样本序号的列表] } feature_dict = {} tmp_item = '' for num in note_data: item = str( data[feature_name][num] ) if tmp_item != item: if item in feature_dict.keys(): feature_dict[item].append(num) else: feature_dict[item] = [num, ] tmp_item = item else: feature_dict[item].append(num) # 从样本集合中将该特征删除 del data[feature_name] # 准备左子节点和右子节点。节点的 name 和 data 是个空列表 root.left = BinaryTreeNode( [], [] ) root.right = BinaryTreeNode( [], [] ) # 计算“特征字典”中各个集合中是属于“能贷贷款”的多还是“不能贷贷款”的多 # 假设是前者: # 递归调用 classify,形成左子节点 # 假设是后者: # 递归调用 classify。形成右子节点 for key in feature_dict.keys(): num_yes = 0; num_no = 0 for num in feature_dict[key]: if type_list[num] == 1: num_yes = num_yes + 1 elif type_list[num] == 0: num_no = num_no + 1 else: print 'ERROR: wrong type in _type' exit() note_type = 'not_exit' if num_yes == 0 or num_no == 0 or step == 'over': note_type = 'exit' if num_yes >= num_no: classify( root.left, '%s:%s' % (feature_name, key), feature_dict[key], note_type ) else: classify( root.right, '%s:%s' % (feature_name, key), feature_dict[key], note_type ) return root tmp_list = [] for num in xrange( len(dict_all[dict_all.keys()[0]]) ): tmp_list.append( num ) return classify( BinaryTreeNode( [], [] ), 'root', tmp_list, 'not_exit' ) # C4.5 算法 def C4_5( data, type_list, threshold ): # 获得最大的信息增益比 def get_max_information_gain( num_list ): step = 'continue' tmp_value = 0.0 feature_name = '' for key in data.keys(): information_gain_ratio = get_information_gain_ratio( data[key], type_list, num_list ) if information_gain_ratio > tmp_value: feature_name = key tmp_value = information_gain_ratio # 假设信息增益比小于阈值。则告诉后面的程序,不用在迭代了,到此就可以 if information_gain_ratio < threshold: step = 'over' return feature_name, step # 进行分类 def classify( root, note_name, note_data, note_type ): # 将'特征可能值名字'追加到 root.name 中 # 将[样本序号的列表]合并到 root.data 中 root.name.append( note_name ) root.data.extend( note_data ) # note_type=='exit' 意味着当前的数据所有属于某一类。不用在分类了 if not data or note_type=='exit': return feature_name, step = get_max_information_gain( note_data ) # 依据特征的可能值将样本数据分成数个集合,并保存成“特征字典”。 # 字典结构为:{ '特征可能值名字': [样本序号的列表] } feature_dict = {} tmp_item = '' for num in note_data: item = str( data[feature_name][num] ) if tmp_item != item: if item in feature_dict.keys(): feature_dict[item].append(num) else: feature_dict[item] = [num, ] tmp_item = item else: feature_dict[item].append(num) # 从样本集合中将该特征删除 del data[feature_name] # 准备左子节点和右子节点。节点的 name 和 data 是个空列表 root.left = BinaryTreeNode( [], [] ) root.right = BinaryTreeNode( [], [] ) # 计算“特征字典”中各个集合中是属于“能贷贷款”的多还是“不能贷贷款”的多 # 假设是前者: # 递归调用 classify,形成左子节点 # 假设是后者: # 递归调用 classify,形成右子节点 for key in feature_dict.keys(): num_yes = 0; num_no = 0 for num in feature_dict[key]: if type_list[num] == 1: num_yes = num_yes + 1 elif type_list[num] == 0: num_no = num_no + 1 else: print 'ERROR: wrong type in _type' exit() note_type = 'not_exit' if num_yes == 0 or num_no == 0 or step == 'over': note_type = 'exit' if num_yes >= num_no: classify( root.left, '%s:%s' % (feature_name, key), feature_dict[key], note_type ) else: classify( root.right, '%s:%s' % (feature_name, key), feature_dict[key], note_type ) return root tmp_list = [] for num in xrange( len(dict_all[dict_all.keys()[0]]) ): tmp_list.append( num ) return classify( BinaryTreeNode( [], [] ), 'root', tmp_list, 'not_exit' ) # 阈值 threshold = 0.3 dict_all_id3 = copy.deepcopy( dict_all ) root = ID3( dict_all_id3, _type, threshold ) bt = BTree( root ) print '--------------ID3----------------' bt.inOrder( bt.root ) print '--------------------------------- ' dict_all_c45 = copy.deepcopy( dict_all ) root = C4_5( dict_all_c45, _type, threshold ) bt = BTree( root ) print '--------------C4.5----------------' bt.inOrder( bt.root ) print '---------------------------------- '



  • 相关阅读:
    hdu 4370
    lightoj 1074
    poj 1026
    poj 3159
    poj3660 cow contest
    hdu 4069 垃圾数独
    操作系统概念题复习
    ARM指令
    C++ 抢占时优先级进程调度
    Docker 入门
  • 原文地址:https://www.cnblogs.com/wzjhoutai/p/7399672.html
Copyright © 2011-2022 走看看