决策树ID3,C4.5,CART算法及实现

文章目录

      • 一.决策树
        • 1.画法
        • 2.决策树的剪枝
        • 3.挑西瓜决策树
          • 3.1利用信息增益选择最优划分属性
          • 3.2python代码实现
      • 二.sk-learn库对西瓜数据集,分别进行ID3、C4.5和CART的算法代码实现
        • 1.ID3算法
        • 2.C4.5算法
        • 3.CART算法
      • 三.总结
      • 四.参考链接:

一.决策树

在机器学习中,决策树是一个预测模型,他代表的是对象属性与对象值之间的一种映射关系。Entropy = 系统的凌乱程度,使用算法ID3, C4.5和C5.0生成树算法使用熵。这一度量是基于信息学理论中熵的概念。

1.画法

机器学习中,决策树是一个预测模型;他代表的是对象属性与对象值之间的一种映射关系。树中每个节点表示某个对象,而每个分叉路径则代表的某个可能的属性值,而每个叶结点则对应从根节点到该叶节点所经历的路径所表示的对象的值。决策树仅有单一输出,若欲有复数输出,可以建立独立的决策树以处理不同输出。数据挖掘中决策树是一种经常要用到的技术,可以用于分析数据,同样也可以用来作预测。

从数据产生决策树的机器学习技术叫做决策树学习, 通俗说就是决策树

一个决策树包含三种类型的节点:

  1. 决策节点:通常用矩形框来表示
  2. 机会节点:通常用圆圈来表示
  3. 终结点:通常用三角形来表示

决策树学习也是资料探勘中一个普通的方法。在这里,每个决策树都表述了一种树型结构,它由它的分支来对该类型的对象依靠属性进行分类。每个决策树可以依靠对源数据库的分割进行数据测试。这个过程可以递归式的对树进行修剪。 当不能再进行分割或一个单独的类可以被应用于某一分支时,递归过程就完成了。另外,随机森林分类器将许多决策树结合起来以提升分类的正确率。

决策树同时也可以依靠计算条件概率来构造。

决策树如果依靠数学的计算方法可以取得更加理想的效果。 数据库已如下所示:

(x, y) = (x1, x2, x3…, xk, y)

相关的变量 Y 表示我们尝试去理解,分类或者更一般化的结果。 其他的变量x1, x2, x3 等则是帮助我们达到目的的变量。

2.决策树的剪枝

剪枝是决策树停止分支的方法之一,剪枝有分预先剪枝和后剪枝两种。预先剪枝是在树的生长过程中设定一个指标,当达到该指标时就停止生长,这样做容易产生“视界局限”,就是一旦停止分支,使得节点N成为叶节点,就断绝了其后继节点进行“好”的分支操作的任何可能性。不严格的说这些已停止的分支会误导学习算法,导致产生的树不纯度降差最大的地方过分靠近根节点。后剪枝中树首先要充分生长,直到叶节点都有最小的不纯度值为止,因而可以克服“视界局限”。然后对所有相邻的成对叶节点考虑是否消去它们,如果消去能引起令人满意的不纯度增长,那么执行消去,并令它们的公共父节点成为新的叶节点。这种“合并”叶节点的做法和节点分支的过程恰好相反,经过剪枝后叶节点常常会分布在很宽的层次上,树也变得非平衡。后剪枝技术的优点是克服了“视界局限”效应,而且无需保留部分样本用于交叉验证,所以可以充分利用全部训练集的信息。但后剪枝的计算量代价比预剪枝方法大得多,特别是在大样本集中,不过对于小样本的情况,后剪枝方法还是优于预剪枝方法的。

最经典的决策树算法有ID3、C4.5、CART,其中ID3算法是最早被提出的,它可以处理离散属性样本的分类,C4.5和CART算法则可以处理更加复杂的分类问题。

3.挑西瓜决策树

一个西瓜具有色泽,根蒂,敲声,纹理,脐部,触感等特征,我们要通过这些特征来判断一个西瓜是否是好瓜,比如我们先以色泽作为第一次判别的特征,然后是声音,就能得到如下决策树:

决策树ID3,C4.5,CART算法及实现_第1张图片

如上所述,接下来以如下数据构建决策树模型:

编号 色泽 根蒂 敲声 纹理 脐部 触感 好瓜
1 青绿 蜷缩 浊响 清晰 凹陷 硬滑
2 乌黑 蜷缩 沉闷 清晰 凹陷 硬滑
3 乌黑 蜷缩 浊响 清晰 凹陷 硬滑
4 青绿 蜷缩 沉闷 清晰 凹陷 硬滑
5 浅白 蜷缩 浊响 清晰 凹陷 硬滑
6 青绿 稍蜷 浊响 清晰 稍凹 软粘
7 乌黑 稍蜷 浊响 稍糊 稍凹 软粘
8 乌黑 稍蜷 浊响 清晰 稍凹 硬滑
9 乌黑 稍蜷 沉闷 稍糊 稍凹 硬滑
10 青绿 硬挺 清脆 清晰 平坦 软粘
11 浅白 硬挺 清脆 模糊 平坦 硬滑
12 浅白 蜷缩 浊响 模糊 平坦 软粘
13 青绿 稍蜷 浊响 稍糊 凹陷 硬滑
14 浅白 稍蜷 沉闷 稍糊 凹陷 硬滑
15 乌黑 稍蜷 浊响 清晰 稍凹 软粘
16 浅白 蜷缩 浊响 模糊 平坦 硬滑
17 青绿 蜷缩 沉闷 稍糊 稍凹 硬滑
3.1利用信息增益选择最优划分属性

样本有多个属性,该先选哪个样本来划分数据集呢?原则是随着划分不断进行,我们希望决策树的分支节点所包含的样本尽可能属于同一分类,即“纯度”越来越高。先来学习一下“信息熵”和“信息增益”。

信息熵

样本集合D中第k类样本所占的比例(k=1,2,…,|Y|),|Y|为样本分类的个数,则D的信息熵为:

在这里插入图片描述

Ent(D)的值越小,则D的纯度越高。直观理解一下:假设样本集合有2个分类,每类样本的比例为1/2,Ent(D)=1;只有一个分类,Ent(D)= 0,显然后者比前者的纯度高。

在西瓜样本集中,共有17个样本,其中正样本8个,负样本9个,样本集的信息熵为:

在这里插入图片描述

信息增益

使用属性a对样本集D进行划分所获得的“信息增益”的计算方法是,用样本集的总信息熵减去属性a的每个分支的信息熵与权重(该分支的样本数除以总样本数)的乘积,通常,信息增益越大,意味着用属性a进行划分所获得的“纯度提升”越大。因此,优先选择信息增益最大的属性来划分。设属性a有V个可能的取值,则属性a的信息增益为:

在这里插入图片描述

西瓜样本集中,以属性“色泽”为例,它有3个取值{青绿、乌黑、浅白},对应的子集D^1(色泽=青绿)中有6个样本,其中正负样本各3个,D^2(色泽=乌黑)中有6个样本,正样本4个,负样本2个,D^3(色泽=浅白)中有5个样本,正样本1个,fuya负样本4个。

决策树ID3,C4.5,CART算法及实现_第2张图片

信息增益率

基于信息增益的最优属性划分原则——信息增益准则,对可取值数据较多的属性有所偏好。C4.5算法使用增益率替代信息增益来选择最优划分属性,增益率定义为:

​ Gain_ratio(D,a) = Gain(D,a)/IV(a)

其中

​ IV(a) = -∑v=1 |Dv|/|D|·log2 |Dv|/|D|

称为属性a的固有值。属性a的可能取值数目越多(即V越大),则IV(a)的值通常会越大。这在一定程度上消除了对可取值数据较多的属性的偏好。

事实上,增益率准则对可取值数目较少的属性有所偏好,C4.5算法并不是直接使用增益率准则,而是先从候选划分属性中找出信息增益高于平均水平的属性,再从中选择增益率最高的。

基尼指数

CART决策树算法使用基尼指数来选择划分属性,基尼指数定义为:

​ Gini(D) = ∑k=1 ∑k’≠1 pk·pk’ = 1- ∑k=1 pk·pk

可以这样理解基尼指数:从数据集D中随机抽取两个样本,其类别标记不一致的概率。Gini(D)越小,纯度越高。

属性a的基尼指数定义:

​ Gain_index(D,a) = ∑v=1 |Dv|/|D|·Gini(Dv)

使用基尼指数选择最优划分属性,即选择使得划分后基尼指数最小的属性作为最优划分属性。

3.2python代码实现
#导入模块
import pandas as pd
import numpy as np
from collections import Counter
from math import log2

#数据获取与处理
def getData(filePath):
    data = pd.read_excel(filePath)
    return data

def dataDeal(data):
    dataList = np.array(data).tolist()
    dataSet = [element[1:] for element in dataList]
    return dataSet
    
    #获取属性名称
def getLabels(data):
    labels = list(data.columns)[1:-1]
    return labels
    
    #获取类别标记
def targetClass(dataSet):
    classification = set([element[-1] for element in dataSet])
    return classification
    
    #将分支结点标记为叶结点,选择样本数最多的类作为类标记
def majorityRule(dataSet):
    mostKind = Counter([element[-1] for element in dataSet]).most_common(1)
    majorityKind = mostKind[0][0]
    return majorityKind
    
    #计算信息熵
def infoEntropy(dataSet):
    classColumnCnt = Counter([element[-1] for element in dataSet])
    Ent = 0
    for symbol in classColumnCnt:
        p_k = classColumnCnt[symbol]/len(dataSet)
        Ent = Ent-p_k*log2(p_k)
    return Ent
    
    #子数据集构建
def makeAttributeData(dataSet,value,iColumn):
    attributeData = []
    for element in dataSet:
        if element[iColumn]==value:
            row = element[:iColumn]
            row.extend(element[iColumn+1:])
            attributeData.append(row)
    return attributeData
    
    #计算信息增益
def infoGain(dataSet,iColumn):
    Ent = infoEntropy(dataSet)
    tempGain = 0.0
    attribute = set([element[iColumn] for element in dataSet])
    for value in attribute:
        attributeData = makeAttributeData(dataSet,value,iColumn)
        tempGain = tempGain+len(attributeData)/len(dataSet)*infoEntropy(attributeData)
        Gain = Ent-tempGain
    return Gain
    
    #选择最优属性                
def selectOptimalAttribute(dataSet,labels):
    bestGain = 0
    sequence = 0
    for iColumn in range(0,len(labels)):#不计最后的类别列
        Gain = infoGain(dataSet,iColumn)
        if Gain>bestGain:
            bestGain = Gain
            sequence = iColumn
        print(labels[iColumn],Gain)
    return sequence
    
    #建立决策树
def createTree(dataSet,labels):
    classification = targetClass(dataSet) #获取类别种类(集合去重)
    if len(classification) == 1:
        return list(classification)[0]
    if len(labels) == 1:
        return majorityRule(dataSet)#返回样本种类较多的类别
    sequence = selectOptimalAttribute(dataSet,labels)
    print(labels)
    optimalAttribute = labels[sequence]
    del(labels[sequence])
    myTree = {optimalAttribute:{}}
    attribute = set([element[sequence] for element in dataSet])
    for value in attribute:
        
        print(myTree)
        print(value)
        subLabels = labels[:]
        myTree[optimalAttribute][value] =  \
                createTree(makeAttributeData(dataSet,value,sequence),subLabels)
    return myTree
    
    def main():
    filePath = 'D:/watermelondata.xls'
    data = getData(filePath)
    dataSet = dataDeal(data)
    labels = getLabels(data)
    myTree = createTree(dataSet,labels)
    return myTree
    
    if __name__ == '__main__':
    myTree = main()

输出结果:

色泽 0.10812516526536531
根蒂 0.14267495956679277
敲声 0.14078143361499584
纹理 0.3805918973682686
脐部 0.28915878284167895
触感 0.006046489176565584
['色泽', '根蒂', '敲声', '纹理', '脐部', '触感']
{'纹理': {}}
稍糊
色泽 0.3219280948873623
根蒂 0.07290559532005603
敲声 0.3219280948873623
脐部 0.17095059445466865
触感 0.7219280948873623
['色泽', '根蒂', '敲声', '脐部', '触感']
{'触感': {}}
硬滑
{'触感': {'硬滑': '否'}}
软粘
{'纹理': {'稍糊': {'触感': {'硬滑': '否', '软粘': '是'}}}}
模糊
{'纹理': {'稍糊': {'触感': {'硬滑': '否', '软粘': '是'}}, '模糊': '否'}}
清晰
色泽 0.04306839587828004
根蒂 0.45810589515712374
敲声 0.33085622540971754
脐部 0.45810589515712374
触感 0.45810589515712374
['色泽', '根蒂', '敲声', '脐部', '触感']
{'根蒂': {}}
硬挺
{'根蒂': {'硬挺': '否'}}
稍蜷
色泽 0.2516291673878229
敲声 0.0
脐部 0.0
触感 0.2516291673878229
['色泽', '敲声', '脐部', '触感']
{'色泽': {}}
乌黑
敲声 0.0
脐部 0.0
触感 1.0
['敲声', '脐部', '触感']
{'触感': {}}
硬滑
{'触感': {'硬滑': '是'}}
软粘
{'色泽': {'乌黑': {'触感': {'硬滑': '是', '软粘': '否'}}}}
青绿
{'根蒂': {'硬挺': '否', '稍蜷': {'色泽': {'乌黑': {'触感': {'硬滑': '是', '软粘': '否'}}, '青绿': '是'}}}}
蜷缩

决策树ID3,C4.5,CART算法及实现_第3张图片

结果分析:

特征 信息增益
色泽 0.10812516526536531
根蒂 0.14267495956679277
敲声 0.14078143361499584
纹理 0.3805918973682686
脐部 0.28915878284167895
触感 0.006046489176565584

纹理的信息增益是最大的,所以将纹理作为根节点进行分类更合适。

二.sk-learn库对西瓜数据集,分别进行ID3、C4.5和CART的算法代码实现

1.ID3算法

熵和信息增益

设S是训练样本集,它包括n个类别的样本,这些方法用Ci表示,那么熵和信息增益用下面公式表示:
信息熵:
决策树ID3,C4.5,CART算法及实现_第4张图片

其中pi表示Ci的概率
样本熵:
决策树ID3,C4.5,CART算法及实现_第5张图片

其中Si表示根据属性A划分的S的第i个子集,S和Si表示样本数目
信息增益:
在这里插入图片描述

ID3中样本分布越均匀,它的信息熵就越大,所以其原则就是样本熵越小越好,也就是信息增益越大越好。

实现代码

# 读取西瓜数据集
import numpy as np
import pandas as pd
df = pd.read_table(r'D:/watermelon.txt',encoding='utf8',delimiter=',',index_col=0)
df.head()
# 由于上面的数据中包含了中文汉字,所以需要对数据进一步处理
'''
属性:
色泽 1-3代表 浅白 青绿 乌黑 根蒂 1-3代表 稍蜷 蜷缩 硬挺
敲声 1-3代表 清脆 浊响 沉闷 纹理 1-3代表 清晰 稍糊 模糊
脐部 1-3代表 平坦 稍凹 凹陷 触感 1-2代表 硬滑 软粘
标签:
好瓜 1代表 是 0 代表 不是
'''
df['色泽']=df['色泽'].map({'浅白':1,'青绿':2,'乌黑':3})
df['根蒂']=df['根蒂'].map({'稍蜷':1,'蜷缩':2,'硬挺':3})
df['敲声']=df['敲声'].map({'清脆':1,'浊响':2,'沉闷':3})
df['纹理']=df['纹理'].map({'清晰':1,'稍糊':2,'模糊':3})
df['脐部']=df['脐部'].map({'平坦':1,'稍凹':2,'凹陷':3})
df['触感'] = np.where(df['触感']=="硬滑",1,2)
df['好瓜'] = np.where(df['好瓜']=="是",1,0)
#由于西瓜数据集样本比较少,所以不划分数据集,将所有的西瓜数据用来训练模型
Xtrain = df.iloc[:,:-1]
Xtrain = np.array(Xtrain)
Ytrain = df.iloc[:,-1]
# 调用sklearn内置的决策树的库和画图工具
from sklearn import tree
import graphviz
# 采用ID3算法,利用信息熵构建决策树模型
clf = tree.DecisionTreeClassifier(criterion="entropy")
clf = clf.fit(Xtrain,Ytrain)
# 绘制决策树的图形
feature_names = ["色泽","根蒂","敲声","纹理","脐部","触感"]
dot_data = tree.export_graphviz(clf
,feature_names=feature_names
,class_names=["好瓜","坏瓜"]
,filled=True
,rounded=True
)
graph = graphviz.Source(dot_data)
graph

结果:

决策树ID3,C4.5,CART算法及实现_第6张图片

2.C4.5算法

C4.5算法的基本流程与ID3类似,但C4.5算法进行特征选择时不是通过计算信息增益完成的,而是通过信息增益比来进行特征选择。

信息增益比

训练数据关于特征的信息增益为,关于的熵为,,则关于的信息增益比为信息增益为与熵的比值:

在这里插入图片描述

通过上面的定义,我们可以将信息增益比理解为信息增益的标准化,特征之间的信息增益比可比较性更强一些。同时我们要注意区分关于类别的熵,关于特征的条件熵和关于特征的熵。

决策树构建

输入:训练数据D={(x1,y1),⋯,(xN,yN)},特征集AA,信息增益比阈值ϵϵ
输出:决策树T
step1 若D中样本特征为空,那么树T为一棵单结点树,将样本数最大的类别作为树的类别,返回T;否则,转到step2。
step2 计算训练集D关于所有特征的信息增益比,若信息增益比均小于阈值ϵ,则TT是一棵单结点树,将样本数最大的类别作为树的类别,返回T;否则,转到step3。
step3 选择信息增益比最大的特征Amax作为根结点特征,依据Amax的所有可能值建立相应子结点,若子结点的样本全部属于同一类别,则子结点为叶结点,将叶结点类别标记为该结点所有样本所属的类别,若所有子结点均为叶结点,返回T;否则,返回T并转到step4。
step4 对非叶子结点i,以Di为训练数据集,A=A−{Amax}为特征集,递归地调用step1-step3,返回子树Ti。

剪枝
通过信息增益比计算得到的决策树也需要进行剪枝,剪枝方法同ID3算法。

3.CART算法

CART算法构造的是二叉决策树,决策树构造出来后同样需要剪枝,才能更好的应用于未知数据的分类。CART算法在构造决策树时通过基尼系数来进行特征选择。

基尼指数

**基尼指数:**若某样本数据分为类,数据属于第k类的概率为,则样本数据的基尼指数定义为:

决策树ID3,C4.5,CART算法及实现_第7张图片

对于训练样本集,其基尼指数如下:

决策树ID3,C4.5,CART算法及实现_第8张图片

如果样本集合D根据特征Am的取值可以分为两部分D1和D2,那么在特征Am的条件下,D的基尼指数如下:

决策树ID3,C4.5,CART算法及实现_第9张图片

基尼指数Gini(D)表征着数据集D的不确定性,而在特征Am的条件下,D的基尼指数则表征着在特征Am确定的条件下D的不确定性,因此基尼指数之差和信息增益及信息增益比一样,可以表征特征Am对数据集D的分类的能力。

决策树构建

输入: 训练数据集D,特征集A,结点样本数阈值δ,基尼指数阈值ϵ
输出: CART二叉决策树TT
step1 若样本特征集为空,则TT是一棵单节点数,其类别为D中样本数最多的类,返回T;否则,转到step2。
step2 对数据集D,计算特征集A中所有特征所有可能切分点的基尼指数,若基尼指数的值均小于给定阈值ϵ,则TT是一棵单节点数,其类别为D中样本数最多的类,返回T;否则,转到step3。
step3 选择基尼指数最小的特征Amin和相应切分点α作为根节点的特征值和切分标准,根据D中样本是否等于α(或≤α≤α,或≥α≥α)将D分为两个子集D1和D2,将D1和D2分别分配到两个子结点中,若子结点样本数均小于给定阈值δ,则该子结点是一个叶结点,若两个子结点均为叶结点,返回T;否则,返回T并转到step4。
step4 对于非叶子结点,令D等于该子结点所对应的数据集,特征集A=A−{Amin},递归的调用step1-step3,返回子树T。

剪枝

CART算法生成的决策树同样需要剪枝,剪枝的方式是先进行剪枝形成一系列子树,再用独立的验证数据集对这些子树进行验证,找到对于验证数据集基尼指数最小的树,将作为最优决策树。在那些结点剪枝是通过损失函数计算的,损失函数形式如下:
Cα=C(T)+α|T|α≥0

其中[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VMX5rxUO-1635167469863)(C:\Users\28205\Desktop\杂项\16.png)],它是决策树对训练数据的分类误差,|T|是决策树结点总数,α是平衡两者的参数,α越大,优化后的决策树越简单(结点越少),α越小,优化后的决策树对训练数据的分类误差越小。
对于整体二叉决策树T的某个内部结点t,以t为单结点树的损失函数是Cα(t)=C(t)+α,以t为根结点的子树Tt的损失函数是Cα(Tt)=C(Tt)+α|Tt|,当α充分小时,有Cα(Tt)Cα(t)。当Cα(Tt)=Cα(t)时,α=C(t)−C(Tt)|Tt|−1,此时,t与Tt有相同的损失函数,但t的结构更简单,因此剪掉子树Tt,构建子树序列T0,T1,⋯,TL的方法就是基于这种思想的。
α0=0,对决策树T0,对于其所有内部结点tt,计算
g(t)=C(t)−C(Tt)|Tt|−1

g(t)的最小值为α1,T0为区间[α0,α1)上的最优子树,g(t)取最小值处的结点为t1,剪掉以t1为根结点的子树Tt1即得到T1,T1为区间[α1,α2)上的最优子树。重复上述操作,直到得到的最优子树为一个根结点和两个叶结点构成的树,此时我们得到了一系列的αα值:0=α0<α1<⋯<αL,和区间[α0,α1),[α1,α2),⋯,[αL,∞)上的最优子树序列:T0,T1,⋯,TL。
然后利用验证数据集对最优子树序列进行测试,选择对于验证数据集有最小基尼指数的最优子树作为最终的最优决策树。若最终Tl为最优决策树,那么其所对应的αl就是损失函数的参数α。

输入: CART算法生成的决策树T
输出: 最优决策树Tα
step1 若T为单结点树或由一个根结点和两个叶结点构成的树,返回T;否则,k=0,α0=0,T0=T,转到step2。
step2 k=k+1,计算Tk−1所有内部结点t的g(t)值,g(t)的最小值为αk,g(t)取最小值处的结点为tk,剪掉以tk为根结点的子树Ttk,并以tk处类别最多的样本作为新的叶结点tk的类,得到[αk,αk+1)上的最优子树Tk。
step3 若Tk是由一个根结点和两个叶结点构成的树,转到step4;否则,转到step2。
step4 利用验证数据集对最优子树序列T0,T1,⋯,TL进行测试,选择对于验证数据集有最小基尼指数的最优子树作为最终的最优决策树Tα,返回Tα。

三.总结

通过学习进一步了解ID3,C4.5,CART算法之间的优点和缺点。

四.参考链接:

https://blog.csdn.net/leaf_zizi/article/details/82848682

https://www.cnblogs.com/dennis-liucd/p/7905793.html

https://blog.csdn.net/keyue123/article/details/82253538

https://blog.csdn.net/qq_41775769/article/details/110822101

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