决策树ID3算法实现与讨论(完整代码与数据)

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

 

文章目录

  • 一、算法相关介绍
    • 1.简介
    • 2.决策树直观表示
    • 3.ID3算法核心思想(含流程图)
  • 二、实例算法实现演示
    • 1.问题描述
    • 2.代码结构及其描述
    • 3.完整代码
  • 三、ID3算法讨论与分析
    • 1.优势
    • 2.缺点

 

一、算法相关介绍

     1.简介

    决策树学习是应用最广的归纳推理算法之一。它是一种逼近离散函数的方法,且对噪声数据有很好的鲁棒性,能够学习析取表达式。相关算法包多为以ID3思想为内核。

决策树学习方法搜索完整表示的假设空间,从而避免了受限假设空间的不足。决策树学习的归纳偏置是优先选择较小的树。

     2.决策树直观表示

决策树通过把实例从根结点排列到某个叶子结点来分类实例,叶子结点即为实例所属的分类。树上的每一个结点指定了对实例的某个属性的测试,并且该结点的每一个后继分支对应于该属性的一个可能值。分类实例的方法是从这棵树的根结点开始,测试这个结点指定的属性,然后按照给定实例的该属性值对应的树枝向下移动。这个过程再在以新结点为根的子树上重复。

其本质是多个if-then有序规则的树状罗列,下图为教材中一棵典型的学习到的决策树:

决策树ID3算法实现与讨论(完整代码与数据)_第1张图片

   3.ID3算法核心思想

基本的 ID3 算法通过自顶向下构造决策树来进行学习。构造过程是从“哪一个属性将在树的根结点被测试?”这个问题开始的。然后为根结点属性的每个可能值产生一个分支,并把训练样例排列到适当的分支(也就是,样例的该属性值对应的分支)之下。然后重复整个过程,用每个分支结点关联的训练样例来选取在该点被测试的最佳属性。同时此贪婪搜索从不回溯重新考虑先前的选择。

故ID3算法主要围绕3个问题的解决来进行:

    • 如何选择最优属性
    • 结点数据如何拆分
    • 子树何时停止增长

3.1属性选择依据

在为树节点选择测试属性时,需要选择最有助于分类实例的属性(也即特征)。ID3定义了一个统计属性“信息增益”,用来衡量给定属性区分当前训练样例集的能力,在其增长树的每一步使用该信息增益标准从侯选属性集中选择属性。

3.1.1用熵度量样例的均一性的依据

决策树ID3算法实现与讨论(完整代码与数据)_第2张图片

3.1.2信息增益度量期望的熵降低

决策树ID3算法实现与讨论(完整代码与数据)_第3张图片

3.2结点数据拆分

    依据所选特征(属性),依照不同的特征值将数据进行划分

3.3子树停止增长条件

    满足以下条件之一:

  • 所有的属性均已被这条路径包括
  • 与该结点关联的所有样例具有相同的目标特征值(也即熵值为0)

3.4 ID3算法流程图

决策树ID3算法实现与讨论(完整代码与数据)_第4张图片

二、实例算法实现演示

1.问题描述

通过outlook、temperature,humidity,wind这四个特征值来判定该天(day)是否适宜打网球(playtennis)。数据以表格形式存于train_data.csv文件中。数据如下图所示。

决策树ID3算法实现与讨论(完整代码与数据)_第5张图片

2.代码结构及其描述

      • Outlook:sunny-1,rain-2,overcast-3
      • Temperature:hot-1,mild-2,cool-3
      • Humidity:high-1,normal-2
      • Wind:true-1,false-2

1.主要函数模块:

    • createDataset(file):将来自csv文件的数据集进行映射转换
    • majority_eigen(data_ls,eigen_idx): 获得“最通常值”
    • handle_incomplete(data_ls,incomplete_ls):对初始化的训练集进行不完整属性值填充(使用“最通常值”进行填充)
    • cal_gain(node_cls_ls):计算结点集信息增益(由于在选择测试结点时,依据公式Gain(A)=I(p,n)-E(A),知Gain与E(A)负相关,故只计算熵值)
    • choose_best_eigen(node_data,eigen_ls):求取最佳分类特征
    • create_tree(data,eigen_ls):构造决策树
    • make_predict(decision_tree,test_data):根据学习好的决策树对给定数据进行目标特征预测

2.函数调用关系图示如下:

决策树ID3算法实现与讨论(完整代码与数据)_第6张图片

3.完整代码

from math import log
import csv
import numpy as np

#自动获取函数调用图示
from pycallgraph import PyCallGraph
from pycallgraph.output import GraphvizOutput
from pycallgraph import Config
from pycallgraph import GlobbingFilter
config = Config()

#不完整属性值先置0
default_fill=0 

#特征列表
eigens=[
    'outlook',
    'temperature',
    'humidity',
    'wind',
    'class',
]

#属性值与代码映射字典
eigen_affine=[
{
    "sunny":1,
    "rain":2,
    "overcast":3,
},
{
    "hot":1,
    "mild":2,
    "cool":3,
},
{
    "high":1,
    "normal":2,
},
{
    "true":1,
    "false":2,
},
{
    "pos":"positive",
    "neg":"negtive",
}
]


#将来自csv文件的数据集进行映射转换
def createDataset(file):
    with open(file,encoding='utf-8') as f:

        f_handle=csv.reader(f)
        eigens=next(f_handle)
        
        data_ls=[]
        incomplete_ls=[]
        for row_idx,row in enumerate(f_handle):
            row_ls =[]
            for idx,value in enumerate(row):
                # print(idx,value)
                if value=="":
                    row_ls.append(default_fill)
                    incomplete_ls.append(row_idx)
                else:
                    row_ls.append(eigen_affine[idx][value])
            data_ls.append(row_ls)
        return data_ls,incomplete_ls

#对初始化的训练集进行不完整属性值填充(使用“最通常值”进行填充)
def handle_incomplete(data_ls,incomplete_ls):
    for idx in incomplete_ls:
        for eigen_idx,eigen in enumerate(data_ls[idx]):
            if eigen==0:
                value_dict={}
                for data_idx in range(idx):
                    eigen_val=data_ls[data_idx][eigen_idx]
                    if  eigen_val in value_dict:
                        value_dict[eigen_val]+=1
                    else:
                        value_dict[eigen_val]=1
                count_ls=[]
                for val_count in value_dict.values():
                    count_ls.append(val_count)
                data_ls[idx][eigen_idx]=max(count_ls)
    return data_ls
            

#计算结点集信息增益
#由于在选择测试结点时,依据公式Gain(A)=I(p,n)-E(A),
#知Gain与E(A)负相关,故只计算熵值 
def cal_gain(node_cls_ls):
    # cls_count=[0 for i in len(eigen_affine[-1])]
    cls_dict={}
    for cls_val in set(node_cls_ls):
        cls_dict[cls_val]=node_cls_ls.count(cls_val)
    entropy=0
    for cls_count in cls_dict.values():
        entropy=(-(cls_count/len(node_cls_ls))*log(cls_count/len(node_cls_ls)))
    return entropy

#eigen_ls 特征序号列表
#返回最佳特征序号
def choose_best_eigen(node_data,eigen_ls):
    node_cls_ls=[data[-1] for data in node_data]
    # base_entropy=cal_entropy(node_cls_ls)
    best_gain=float('-inf')
    best_eigen=eigen_ls[0]
    for eigen in eigen_ls:
        cls_dict={}
        for data in node_data:
            if data[eigen] in cls_dict.keys():
                cls_dict[eigen].append(data[-1])
            else:
                cls_dict[eigen]=[]
        eigen_entropy = 0
        for data_ls in cls_dict.values():
            eigen_entropy+=cal_gain(data_ls)
        if eigen_entropy

三、ID3算法讨论与分析

作为一种最基础也最为核心的决策树算法,ID3算法从一个假设空间中搜索一个拟合训练样例的假设,通过观察ID3算法的搜索空间和搜索策略,我们可以看到这个算法的优势与不足

1.优势

    • ID3算法使用信息增益作为结点选择依据,从信息论的角度来进行学习,原理明晰、可解释性强
    • ID3算法操作简单,学习泛化能力强,考虑到ID3算法的归纳偏置:较短的树比较长的树优先,信息增益高的特征更靠近根节点的树优先,我们可以看到决策树所习得的规则是简单且易泛化到新数据的(符合奥卡姆剃刀原则)
    • 对于样例集中不充足属性的数据,可以有多种有效的方式进行填充,包括此次所用的“最通常值法”、“比例分配法”、“调换特征与目标属性角色法”等等。
    • ID3 算法在搜索的每一步都使用当前的所有训练样例,以统计为基础决定怎样精化当前的假设。这与那些基于单独的训练样例递增作出决定的方法(例如,Find-S或候选消除法)不同。使用所有样例的统计属性(例如,信息增益)的一个优点是大大减小了对个别训练样例错误的敏感性。

2.不足

      •  ID3算法只能处理分类属性的数据,不适宜连续类型的数据
      •  不能判断有多少个其他的决策树也是与现有的训练数据一致的
      •  ID3算法很容易出现过度拟合训练数据的问题(特别是当训练数据集合小的时候)。因为训练样例仅仅是所有可能实例的一个样本,向树增加分支可能提高在训练样例上的性能,但却降低在训练实例外的其他实例上的性能。因此,通常需要后修剪决策树来防止过度拟合训练集,一般来说,这可以通过划分一个验证集来观测修剪。

你可能感兴趣的:(决策树,算法,python,机器学习,人工智能)