C4.5,是机器学习算法中的另一个分类决策树算法,它是决策树(决策树也就是做决策的节点间的组织方式像一棵树,其实是一个倒树)核心算法,也是上文1.2节所介绍的ID3的改进算法,所以基本上了解了一半决策树构造方法就能构造它。
决策树构造方法其实就是每次选择一个好的特征以及分裂点作为当前节点的分类条件。
既然说C4.5算法是ID3的改进算法,那么C4.5相比于ID3改进的地方有哪些呢?:
- 用信息增益率来选择属性。ID3选择属性用的是子树的信息增益,这里可以用很多方法来定义信息,ID3使用的是熵(entropy,熵是一种不纯度度量准则),也就是熵的变化值,而C4.5用的是信息增益率。对,区别就在于一个是信息增益,一个是信息增益率。
- 在树构造过程中进行剪枝,在构造决策树的时候,那些挂着几个元素的节点,不考虑最好,不然容易导致overfitting。
- 对非离散数据也能处理。
- 能够对不完整数据进行处理
针对上述第一点,解释下:一般来说率就是用来取平衡用的,就像方差起的作用差不多,比如有两个跑步的人,一个起点是10m/s的人、其10s后为20m/s;另一个人起速是1m/s、其1s后为2m/s。如果紧紧算差值那么两个差距就很大了,如果使用速度增加率(加速度,即都是为1m/s^2)来衡量,2个人就是一样的加速度。因此,C4.5克服了ID3用信息增益选择属性时偏向选择取值多的属性的不足。
C4.5算法之信息增益率
OK,既然上文中提到C4.5用的是信息增益率,那增益率的具体是如何定义的呢?:
是的,在这里,C4.5算法不再是通过信息增益来选择决策属性。一个可以选择的度量标准是增益比率gain ratio(Quinlan 1986)。增益比率度量是用前面的增益度量Gain(S,A)和分裂信息度量SplitInformation(S,A)来共同定义的,如下所示:
其中,分裂信息度量被定义为( 分裂信息用来衡量属性分裂数据的广度和均匀):
其中S1到Sc是c个值的属性A分割S而形成的c个样例子集。注意分裂信息实际上就是S关于属性A的各值的熵。这与我们前面对熵的使用不同,在那里我们只考虑S关于学习到的树要预测的目标属性的值的熵。
请注意,分裂信息项阻碍选择值为均匀分布的属性。例如,考虑一个含有n个样例的集合被属性A彻底分割(译注:分成n组,即一个样例一组)。这时分裂信息的值为log2n。相反,一个布尔属性B分割同样的n个实例,如果恰好平分两半,那么分裂信息是1。如果属性A和B产生同样的信息增益,那么根据增益比率度量,明显B会得分更高。
使用增益比率代替增益来选择属性产生的一个实际问题是,当某个Si接近S(|Si|»|S|)时分母可能为0或非常小。如果某个属性对于S的所有样例有几乎同样的值,这时要么导致增益比率未定义,要么是增益比率非常大。为了避免选择这种属性,我们可以采用这样一些启发式规则,比如先计算每个属性的增益,然后仅对那些增益高过平均值的属性应用增益比率测试(Quinlan 1986)。
除了信息增益,Lopez de Mantaras(1991)介绍了另一种直接针对上述问题而设计的度量,它是基于距离的(distance-based)。这个度量标准基于所定义的一个数据划分间的距离尺度。具体更多请参看:Tom M.Mitchhell所著的机器学习之3.7.3节。
1.3.2、C4.5算法构造决策树的过程
- Function C4.5(R:包含连续属性的无类别属性集合,C:类别属性,S:训练集)
- Begin
- If S为空,返回一个值为Failure的单个节点;
- If S是由相同类别属性值的记录组成,
- 返回一个带有该值的单个节点;
- If R为空,则返回一个单节点,其值为在S的记录中找出的频率最高的类别属性值;
- [注意未出现错误则意味着是不适合分类的记录];
- For 所有的属性R(Ri) Do
- If 属性Ri为连续属性,则
- Begin
- 将Ri的最小值赋给A1:
- 将Rm的最大值赋给Am;
- For j From 2 To m-1 Do Aj=A1+j*(A1Am)/m;
- 将Ri点的基于{< =Aj,>Aj}的最大信息增益属性(Ri,S)赋给A;
- End;
- 将R中属性之间具有最大信息增益的属性(D,S)赋给D;
- 将属性D的值赋给{dj/j=1,2...m};
- 将分别由对应于D的值为dj的记录组成的S的子集赋给{sj/j=1,2...m};
- 返回一棵树,其根标记为D;树枝标记为d1,d2...dm;
- 再分别构造以下树:
- C4.5(R-{D},C,S1),C4.5(R-{D},C,S2)...C4.5(R-{D},C,Sm);
- End C4.5
1.3.3、C4.5算法实现中的几个关键步骤
在上文中,我们已经知道了决策树学习C4.5算法中4个重要概念的表达,如下:
- double C4_5::entropy(int *attrClassCount, int classNum, int allNum){
- double iEntropy = 0.0;
- for(int i = 0; i < classNum; i++){
- double temp = ((double)attrClassCount[i]) / allNum;
- if(temp != 0.0)
- iEntropy -= temp * (log(temp) / log(2.0));
- }
- return iEntropy;
- }
- double C4_5::gainRatio(int classNum, vector<int *> attriCount, double pEntropy){
- int* attriNum = newint[attriCount.size()];
- int allNum = 0;
- for(int i = 0; i < (int)attriCount.size(); i++){
- attriNum[i] = 0;
- for(int j = 0; j < classNum; j++){
- attriNum[i] += attriCount[i][j];
- allNum += attriCount[i][j];
- }
- }
- double gain = 0.0;
- double splitInfo = 0.0;
- for(int i = 0; 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);
- }
- int C4_5::chooseAttribute(vector<int> attrIndex, vector<int *>* sampleCount){
- int bestIndex = 0;
- double maxGainRatio = 0.0;
- int classNum = (int)(decisions[attrIndex[(int)attrIndex.size()-1]]).size();//number of class
- //computer the class entropy
- int* temp = newint[classNum];
- int allNum = 0;
- for(int i = 0; i < classNum; i++){
- temp[i] = sampleCount[(int)attrIndex.size()-1][i][i];
- allNum += temp[i];
- }
- double pEntropy = entropy(temp, classNum, allNum);
- delete[] temp;
- //computer gain ratio for every attribute
- for(int i = 0; i < (int)attrIndex.size()-1; i++){
- double gainR = gainRatio(classNum, sampleCount[i], pEntropy);
- if(gainR > maxGainRatio){
- bestIndex = i;
- maxGainRatio = gainR;
- }
- }
- return bestIndex;
- }
- form Wind:决策树使用于特征取值离散的情况,连续的特征一般也要处理成离散的(而很多文章没有表达出决策树的关键特征or概念)。实际应用中,决策树overfitting比较的严重,一般要做boosting。分类器的性能上不去,很主要的原因在于特征的鉴别性不足,而不是分类器的好坏,好的特征才有好的分类效果,分类器只是弱相关。
- 那如何提高 特征的鉴别性呢?一是设计特征时尽量引入domain knowledge,二是对提取出来的特征做选择、变换和再学习,这一点是机器学习算法不管的部分。