用python实现红酒数据集的ID3,C4.5和CART算法?

Python030

用python实现红酒数据集的ID3,C4.5和CART算法?,第1张

ID3算法介绍

ID3算法全称为迭代二叉树3代算法(Iterative Dichotomiser 3)

该算法要先进行特征选择,再生成决策树,其中特征选择是基于“信息增益”最大的原则进行的。

但由于决策树完全基于训练集生成的,有可能对训练集过于“依赖”,即产生过拟合现象。因此在生成决策树后,需要对决策树进行剪枝。剪枝有两种形式,分别为前剪枝(Pre-Pruning)和后剪枝(Post-Pruning),一般采用后剪枝。

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

信息熵:来自于香农定理,表示信息集合所含信息的平均不确定性。信息熵越大,表示不确定性越大,所含的信息量也就越大。

设x 1 , x 2 , x 3 , . . . x n {x_1, x_2, x_3, ...x_n}x

1

,x

2

,x

3

,...x

n

为信息集合X的n个取值,则x i x_ix

i

的概率:

P ( X = i ) = p i , i = 1 , 2 , 3 , . . . , n P(X=i) = p_i, i=1,2,3,...,n

P(X=i)=p

i

,i=1,2,3,...,n

信息集合X的信息熵为:

H ( X ) = − ∑ i = 1 n p i log ⁡ p i H(X) =- \sum_{i=1}^{n}{p_i}\log{p_i}

H(X)=−

i=1

n

p

i

logp

i

条件熵:指已知某个随机变量的情况下,信息集合的信息熵。

设信息集合X中有y 1 , y 2 , y 3 , . . . y m {y_1, y_2, y_3, ...y_m}y

1

,y

2

,y

3

,...y

m

组成的随机变量集合Y,则随机变量(X,Y)的联合概率分布为

P ( x = i , y = j ) = p i j P(x=i,y=j) = p_{ij}

P(x=i,y=j)=p

ij

条件熵:

H ( X ∣ Y ) = ∑ j = 1 m p ( y j ) H ( X ∣ y j ) H(X|Y) = \sum_{j=1}^m{p(y_j)H(X|y_j)}

H(X∣Y)=

j=1

m

p(y

j

)H(X∣y

j

)

H ( X ∣ y j ) = − ∑ j = 1 m p ( y j ) ∑ i = 1 n p ( x i ∣ y j ) log ⁡ p ( x i ∣ y j ) H(X|y_j) = - \sum_{j=1}^m{p(y_j)}\sum_{i=1}^n{p(x_i|y_j)}\log{p(x_i|y_j)}

H(X∣y

j

)=−

j=1

m

p(y

j

)

i=1

n

p(x

i

∣y

j

)logp(x

i

∣y

j

)

和贝叶斯公式:

p ( x i y j ) = p ( x i ∣ y j ) p ( y j ) p(x_iy_j) = p(x_i|y_j)p(y_j)

p(x

i

y

j

)=p(x

i

∣y

j

)p(y

j

)

可以化简条件熵的计算公式为:

H ( X ∣ Y ) = ∑ j = 1 m ∑ i = 1 n p ( x i , y j ) log ⁡ p ( x i ) p ( x i , y j ) H(X|Y) = \sum_{j=1}^m \sum_{i=1}^n{p(x_i, y_j)\log\frac{p(x_i)}{p(x_i, y_j)}}

H(X∣Y)=

j=1

m

i=1

n

p(x

i

,y

j

)log

p(x

i

,y

j

)

p(x

i

)

信息增益:信息熵-条件熵,用于衡量在知道已知随机变量后,信息不确定性减小越大。

d ( X , Y ) = H ( X ) − H ( X ∣ Y ) d(X,Y) = H(X) - H(X|Y)

d(X,Y)=H(X)−H(X∣Y)

python代码实现

import numpy as np

import math

def calShannonEnt(dataSet):

""" 计算信息熵 """

labelCountDict = {}

for d in dataSet:

label = d[-1]

if label not in labelCountDict.keys():

labelCountDict[label] = 1

else:

labelCountDict[label] += 1

entropy = 0.0

for l, c in labelCountDict.items():

p = 1.0 * c / len(dataSet)

entropy -= p * math.log(p, 2)

return entropy

def filterSubDataSet(dataSet, colIndex, value):

"""返回colIndex特征列label等于value,并且过滤掉改特征列的数据集"""

subDataSetList = []

for r in dataSet:

if r[colIndex] == value:

newR = r[:colIndex]

newR = np.append(newR, (r[colIndex + 1:]))

subDataSetList.append(newR)

return np.array(subDataSetList)

def chooseFeature(dataSet):

""" 通过计算信息增益选择最合适的特征"""

featureNum = dataSet.shape[1] - 1

entropy = calShannonEnt(dataSet)

bestInfoGain = 0.0

bestFeatureIndex = -1

for i in range(featureNum):

uniqueValues = np.unique(dataSet[:, i])

condition_entropy = 0.0

for v in uniqueValues: #计算条件熵

subDataSet = filterSubDataSet(dataSet, i, v)

p = 1.0 * len(subDataSet) / len(dataSet)

condition_entropy += p * calShannonEnt(subDataSet)

infoGain = entropy - condition_entropy#计算信息增益

if infoGain >= bestInfoGain:#选择最大信息增益

bestInfoGain = infoGain

bestFeatureIndex = i

return bestFeatureIndex

def creatDecisionTree(dataSet, featNames):

""" 通过训练集生成决策树 """

featureName = featNames[:]# 拷贝featNames,此处不能直接用赋值操作,否则新变量会指向旧变量的地址

classList = list(dataSet[:, -1])

if len(set(classList)) == 1:# 只有一个类别

return classList[0]

if dataSet.shape[1] == 1:#当所有特征属性都利用完仍然无法判断样本属于哪一类,此时归为该数据集中数量最多的那一类

return max(set(classList), key=classList.count)

bestFeatureIndex = chooseFeature(dataSet)#选择特征

bestFeatureName = featNames[bestFeatureIndex]

del featureName[bestFeatureIndex]#移除已选特征列

decisionTree = {bestFeatureName: {}}

featureValueUnique = sorted(set(dataSet[:, bestFeatureIndex]))#已选特征列所包含的类别, 通过递归生成决策树

for v in featureValueUnique:

copyFeatureName = featureName[:]

subDataSet = filterSubDataSet(dataSet, bestFeatureIndex, v)

decisionTree[bestFeatureName][v] = creatDecisionTree(subDataSet, copyFeatureName)

return decisionTree

def classify(decisionTree, featnames, featList):

""" 使用训练所得的决策树进行分类 """

classLabel = None

root = decisionTree.keys()[0]

firstGenDict = decisionTree[root]

featIndex = featnames.index(root)

for k in firstGenDict.keys():

if featList[featIndex] == k:

if isinstance(firstGenDict[k], dict): #若子节点仍是树,则递归查找

classLabel = classify(firstGenDict[k], featnames, featList)

else:

classLabel = firstGenDict[k]

return classLabel

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

下面用鸢尾花数据集对该算法进行测试。由于ID3算法只能用于标称型数据,因此用在对连续型的数值数据上时,还需要对数据进行离散化,离散化的方法稍后说明,此处为了简化,先使用每一种特征所有连续性数值的中值作为分界点,小于中值的标记为1,大于中值的标记为0。训练1000次,统计准确率均值。

from sklearn import datasets

from sklearn.model_selection import train_test_split

iris = datasets.load_iris()

data = np.c_[iris.data, iris.target]

scoreL = []

for i in range(1000): #对该过程进行10000次

trainData, testData = train_test_split(data) #区分测试集和训练集

featNames = iris.feature_names[:]

for i in range(trainData.shape[1] - 1): #对训练集每个特征,以中值为分界点进行离散化

splitPoint = np.mean(trainData[:, i])

featNames[i] = featNames[i]+'<='+'{:.3f}'.format(splitPoint)

trainData[:, i] = [1 if x <= splitPoint else 0 for x in trainData[:, i]]

testData[:, i] = [1 if x <= splitPoint else 0 for x in testData[:, i]]

decisionTree = creatDecisionTree(trainData, featNames)

classifyLable = [classify(decisionTree, featNames, td) for td in testData]

scoreL.append(1.0 * sum(classifyLable == testData[:, -1]) / len(classifyLable))

print 'score: ', np.mean(scoreL)

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

输出结果为:score: 0.7335,即准确率有73%。每次训练和预测的准确率分布如下:

数据离散化

然而,在上例中对特征值离散化的划分点实际上过于“野蛮”,此处介绍一种通过信息增益最大的标准来对数据进行离散化。原理很简单,当信息增益最大时,说明用该点划分能最大程度降低数据集的不确定性。

具体步骤如下:

对每个特征所包含的数值型特征值排序

对相邻两个特征值取均值,这些均值就是待选的划分点

用每一个待选点把该特征的特征值划分成两类,小于该特征点置为1, 大于该特征点置为0,计算此时的条件熵,并计算出信息增益

选择信息使信息增益最大的划分点进行特征离散化

实现代码如下:

def filterRawData(dataSet, colIndex, value, tag):

""" 用于把每个特征的连续值按照区分点分成两类,加入tag参数,可用于标记筛选的是哪一部分数据"""

filterDataList = []

for r in dataSet:

if (tag and r[colIndex] <= value) or ((not tag) and r[colIndex] >value):

newR = r[:colIndex]

newR = np.append(newR, (r[colIndex + 1:]))

filterDataList.append(newR)

return np.array(filterDataList)

def dataDiscretization(dataSet, featName):

""" 对数据每个特征的数值型特征值进行离散化 """

featureNum = dataSet.shape[1] - 1

entropy = calShannonEnt(dataSet)

for featIndex in range(featureNum): #对于每一个特征

uniqueValues = sorted(np.unique(dataSet[:, featIndex]))

meanPoint = []

for i in range(len(uniqueValues) - 1): # 求出相邻两个值的平均值

meanPoint.append(float(uniqueValues[i+1] + uniqueValues[i]) / 2.0)

bestInfoGain = 0.0

bestMeanPoint = -1

for mp in meanPoint: #对于每个划分点

subEntropy = 0.0 #计算该划分点的信息熵

for tag in range(2): #分别划分为两类

subDataSet = filterRawData(dataSet, featIndex, mp, tag)

p = 1.0 * len(subDataSet) / len(dataSet)

subEntropy += p * calShannonEnt(subDataSet)

## 计算信息增益

infoGain = entropy - subEntropy

## 选择最大信息增益

if infoGain >= bestInfoGain:

bestInfoGain = infoGain

bestMeanPoint = mp

featName[featIndex] = featName[featIndex] + "<=" + "{:.3f}".format(bestMeanPoint)

dataSet[:, featIndex] = [1 if x <= bestMeanPoint else 0 for x in dataSet[:, featIndex]]

return dataSet, featName

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

重新对数据进行离散化,并重复该步骤1000次,同时用sklearn中的DecisionTreeClassifier对相同数据进行分类,分别统计平均准确率。运行代码如下:

from sklearn.tree import DecisionTreeClassifier

import matplotlib.pyplot as plt

scoreL = []

scoreL_sk = []

for i in range(1000): #对该过程进行1000次

featNames = iris.feature_names[:]

trainData, testData = train_test_split(data) #区分测试集和训练集

trainData_tmp = copy.copy(trainData)

testData_tmp = copy.copy(testData)

discritizationData, discritizationFeatName= dataDiscretization(trainData, featNames) #根据信息增益离散化

for i in range(testData.shape[1]-1): #根据测试集的区分点离散化训练集

splitPoint = float(discritizationFeatName[i].split('<=')[-1])

testData[:, i] = [1 if x<=splitPoint else 0 for x in testData[:, i]]

decisionTree = creatDecisionTree(trainData, featNames)

classifyLable = [classify(decisionTree, featNames, td) for td in testData]

scoreL.append(1.0 * sum(classifyLable == testData[:, -1]) / len(classifyLable))

clf = DecisionTreeClassifier('entropy')

clf.fit(trainData[:, :-1], trainData[:, -1])

clf.predict(testData[:, :-1])

scoreL_sk.append(clf.score(testData[:, :-1], testData[:, -1]))

print 'score: ', np.mean(scoreL)

print 'score-sk: ', np.mean(scoreL_sk)

fig = plt.figure(figsize=(10, 4))

plt.subplot(1,2,1)

pd.Series(scoreL).hist(grid=False, bins=10)

plt.subplot(1,2,2)

pd.Series(scoreL_sk).hist(grid=False, bins=10)

plt.show()

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

两者准确率分别为:

score: 0.7037894736842105

score-sk: 0.7044736842105263

准确率分布如下:

两者的结果非常一样。

(但是。。为什么根据信息熵离散化得到的准确率比直接用均值离散化的准确率还要低啊??哇的哭出声。。)

最后一次决策树图形如下:

决策树剪枝

由于决策树是完全依照训练集生成的,有可能会有过拟合现象,因此一般会对生成的决策树进行剪枝。常用的是通过决策树损失函数剪枝,决策树损失函数表示为:

C a ( T ) = ∑ t = 1 T N t H t ( T ) + α ∣ T ∣ C_a(T) = \sum_{t=1}^TN_tH_t(T) +\alpha|T|

C

a

(T)=

t=1

T

N

t

H

t

(T)+α∣T∣

其中,H t ( T ) H_t(T)H

t

(T)表示叶子节点t的熵值,T表示决策树的深度。前项∑ t = 1 T N t H t ( T ) \sum_{t=1}^TN_tH_t(T)∑

t=1

T

N

t

H

t

(T)是决策树的经验损失函数当随着T的增加,该节点被不停的划分的时候,熵值可以达到最小,然而T的增加会使后项的值增大。决策树损失函数要做的就是在两者之间进行平衡,使得该值最小。

对于决策树损失函数的理解,如何理解决策树的损失函数? - 陶轻松的回答 - 知乎这个回答写得挺好,可以按照答主的思路理解一下

C4.5算法

ID3算法通过信息增益来进行特征选择会有一个比较明显的缺点:即在选择的过程中该算法会优先选择类别较多的属性(这些属性的不确定性小,条件熵小,因此信息增益会大),另外,ID3算法无法解决当每个特征属性中每个分类都只有一个样本的情况(此时每个属性的条件熵都为0)。

C4.5算法ID3算法的改进,它不是依据信息增益进行特征选择,而是依据信息增益率,它添加了特征分裂信息作为惩罚项。定义分裂信息:

S p l i t I n f o ( X , Y ) = − ∑ i n ∣ X i ∣ ∣ X ∣ log ⁡ ∣ X i ∣ ∣ X ∣ SplitInfo(X, Y) =-\sum_i^n\frac{|X_i|}{|X|}\log\frac{|X_i|}{|X|}

SplitInfo(X,Y)=−

i

n

∣X∣

∣X

i

log

∣X∣

∣X

i

则信息增益率为:

G a i n R a t i o ( X , Y ) = d ( X , Y ) S p l i t I n f o ( X , Y ) GainRatio(X,Y)=\frac{d(X,Y)}{SplitInfo(X, Y)}

GainRatio(X,Y)=

SplitInfo(X,Y)

d(X,Y)

关于ID3和C4.5算法

在学习分类回归决策树算法时,看了不少的资料和博客。关于这两个算法,ID3算法是最早的分类算法,这个算法刚出生的时候其实带有很多缺陷:

无法处理连续性特征数据

特征选取会倾向于分类较多的特征

没有解决过拟合的问题

没有解决缺失值的问题

即该算法出生时是没有带有连续特征离散化、剪枝等步骤的。C4.5作为ID3的改进版本弥补列ID3算法不少的缺陷:

通过信息最大增益的标准离散化连续的特征数据

在选择特征是标准从“最大信息增益”改为“最大信息增益率”

通过加入正则项系数对决策树进行剪枝

对缺失值的处理体现在两个方面:特征选择和生成决策树。初始条件下对每个样本的权重置为1。

特征选择:在选取最优特征时,计算出每个特征的信息增益后,需要乘以一个**“非缺失值样本权重占总样本权重的比例”**作为系数来对比每个特征信息增益的大小

生成决策树:在生成决策树时,对于缺失的样本我们按照一定比例把它归属到每个特征值中,比例为该特征每一个特征值占非缺失数据的比重

关于C4.5和CART回归树

作为ID3的改进版本,C4.5克服了许多缺陷,但是它自身还是存在不少问题:

C4.5的熵运算中涉及了对数运算,在数据量大的时候效率非常低。

C4.5的剪枝过于简单

C4.5只能用于分类运算不能用于回归

当特征有多个特征值是C4.5生成多叉树会使树的深度加深

————————————————

版权声明:本文为CSDN博主「Sarah Huang」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:https://blog.csdn.net/weixin_44794704/article/details/89406612

在ID3决策树归纳方法中,通常是使用信息增益方法来帮助确定生成每个节点时所应采用的合适属性。这样就可以选择具有最高信息增益(熵减少的程度最大)的属性最为当前节点的测试属性,以便对之后划分的训练样本子集进行分类所需要的信息最小,也就是说,利用该属性进行当前(节点所含)样本集合划分,将会使得所产生的样本子集中的“不同类别的混合程度”降为最低。因此,采用这样一种信息论方法将有效减少对象分来所需要的次数,从而确保所产生的决策树最为简单。

一、实验目的

1、理解分类

2、掌握分类挖掘算法ID3

3、为改进ID3打下基础

二、实验内容

1、选定一个数据集(可以参考教学中使用的数据集)

2、选择合适的实现环境和工具实现算法 ID3

3、给出分类规则

三、实验原理

决策树是一种最常见的分类算法,它包含有很多不同的变种,ID3算法是其中最简单的一种。ID3算法中最主要的部分就是信息熵和信息增益的计算。

https://blog.csdn.net/dorisi_h_n_q/article/details/82787295

决策树(decision tree)是一个树结构(可以是二叉树或非二叉树)。决策过程是从根节点开始,测试待分类项中相应的特征属性,并按照其值选择输出分支,直到到达叶子节点,将叶子节点存放的类别作为决策结果。

决策树的关键步骤是分裂属性。就是在某节点处按某一特征属性的不同划分构造不同的分支,目标是让各个分裂子集尽可能地“纯”。即让一个分裂子集中待分类项属于同一类别。

简而言之,决策树的划分原则就是:将无序的数据变得更加有序

分裂属性分为三种不同的情况 :

构造决策树的关键性内容是进行属性选择度量,属性选择度量(找一种计算方式来衡量怎么划分更划算)是一种选择分裂准则,它决定了拓扑结构及分裂点split_point的选择。

属性选择度量算法有很多,一般使用自顶向下递归分治法,并采用不回溯的贪心策略。这里介绍常用的ID3算法。

贪心算法(又称贪婪算法)是指,在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,所做出的是在某种意义上的局部最优解。

此概念最早起源于物理学,是用来度量一个热力学系统的无序程度。

而在信息学里面,熵是对不确定性的度量。

在1948年,香农引入了信息熵,将其定义为离散随机事件出现的概率,一个系统越是有序,信息熵就越低,反之一个系统越是混乱,它的信息熵就越高。所以信息熵可以被认为是系统有序化程度的一个度量。

熵定义为信息的期望值,在明晰这个概念之前,我们必须知道信息的定义。如果待分类的事务可能划分在多个分类之中,则符号x的信息定义为:

在划分数据集之前之后信息发生的变化称为信息增益。

知道如何计算信息增益,就可计算每个特征值划分数据集获得的信息增益,获得信息增益最高的特征就是最好的选择。

条件熵 表示在已知随机变量的条件下随机变量的不确定性,随机变量X给定的条件下随机变量Y的条

件熵(conditional entropy) ,定义X给定条件下Y的条件概率分布的熵对X的数学期望:

根据上面公式,我们假设将训练集D按属性A进行划分,则A对D划分的期望信息为

则信息增益为如下两者的差值

ID3算法就是在每次需要分裂时,计算每个属性的增益率,然后选择增益率最大的属性进行分裂

步骤:1. 对当前样本集合,计算所有属性的信息增益;

是最原始的决策树分类算法,基本流程是,从一棵空数出发,不断的从决策表选取属性加入数的生长过程中,直到决策树可以满足分类要求为止。CLS算法存在的主要问题是在新增属性选取时有很大的随机性。ID3算法是对CLS算法的改进,主要是摒弃了属性选择的随机性。

基于ID3算法的改进,主要包括:使用信息增益比替换了信息增益下降度作为属性选择的标准;在决策树构造的同时进行剪枝操作;避免了树的过度拟合情况;可以对不完整属性和连续型数据进行处理;使用k交叉验证降低了计算复杂度;针对数据构成形式,提升了算法的普适性。

信息增益值的大小相对于训练数据集而言的,并没有绝对意义,在分类问题困难时,也就是说在训练数据集经验熵大的时候,信息增益值会偏大,反之信息增益值会偏小,使用信息增益比可以对这个问题进行校正,这是特征选择

的另一个标准。

特征对训练数据集的信息增益比定义为其信息增益gR( D,A) 与训练数据集的经验熵g(D,A)之比 :

gR(D,A) = g(D,A) / H(D)

sklearn的决策树模型就是一个CART树。是一种二分递归分割技术,把当前样本划分为两个子样本,使得生成的每个非叶子节点都有两个分支,因此,CART算法生成的决策树是结构简洁的二叉树。

分类回归树算法(Classification and Regression Trees,简称CART算法)是一种基于二分递归分割技术的算法。该算法是将当前的样本集,分为两个样本子集,这样做就使得每一个非叶子节点最多只有两个分支。因此,使用CART

算法所建立的决策树是一棵二叉树,树的结构简单,与其它决策树算法相比,由该算法生成的决策树模型分类规则较少。

CART分类算法的基本思想是:对训练样本集进行递归划分自变量空间,并依次建立决策树模型,然后采用验证数据的方法进行树枝修剪,从而得到一颗符合要求的决策树分类模型。

CART分类算法和C4.5算法一样既可以处理离散型数据,也可以处理连续型数据。CART分类算法是根据基尼(gini)系

数来选择测试属性,gini系数的值越小,划分效果越好。设样本集合为T,则T的gini系数值可由下式计算:

CART算法优点:除了具有一般决策树的高准确性、高效性、模式简单等特点外,还具有一些自身的特点。

如,CART算法对目标变量和预测变量在概率分布上没有要求,这样就避免了因目标变量与预测变量概率分布的不同造成的结果;CART算法能够处理空缺值,这样就避免了因空缺值造成的偏差;CART算法能够处理孤立的叶子结点,这样可以避免因为数据集中与其它数据集具有不同的属性的数据对进一步分支产生影响;CART算法使用的是二元分支,能够充分地运用数据集中的全部数据,进而发现全部树的结构;比其它模型更容易理解,从模型中得到的规则能获得非常直观的解释。

CART算法缺点:CART算法是一种大容量样本集挖掘算法,当样本集比较小时不够稳定;要求被选择的属性只能产生两个子结点,当类别过多时,错误可能增加得比较快。

sklearn.tree.DecisionTreeClassifier

1.安装graphviz.msi , 一路next即可

ID3算法就是在每次需要分裂时,计算每个属性的增益率,然后选择增益率最大的属性进行分裂

按照好友密度划分的信息增益:

按照是否使用真实头像H划分的信息增益

**所以,按先按好友密度划分的信息增益比按真实头像划分的大。应先按好友密度划分。