决策树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 '---------------------------------\n'

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 '----------------------------------\n'
时间: 2024-11-02 13:05:41

决策树1 -- ID3_C4.5算法的相关文章

web安全之机器学习入门——3.2 决策树与随机森林算法

目录 简介 决策树简单用法 决策树检测P0P3爆破 决策树检测FTP爆破 随机森林检测FTP爆破 简介 决策树和随机森林算法是最常见的分类算法: 决策树,判断的逻辑很多时候和人的思维非常接近. 随机森林算法,利用多棵决策树对样本进行训练并预测的一种分类器,并且其输出的类别是由个别决策树输出的类别的众数决定. 决策树简单用法 使用sklearn自带的iris数据集 # -*- coding: utf-8 -*- from sklearn.datasets import load_iris from

决策树之C4.5算法学习

决策树<Decision Tree>是一种预测模型,它由决策节点,分支和叶节点三个部分组成.决策节点代表一个样本测试,通常代表待分类样本的某个属性,在该属性上的不同测试结果代表一个分支:分支表示某个决策节点的不同取值.每个叶节点代表一种可能的分类结果. 使用训练集对决策树算法进行训练,得到一个决策树模型,利用模型对未知样本(类别未知)的类别判断时,从决策树根节点开始,从上到下搜索,直到沿某分支到达叶节点,叶节点的类别标签就是该未知样本的类别. 网上有个例子可以很形象的说明利用决策树决策的过程(

决策树与随机森林算法

决策树 决策树模型是一种树形结构,基于特征对实例进行分类或回归的过程.即根据某个特征把数据分划分到若干个子区域(子树),再对子区域递归划分,直到满足某个条件则停止划分并作为叶子节点,不满足条件则继续递归划分. 一个简单的决策树分类模型:红色框出的是特征. 决策树模型学习过程通常包3个步骤:特征选择.决策树的生成.决策树的修剪. 1.特征选择 选择特征顺序的不同将会产生不同决策树,选择好的特征能使得各个子集下标签更纯净.度量特征对产生子集的好坏有若干方法,如误差率,信息增益.信息增益比和基尼指数等

R语言︱决策树族——随机森林算法

笔者寄语:有一篇<有监督学习选择深度学习还是随机森林或支持向量机?>(作者Bio:SebastianRaschka)中提到,在日常机器学习工作或学习中,当我们遇到有监督学习相关问题时,不妨考虑下先用简单的假设空间(简单模型集合),例如线性模型逻辑回归.若效果不好,也即并没达到你的预期或评判效果基准时,再进行下换其他更复杂模型来实验. ---------------------------------------------- 一.随机森林理论介绍 1.1 优缺点 优点. (1)不必担心过度拟合

决策树的基本ID3算法

一  ID3算法的大致思想 基本的ID3算法是通过自顶向下构造决策树来进行学习的.我们首先思考的是树的构造从哪里开始,这就涉及到选择属性进行树的构造了,那么怎样选择属性呢?为了解决这个问题,我们使用统计测试来确定每一个实例属性单独分类训练样例的能力,把分类能力最好的属性作为树根节点的测试.然后为根节点属性的每个可能值产生一个分支,并把训练样例排列到适当的分支之下.然后重复整个过程,用每个分支节点关联的训练样例来选取在该点被测试的最佳属性.这形成了对合格决策树的贪婪搜索,也就是算法从不回溯重新考虑

机器学习第5周--炼数成金-----决策树,组合提升算法,bagging和adaboost,随机森林。

决策树decision tree 什么是决策树输入:学习集输出:分类觃则(决策树) 决策树算法概述 70年代后期至80年代初期,Quinlan开发了ID3算法(迭代的二分器)Quinlan改迚了ID3算法,称为C4.5算法1984年,多位统计学家在著名的<Classification and regression tree>书里提出了CART算法ID3和CART几乎同期出现,引起了研究决策树算法的旋风,至今已经有多种算法被提出 算法的核心问题 该按什么样的次序来选择变量(属性)?最佳分离点(连

从决策树学习谈到贝叶斯分类算法、EM、HMM

从决策树学习谈到贝叶斯分类算法.EM.HMM 引言 近期在面试中,除了基础 &  算法 & 项目之外,经常被问到或被要求介绍和描写叙述下自己所知道的几种分类或聚类算法(当然,这全然不代表你将来的面试中会遇到此类问题,仅仅是由于我的简历上写了句:熟悉常见的聚类 & 分类算法而已),而我向来恨对一个东西仅仅知其皮毛而不得深入,故写一个有关数据挖掘十大算法的系列文章以作为自己备试之用,甚至以备将来经常回想思考.行文杂乱,但侥幸若能对读者起到一点帮助,则幸甚至哉. 本文借鉴和參考了两本书,

数据挖掘十大算法之决策树详解(2)

在2006年12月召开的 IEEE 数据挖掘国际会议上(ICDM, International Conference on Data Mining),与会的各位专家选出了当时的十大数据挖掘算法( top 10 data mining algorithms ),可以参见文献[1].本博客已经介绍过的位列十大算法之中的算法包括: [1] k-means算法(http://blog.csdn.net/baimafujinji/article/details/50570824) [2] 支持向量机SVM

机器学习技法-决策树和CART分类回归树构建算法

课程地址:https://class.coursera.org/ntumltwo-002/lecture 重要!重要!重要~ 一.决策树(Decision Tree).口袋(Bagging),自适应增强(AdaBoost) Bagging和AdaBoost算法再分类的时候,是让所有的弱分类器同时发挥作用.它们之间的区别每个弱分离器是否对后来的blending生成G有相同的权重. Decision Tree是一种有条件的融合算法,每次只能根据条件让某个分类器发挥作用. 二.基本决策树算法 1.用递