决策树挑出好西瓜

本文深入探讨了决策树算法,包括ID3的基本原理、信息熵和信息增益,以及C4.5对ID3的改进,如信息增益率和处理连续特征。CART算法则使用基尼系数,适用于分类和回归任务。此外,还介绍了决策树的剪枝策略,以防止过拟合。通过西瓜分类案例展示了ID3算法的实现过程。
摘要由CSDN通过智能技术生成

一、决策树

在这里插入图片描述

1、决策树(Decision Tree)是一类常见的机器学习方法,是一种非常常用的分类方法,它是一种监督学习。常见的决策树算法有ID3,C4.5、C5.0和CART(classification and regression tree),CART的分类效果一般要优于其他决策树。

2、决策树是基于树状结构来进行决策的,一般地,一棵决策树包含一个根节点、若干个内部节点和若干个叶节点。

3、 每个内部节点表示一个属性上的判断 每个分支代表一个判断结果的输出 每个叶节点代表一种分类结果。 根节点包含样本全集
4、决策树学习的目的是为了产生一棵泛化能力强,即处理未见示例能力强的决策树,其基本流程遵循简单且直观的“分而治之”(divide-and-conquer)策略。
在这里插入图片描述

二、ID3算法理论

(一)算法核心

ID3算法的核心是根据信息增益来选择进行划分的特征,然后递归地构建决策树

(二)特征选择

特征选择也即选择最优划分属性,从当前数据的特征中选择一个特征作为当前节点的划分标准。 随着划分过程不断进行,希望决策树的分支节点所包含的样本尽可能属于同一类别,即节点的“纯度”越来越高。

(三)熵(entropy)

熵表示事务不确定性的程度,也就是信息量的大小(一般说信息量大,就是指这个时候背后的不确定因素太多),熵的公式如下:
在这里插入图片描述
在这里插入图片描述

对于在X的条件下Y的条件熵,是指在X的信息之后,Y这个变量的信息量(不确定性)的大小,计算公式如下:
在这里插入图片描述
在这里插入图片描述
所以当Entropy最大为1的时候,是分类效果最差的状态,当它最小为0的时候,是完全分类的状态。因为熵等于零是理想状态,一般实际情况下,熵介于0和1之间 。

熵的不断最小化,实际上就是提高分类正确率的过程。

(四)信息增益(information gain)

信息增益:在划分数据集之前之后信息发生的变化,计算每个特征值划分数据集获得的信息增益,获得信息增益最高的特征就是最好的选择。

定义属性A对数据集D的信息增益为infoGain(D|A),它等于D本身的熵,减去 给定A的条件下D的条件熵,即:
在这里插入图片描述
在这里插入图片描述
信息增益的意义:引入属性A后,原来数据集D的不确定性减少了多少。

计算每个属性引入后的信息增益,选择给D带来的信息增益最大的属性,即为最优划分属性。一般,信息增益越大,则意味着使用属性A来进行划分所得到的的“纯度提升”越大。

(五)步骤

  • 从根节点开始,计算所有可能的特征的信息增益,选择信息增益最大的特征作为节点的划分特征;
  • 由该特征的不同取值建立子节点;
  • 再对子节点递归1-2步,构建决策树;
  • 直到没有特征可以选择或类别完全相同为止,得到最终的决策树。

三、ID3算法应用举例——西瓜树

(一)西瓜树理论推导

1.西瓜的决策树

在这里插入图片描述
西瓜样本构建决策树模型
在这里插入图片描述
2.利用信息增益选择最优划分属性
(1)西瓜树信息熵
在西瓜样本集中,共有17个样本,其中正样本8个,负样本9个,样本集的信息熵为
在这里插入图片描述
(2)西瓜树信息增量
西瓜样本集中,以属性“色泽”为例,它有3个取值{青绿、乌黑、浅白},对应的子集D1(色泽=青绿)中有6个样本,其中正负样本各3个,D2(色泽=乌黑)中有6个样本,正样本4个,负样本2个,D^3(色泽=浅白)中有5个样本,正样本1个,fuya负样本4个。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述在这里插入图片描述
在这里插入图片描述
同理也可以计算出其他几个属性的信息增益,选择信息增益最大的属性作为根节点来进行划分,然后再对每个分支做进一步划分.

(二)不用sklearn库算法代码

1.创建一个watermlon.ipynb文件
在这里插入图片描述
2.导入python模块

import pandas as pd
import numpy as np
from collections import Counter
from math import log2

3.数据获取和处理函数

#数据获取与处理
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

4.获取属性名称和类别标记

#获取属性名称
def getLabels(data):
    labels = list(data.columns)[1:-1]
    return labels
#获取类别标记
def targetClass(dataSet):
    classification = set([element[-1] for element in dataSet])
    return classification

5.叶节点标记

#将分支结点标记为叶结点,选择样本数最多的类作为类标记
def majorityRule(dataSet):
    mostKind = Counter([element[-1] for element in dataSet]).most_common(1)
    majorityKind = mostKind[0][0]
    return majorityKind

6.计算信息熵

#计算信息熵
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

7.构建子数据集

#子数据集构建
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

8.计算信息增益

#计算信息增益
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

9.选择最优属性

#选择最优属性                
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

10.建立决策树

#建立决策树
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 = 'watermalon.xls'
    data = getData(filePath)
    dataSet = dataDeal(data)
    labels = getLabels(data)
    myTree = createTree(dataSet,labels)
    return myTree

mytree = main()


色泽 1.5798634010685348
根蒂 1.4020814027560324
敲声 1.3328204045850205
纹理 1.4466479595102761
脐部 1.5485652260309184
触感 0.8739810481273587
好瓜 0.9975025463691161
['色泽', '根蒂', '敲声', '纹理', '脐部', '触感', '好瓜']
{'色泽': {}}
浅白
根蒂 1.3709505944546687
敲声 1.3709505944546687
纹理 1.3709505944546687
脐部 0.9709505944546686
触感 0.7219280948873621
好瓜 0.7219280948873621
['根蒂', '敲声', '纹理', '脐部', '触感', '好瓜']
{'根蒂': {}}
稍蜷
{'根蒂': {'稍蜷': nan}}
硬挺
{'根蒂': {'稍蜷': nan, '硬挺': nan}}
蜷缩
敲声 0.0
纹理 0.9182958340544894
脐部 0.9182958340544894
触感 0.9182958340544894
好瓜 0.9182958340544894
['敲声', '纹理', '脐部', '触感', '好瓜']
{'纹理': {}}
清晰
{'纹理': {'清晰': nan}}
模糊
敲声 0.0
脐部 0.0
触感 1.0
好瓜 0.0
['敲声', '脐部', '触感', '好瓜']
{'触感': {}}
软粘
{'触感': {'软粘': nan}}
硬滑
{'色泽': {'浅白': {'根蒂': {'稍蜷': nan, '硬挺': nan, '蜷缩': {'纹理': {'清晰': nan, '模糊': {'触感': {'软粘': nan, '硬滑': nan}}}}}}}}
青绿
根蒂 1.4591479170272448
敲声 1.4591479170272448
纹理 0.9182958340544896
脐部 1.4591479170272448
触感 0.9182958340544896
好瓜 1.0
['根蒂', '敲声', '纹理', '脐部', '触感', '好瓜']
{'根蒂': {}}
稍蜷
敲声 0.0
纹理 1.0
脐部 1.0
触感 1.0
好瓜 1.0
['敲声', '纹理', '脐部', '触感', '好瓜']
{'纹理': {}}
清晰
{'纹理': {'清晰': nan}}
稍糊
{'根蒂': {'稍蜷': {'纹理': {'清晰': nan, '稍糊': nan}}}}
硬挺
{'根蒂': {'稍蜷': {'纹理': {'清晰': nan, '稍糊': nan}}, '硬挺': nan}}
蜷缩
敲声 0.9182958340544894
纹理 0.9182958340544894
脐部 0.9182958340544894
触感 0.0
好瓜 0.9182958340544894
['敲声', '纹理', '脐部', '触感', '好瓜']
{'敲声': {}}
浊响
{'敲声': {'浊响': nan}}
沉闷
纹理 1.0
脐部 1.0
触感 0.0
好瓜 1.0
['纹理', '脐部', '触感', '好瓜']
{'纹理': {}}
清晰
{'纹理': {'清晰': nan}}
稍糊
{'色泽': {'浅白': {'根蒂': {'稍蜷': nan, '硬挺': nan, '蜷缩': {'纹理': {'清晰': nan, '模糊': {'触感': {'软粘': nan, '硬滑': nan}}}}}}, '青绿': {'根蒂': {'稍蜷': {'纹理': {'清晰': nan, '稍糊': nan}}, '硬挺': nan, '蜷缩': {'敲声': {'浊响': nan, '沉闷': {'纹理': {'清晰': nan, '稍糊': nan}}}}}}}}
乌黑
根蒂 0.9182958340544896
敲声 0.9182958340544896
纹理 0.9182958340544896
脐部 0.9182958340544896
触感 0.9182958340544896
好瓜 0.9182958340544896
['根蒂', '敲声', '纹理', '脐部', '触感', '好瓜']
{'根蒂': {}}
稍蜷
敲声 0.8112781244591329
纹理 1.0
脐部 0.0
触感 1.0
好瓜 1.0
['敲声', '纹理', '脐部', '触感', '好瓜']
{'纹理': {}}
清晰
敲声 0.0
脐部 0.0
触感 1.0
好瓜 1.0
['敲声', '脐部', '触感', '好瓜']
{'触感': {}}
软粘
{'触感': {'软粘': nan}}
硬滑
{'纹理': {'清晰': {'触感': {'软粘': nan, '硬滑': nan}}}}
稍糊
敲声 1.0
脐部 0.0
触感 1.0
好瓜 1.0
['敲声', '脐部', '触感', '好瓜']
{'敲声': {}}
浊响
{'敲声': {'浊响': nan}}
沉闷
{'根蒂': {'稍蜷': {'纹理': {'清晰': {'触感': {'软粘': nan, '硬滑': nan}}, '稍糊': {'敲声': {'浊响': nan, '沉闷': nan}}}}}}
蜷缩
敲声 1.0
纹理 0.0
脐部 0.0
触感 0.0
好瓜 0.0
['敲声', '纹理', '脐部', '触感', '好瓜']
{'敲声': {}}
浊响
{'敲声': {'浊响': nan}}
沉闷

11.调用函数

print(mytree)

在这里插入图片描述
在这里插入图片描述

(三)用sklearn库算法代码

1.数据集如下
在这里插入图片描述

2.导入库

导入相关库
import pandas as pd
import graphviz 
from sklearn.model_selection import train_test_split
from sklearn import tree

f = open('C:/Users/86199/Jupyter/watermalon.csv','r')
data = pd.read_csv(f)

x = data[["色泽","根蒂","敲声","纹理","脐部","触感"]].copy()
y = data['好瓜'].copy()
print(data)

在这里插入图片描述
3.数据转换

#将特征值数值化
x = x.copy()
for i in ["色泽","根蒂","敲声","纹理","脐部","触感"]:
    for j in range(len(x)):
        if(x[i][j] == "青绿" or x[i][j] == "蜷缩" or data[i][j] == "浊响" \
           or x[i][j] == "清晰" or x[i][j] == "凹陷" or x[i][j] == "硬滑"):
            x[i][j] = 1
        elif(x[i][j] == "乌黑" or x[i][j] == "稍蜷" or data[i][j] == "沉闷" \
           or x[i][j] == "稍糊" or x[i][j] == "稍凹" or x[i][j] == "软粘"):
            x[i][j] = 2
        else:
            x[i][j] = 3
            
y = y.copy()
for i in range(len(y)):
    if(y[i] == "是"):
        y[i] = int(1)
    else:
        y[i] = int(-1) 
#需要将数据x,y转化好格式,数据框dataframe,否则格式报错
x = pd.DataFrame(x).astype(int)
y = pd.DataFrame(y).astype(int)
print(x)
print(y)


在这里插入图片描述

4.建立模型并训练

x_train, x_test, y_train, y_test = train_test_split(x,y,test_size=0.2)
print(x_train)

在这里插入图片描述

5.训练结果
当选择“根蒂",“敲声”,“纹理”,“脐部”,"触感”进行训练时

#决策树学习
clf = tree.DecisionTreeClassifier(criterion="entropy")                    #实例化 
clf = clf.fit(x_train, y_train) 
score = clf.score(x_test, y_test)
print(score)

在这里插入图片描述

四、决策树算法——C4.5

(一)对比ID3的改进点

C4.5算法是用于生成决策树的一种经典算法,是ID3算法的一种延伸和优化。C4.5算法对ID3算法进行了改进 ,改进点主要有:

  • 用信息增益率来选择划分特征,克服了用信息增益选择的不足,
  • 信息增益率对可取值数目较少的属性有所偏好;
  • 能够处理离散型和连续型的属性类型,即将连续型的属性进行离散化处理;
  • 能够处理具有缺失属性值的训练数据;
  • 在构造树的过程中进行剪枝;

(二)特征选择

特征选择也即选择最优划分属性,从当前数据的特征中选择一个特征作为当前节点的划分标准。 随着划分过程不断进行,希望决策树的分支节点所包含的样本尽可能属于同一类别,即节点的“纯度”越来越高。

(三)信息增益率

信息增益准则对可取值数目较多的属性有所偏好,为减少这种偏好可能带来的不利影响,C4.5算法采用信息增益率来选择最优划分属性。增益率公式
在这里插入图片描述
在这里插入图片描述
信息增益率准则对可取值数目较少的属性有所偏好。所以,C4.5算法不是直接选择信息增益率最大的候选划分属性,而是先从候选划分属性中找出信息增益高于平均水平的属性,再从中选择信息增益率最高的。

(四)对连续特征的处理

  • 当属性类型为离散型,无须对数据进行离散化处理;

  • 当属性类型为连续型,则需要对数据进行离散化处理。具体思路如下:

(五)对缺失值的处理

ID3算法不能处理缺失值,而C4.5算法可以处理缺失值(常用概率权重方法),主要有三种情况,具体如下:

  • 1.在有缺失值的特征上如何计算信息增益率?
    根据缺失比例,折算信息增益(无缺失值样本所占的比例乘以无缺失值样本子集的信息增益)和信息增益率

  • 2.选定了划分属性,若样本在该属性上的值是缺失的,那么如何对这个样本进行划分?
    将样本以不同概率同时划分到不同节点中,概率是根据其他非缺失属性的比例来得到的

  • 3.对新的样本进行分类时,如果测试样本特性有缺失值如何判断其类别?
    走所有分支,计算每个类别的概率,取概率最大的类别赋值给该样本

(六)剪枝

1.剪枝原因

因为过拟合的树在泛化能力的表现非常差。

剪枝又分为前剪枝和后剪枝,前剪枝是指在构造树的过程中就知道哪些节点可以剪掉 。 后剪枝是指构造出完整的决策树之后再来考查哪些子树可以剪掉。

2.剪前枝

在节点划分前确定是否继续增长,及早停止增长的主要方法有:

  • 节点内数据样本数小于切分最小样本数阈值;
  • 所有节点特征都已分裂;
  • 节点划分前准确率比划分后准确率高。 前剪枝不仅可以降低过拟合的风险而且还可以减少训练时间,但另一方面它是基于“贪心”策略,会带来欠拟合风险。

3.剪后枝

在已经生成的决策树上进行剪枝,从而得到简化版的剪枝决策树。

C4.5算法采用悲观剪枝方法。根据剪枝前后的误判率来判定是否进行子树的修剪, 如果剪枝后与剪枝前相比其误判率是保持或者下降,则这棵子树就可以被替换为一个叶子节点。 因此,不需要单独的剪枝数据集。C4.5 通过训练数据集上的错误分类数量来估算未知样本上的错误率。

把一颗子树(具有多个叶子节点)的剪枝后用一个叶子节点来替代的话,在训练集上的误判率肯定是上升的,但是在新数据上不一定。于是我们需要把子树的误判计算加上一个经验性的惩罚因子。对于一颗叶子节点,它覆盖了N个样本,其中有E个错误,那么该叶子节点的错误率为(E+0.5)/N。这个0.5就是惩罚因子,那么一颗子树,它有L个叶子节点,那么该子树的误判率估计为:
在这里插入图片描述
在这里插入图片描述
这样的话,我们可以看到一颗子树虽然具有多个子节点,但由于加上了惩罚因子,所以子树的误判率计算未必占到便宜。剪枝后内部节点变成了叶子节点,其误判个数J也需要加上一个惩罚因子,变成J+0.5。 [公式] 对于样本的误判率e,可以根据经验把它估计成各种各样的分布模型,比如是二项式分布,比如是正态分布。
那么一棵树错误分类一个样本值为1,正确分类一个样本值为0,该树错误分类的概率(误判率)为e,e通过下式来计算 在这里插入图片描述
那么树的误判次数就是伯努利分布,我们可以估计出该树的误判次数的均值和标准差:
在这里插入图片描述
把子树替换成叶子节点后,该叶子的误判次数也是一个伯努利分布,因为子树合并为一个叶子节点了,所以,L=1,将其代入上面计算误判率的公式中,可以得到叶子节点的误判率为 [公式]
因此叶子节点的误判次数均值为
在这里插入图片描述
这里采用一种保守的分裂方案,即有足够大的置信度保证分裂后准确率比不分裂时的准确率高时才分裂,否则就不分裂–也就是应该剪枝。
如果要分裂(即不剪枝)至少要保证分裂后的误判数E(子树误判次数)要小于不分裂的误判数E(叶子节点的误判次数),而且为了保证足够高的置信度,加了一个标准差可以有95%的置信度,所以,要分裂(即不剪枝)需满足如下不等式
在这里插入图片描述
反之就是不分裂,即剪枝的条件:
在这里插入图片描述

五、决策树算法——CART算符

(一)简介

  • ID3和C4.5算法,生成的决策树是多叉树,只能处理分类不能处理回归。而CART(classification and regression tree)分类回归树算法,既可用于分类也可用于回归。 分类树的输出是样本的类别, 回归树的输出是一个实数。
  • ID3中使用了信息增益选择特征,增益大优先选择。C4.5中,采用信息增益率选择特征,减少因特征值多导致信息增益大的问题。CART分类树算法使用基尼系数选择特征,基尼系数代表了模型的不纯度,基尼系数越小,不纯度越低,特征越好。这和信息增益(率)相反。

CART算法步骤

  • 特征选择;
  • 递归建立决策树;
  • 决策树剪枝;

(二)基尼系数

数据集D的纯度可用基尼值来度量

在这里插入图片描述

在这里插入图片描述在这里插入图片描述
在属性A的条件下,样本D的基尼系数定义为
在这里插入图片描述

(三)对连续特征和离散特征的处理

1.连续特征

与C4.5思想相同,都是将连续的特征离散化。区别在选择划分点时,C4.5是信息增益率,CART是基尼系数。

具体思路:
在这里插入图片描述

2.离散特征

思路:不停的二分离散特征
在ID3、C4.5,特征A被选取建立决策树节点,如果它有3个类别A1,A2,A3,我们会在决策树上建立一个三叉点,这样决策树是多叉树,而且离散特征只会参与一次节点的建立。
在这里插入图片描述

六、总结

本次实验了解了决策树的三种算法ID3、C4.5、CART,以及他们各自的优缺点,改进的不同之处, ID3中使用了信息增益选择特征,增益大优先选择。C4.5中,采用信息增益率选择特征,减少因特征值多导致信息增益大的问题。CART分类树算法使用基尼系数选择特征,基尼系数代表了模型的不纯度,基尼系数越小,不纯度越低,特征越好。这和信息增益(率)相反。通过实验进一步了解他们各自的算法实现。

七、参考资料

决策树算法–ID3算法
机器学习笔记(4)——ID3决策树算法及其Python实现-赵赵赵颖
决策树之挑选西瓜
决策树算法–C4.5算法
决策树算法–CART分类树算法

  • 5
    点赞
  • 45
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值