决策树之C4.5算法

时间:2023-12-05 11:29:20

决策树之C4.5算法

一、C4.5算法概述

  C4.5算法是最常用的决策树算法,因为它继承了ID3算法的所有优点并对ID3算法进行了改进和补充。

  改进有如下几个要点:

  1. 用信息增益率来选择属性,克服了ID3算法中信息增益选择属性时偏向选择取值多的属性的不足。

  C4.5算法选择决策属性的度量标准是增益比率gain ratio(Quinlan 1986)。增益比率度量是用前面的增益度量Gain(S,A)和分裂信息度量Splitlnformation(S,A)来共同定义的。为防遗忘,在此贴出信息熵和和信息增益的计算公式,如下所示:

决策树之C4.5算法

决策树之C4.5算法

由此,得出增益比率的公式如下:

决策树之C4.5算法

其中,分裂信息度量被定义为(分裂信息用来衡量属性分裂数据的广度和均匀):

决策树之C4.5算法

其中S1到Sc是c个值的属性A分割S而形成的c个样例子集。注意分裂信息实际上就是S关于属性A的各值的熵。

还是以一个典型被引用过多次的训练数据集D为例,来说明C4.5算法如何计算信息增益率并选择决策节点。

决策树之C4.5算法

  上面的训练集有4个属性,即属性集合A={OUTLOOK, TEMPERATURE, HUMIDITY, WINDY};而类标签有2个,即类标签集合C={Yes, No},分别表示适合户外运动和不适合户外运动,其实是一个二分类问题。
我们已经计算过信息增益,这里直接列出来,如下所示:

数据集D包含14个训练样本,其中属于类别“Yes”的有9个,属于类别“No”的有5个,则计算其信息熵:Info(D) = -9/14 * log2(9/14) - 5/14 * log2(5/14) = 0.940

下面对属性集中每个属性分别计算信息熵,如下所示:

  (1)

Info(OUTLOOK) = 5/14 * [- 2/5 * log2(2/5) – 3/5 * log2(3/5)] + 4/14 * [ - 4/4 * log2(4/4) - 0/4 * log2(0/4)] + 5/14 * [ - 3/5 * log2(3/5) – 2/5 * log2(2/5)] = 0.694

  (2)

Info(TEMPERATURE) = 4/14 * [- 2/4 * log2(2/4) – 2/4 * log2(2/4)] + 6/14 * [ - 4/6 * log2(4/6) - 2/6 * log2(2/6)] + 4/14 * [ - 3/4 * log2(3/4) – 1/4 * log2(1/4)] = 0.911

  (3)

Info(HUMIDITY) = 7/14 * [- 3/7 * log2(3/7) – 4/7 * log2(4/7)] + 7/14 * [ - 6/7 * log2(6/7) - 1/7 * log2(1/7)] = 0.789

  (4)

Info(WINDY) = 6/14 * [- 3/6 * log2(3/6) – 3/6 * log2(3/6)] + 8/14 * [ - 6/8 * log2(6/8) - 2/8 * log2(2/8)] = 0.892

根据上面的数据,我们可以计算各属性的信息增益值,计算如下所示:

  (1)Gain(OUTLOOK) = Info(D) - Info(OUTLOOK) = 0.940 - 0.694 = 0.246

  (2)Gain(TEMPERATURE) = Info(D) - Info(TEMPERATURE) = 0.940 - 0.911 = 0.029

  (3)Gain(HUMIDITY) = Info(D) - Info(HUMIDITY) = 0.940 - 0.789 = 0.151

  (4)Gain(WINDY) = Info(D) - Info(WINDY) = 0.940 - 0.892 = 0.048

接下来,我们计算分裂信息度量H(V):

OUTLOOK属性

属性OUTLOOK有3个取值,其中Sunny有5个样本、Rainy有5个样本、Overcast有4个样本,则

Split(OUTLOOK) = - 5/14 * log2(5/14) - 5/14 * log2(5/14) - 4/14 * log2(4/14) = 1.577406282852345

TEMPERATURE属性

属性TEMPERATURE有3个取值,其中Hot有4个样本、Mild有6个样本、Cool有4个样本,则

Split(TEMPERATURE) = - 4/14 * log2(4/14) - 6/14 * log2(6/14) - 4/14 * log2(4/14) = 1.5566567074628228

HUMIDITY属性

属性HUMIDITY有2个取值,其中Normal有7个样本、High有7个样本,则

Split(HUMIDITY) = - 7/14 * log2(7/14) - 7/14 * log2(7/14) = 1.0

WINDY属性

属性WINDY有2个取值,其中True有6个样本、False有8个样本,则

Split(WINDY) = - 6/14 * log2(6/14) - 8/14 * log2(8/14) = 0.9852281360342516

根据上面计算结果,我们可以计算信息增益率,如下所示:

(1)GR(OUTLOOK) = Info(OUTLOOK) / H(OUTLOOK) = 0.246/1.577406282852345 = 0.15595221261270145

(2)GR(TEMPERATURE) = Info(TEMPERATURE) / H(TEMPERATURE) = 0.029 / 1.5566567074628228 = 0.018629669509642094

(3)GR(HUMIDITY) = Info(HUMIDITY) / H(HUMIDITY) = 0.151/1.0 = 0.151

(4)GR(WINDY) = Info(WINDY) / H(WINDY) = 0.048/0.9852281360342516 = 0.048719680492692784

根据计算得到的信息增益率进行选择属性集中的属性(有上面的计算可知,应选OUTLOOK属性)作为决策树节点,对该节点进行分裂。

2.可以处理连续数值型属性

C4.5算法既可以处理离散型描述属性,也可以处理连续型描述属性。在选择某节点上的分支属性时,对于离散型描述属性,C4.5算法的处理方法与ID3相同,按照该属性本身的取值个数进行计算;对于某个连续型描述属性Ac,假设在某个节点上的数据集的样本数量为total,C4.5算法将作为以下处理:

  • 将该节点上的所有数据样本按照连续型描述的属性的具体数值,由小到大进行排序,得到属性值的取值序列{A1c,A2c,...,Atotalc}。
  • 在取值序列生成total-1个分割点。第i(0<i<total)个分割点的取值设置为Vi=(Aic+A(i+1)c)/2,它可以将该节点上的数据集划分为两个子集。
  • 从total-1个分割点中选择最佳分割点。对于每一个分割点划分数据集的方式,C4.5算法计算它的信息增益比,并且从中选择信息增益比最大的分割点来划分数据集。

3.采用了一种后后剪枝方法

避免树的高度无节制的增长,避免过度拟合数据,该方法使用训练样本集本身来估计剪枝前后的误差,从而决定是否真正剪枝。方法中使用的公式如下:

决策树之C4.5算法

其中N是实例的数量,f=E/N为观察到的误差率(其中E为N个实例中分类错误的个数),q为真实的误差率,c为置信度(C4.5算法的一个输入参数,默认值为0.25),z为对应于置信度c的标准差,其值可根据c的设定值通过查正态分布表得到。通过该公式即可计算出真实误差率q的一个置信度上限,用此上限为该节点误差率e做一个悲观的估计:

决策树之C4.5算法

通过判断剪枝前后e的大小,从而决定是否需要剪枝。

4.对于缺失值的处理

在某些情况下,可供使用的数据可能缺少某些属性的值。假如〈x,c(x)〉是样本集S中的一个训练实例,但是其属性A的值A(x)未知。处理缺少属性值的一种策略是赋给它结点n所对应的训练实例中该属性的最常见值;另外一种更复杂的策略是为A的每个可能值赋予一个概率。例如,给定一个布尔属性A,如果结点n包含6个已知A=1和4个A=0的实例,那么A(x)=1的概率是0.6,而A(x)=0的概率是0.4。于是,实例x的60%被分配到A=1的分支,40%被分配到另一个分支。这些片断样例(fractional examples)的目的是计算信息增益,另外,如果有第二个缺少值的属性必须被测试,这些样例可以在后继的树分支中被进一步细分。C4.5就是使用这种方法处理缺少的属性值。

二、部分代码示例

1、信息熵

 double C4_5::entropy(int *attrClassCount, int classNum, int allNum){
double iEntropy = 0.0;
for(int i = ; i < classNum; i++){
double temp = ((double)attrClassCount[i]) / allNum;
if(temp != 0.0)
iEntropy -= temp * (log(temp) / log(2.0));
}
return iEntropy;
}

2、信息增益率

 double C4_5::gainRatio(int classNum, vector<int *> attriCount, double pEntropy){
int* attriNum = new int[attriCount.size()];
int allNum = ; for(int i = ; i < (int)attriCount.size(); i++){
attriNum[i] = ;
for(int j = ; j < classNum; j++){
attriNum[i] += attriCount[i][j];
allNum += attriCount[i][j];
}
}
double gain = 0.0;
double splitInfo = 0.0;
for(int i = ; i < (int)attriCount.size(); i++){
gain -= ((double)attriNum[i]) / allNum * entropy(attriCount[i], classNum, attriNum[i]);
splitInfo -= ((double)attriNum[i]) / allNum * (log(((double)attriNum[i])/allNum) / log(2.0));
}
gain += pEntropy;
delete[] attriNum;
return (gain / splitInfo);
}

3、选取最大增益属性作为分类条件

 int C4_5::chooseAttribute(vector<int> attrIndex, vector<int *>* sampleCount){
int bestIndex = ;
double maxGainRatio = 0.0;
int classNum = (int)(decisions[attrIndex[(int)attrIndex.size()-]]).size();//number of class //computer the class entropy
int* temp = new int[classNum];
int allNum = ;
for(int i = ; i < classNum; i++){
temp[i] = sampleCount[(int)attrIndex.size()-][i][i];
allNum += temp[i];
}
double pEntropy = entropy(temp, classNum, allNum);
delete[] temp; //computer gain ratio for every attribute
for(int i = ; i < (int)attrIndex.size()-; i++){
double gainR = gainRatio(classNum, sampleCount[i], pEntropy);
if(gainR > maxGainRatio){
bestIndex = i;
maxGainRatio = gainR;
}
}
return bestIndex;
}