机器学习之决策树ID3/C4.5/CART学习算法比较

一、概述
  • 决策树是机器学习中最基础、应用最广泛的算法模型,常用于解决分类和回归问题。
  • 决策树的构建是一种自上而下的递归分裂学习方法,其学习的关键在于如何选择最优划分属性。一般情况下,随着划分过程不断进行,决策树的分支结点所包含的样本会尽可能属于同一类别,即结点的纯度(purity)越来越高。
  • 度量样本集合纯度有三种常用的评估准则:ID3、C4.5和CART。本文将从构造、应用和实现三个角度,对比这三种模型的异同点。
二、决策树学习算法
2.1 相亲数据集
编号 年龄 长相 工资 编程 类别
1 不会 不见
2 年轻 一般 中等
3 年轻 不会 不见
4 年轻 一般
5 年轻 一般 不会 不见
2.2 ID3(Iterative Dichotomiser 3)--信息增益

  信息熵(information entropy)是度量样本集合纯度最常用的一种指标。对于样本集合D,类别数为K,信息熵定义为:

  其中,是样本集合D中属于第k(k=1,2,3...K)类的样本子集,表示该子集的元素个数,表示样本集合的元素个数。特征属性A的条件熵H(D|A)定义为:

  其中,表示D中特征A取第i个值的样本子集,表示中属于第k类的样本子集。因此,特征A的信息增益等于两者之差:

  以表2.1中相亲数据为例,该数据包含5个样本集,正样本占比,负样本占比。于是,根据公式计算出根节点的信息熵为:

  然后,计算当前属性集合{年龄,长相,工资,编程}中每个属性的条件熵。以属性“年龄”为例,它有2个可能的取值:{老,年轻}。若使用该属性对D进行划分,则可得到2个子集,分别为:(年龄=老),(年龄=年轻)。

  • 子集包含编号{1}的1个样例,其中正样本占比,负样本占比;
  • 子集包含编号{2,3,4,5}的4个样例,其中正样本占比,负样本占比;
    根据公式计算出属性=年龄的条件熵为:

    依此类推:



    每个属性对应的信息增益为:
    g(D,年龄)=0.171,g(D,长相)=0.42,g(D,工资)=0.42,g(D,编程)=0.971
      由此可得,特征“编程”的信息增益最大,使用特征“编程”来进行划分所得的纯度提升越大。图2.1给出了基于“编程”对根节点进行划分的结果:
    机器学习之决策树ID3/C4.5/CART学习算法比较_第1张图片
    图2.1 基于编程属性对根节点划分

      然后,决策树学习算法对每个分支节点做进一步划分。以图2.1中第一个分支节点(编程=会)为例,该节点包含的样本集合有{2,4}两个样本,可用的属性集合有{年龄,长相,工资},基于计算出各属性的信息增益,继续划分树节点,直到满足停止分裂的条件。
      最后,ID3对取值较多的特征有所偏好,特征取值越多意味着确定性越高,也就是条件熵越小,信息增益越大。如果将前面的编号也作为一个划分属性,其信息增益为0.971,它将产生5个分支,每个分支结点仅包含一个样本,显然这样划分出来的决策树不具备泛化能力,无法对新样本进行有效预测。因此,C4.5对ID3进行优化,通过引入信息增益比,对取值较多的特征进行惩罚,避免出现过拟合的特性,提升决策树的泛化能力。

信息熵和条件熵计算的scala-spark实现:

/**
    * 计算信息熵
    *
    * @param df
    * @param column
    * @return
    */
  def calculate(df: DataFrame, column: String = "label"): Double = {
    val counts = df.select(column).groupBy(column).agg(count(column)).collect().map(row => row.getLong(1))
    val totalCount = counts.sum.toDouble
    if (totalCount == 0) {
      return 0
    }
    counts.map {
      count =>
        var impurity = 0.0
        if (count != 0) {
          val freq = count / totalCount
          impurity -= freq * log2(freq)
        }
        impurity
    }.reduce((v1, v2) => v1 + v2)
  }

  /**
    * 计算每个特征的条件熵
    *
    * @param df
    * @param column
    * @return
    */
  def calculateFeature(df: DataFrame, column: String): Double = {
    val counts = df.select(column).groupBy(column).agg(count(column)).collect()
    val totalCount = counts.map(row => row.getLong(1)).sum.toDouble
    if (totalCount == 0) {
      return 0
    }
    val impurity = counts.map {
      row =>
        val featureValue = row.get(0).toString.toDouble
        val featureCount = row.getLong(1)
        val freq = featureCount / totalCount
        val tmp = df.filter(col(column) === featureValue)
        freq * calculate(tmp, "label")
    }.reduce((v1, v2) => v1 + v2)
    impurity
  }
2.3 C4.5--信息增益比

  特征A对于数据集D的信息增益比定义为:

其中

称为数据集D关于A的取值熵。因此,可以根据上面的公式求出每个特征的取值熵:




最终可计算出各个特征的信息增益比:




通过信息增益比,特征年龄对应的指标上升了,而特征长相和工资有所下降。

2.4 CART--基尼指数(Gini)

  CART决策树使用基尼指数来选择划分属性,Gini描述的是数据的纯度,其定义为:

  其中,是样本集合D中属于第k(k=1,2,3...K)类的样本子集,表示该子集的元素个数,表示样本集合的元素个数。如果所有样本都属于同一个类别,则=,=0,此时impurity最小。CART利用基尼指数构造二叉决策树。如果特征是离散型变量,将样本按特征A的取值切分成两份;如果特征是连续型变量,CART的处理方式和C4.5相同,先将特征值进行升序排序,然后把左边第一个值(index=1)作为一个分类,右边其他值作为另一个分类,计算其Gini指数,然后移动index的位置,直到计算完所有的分类结果,然后选取Gini最小的位置对应的index作为切分点。特征A的Gini指数定义为:

当n=2时,该公式可以简化为:

使用CART分类准则,选取年龄维度,把老作为特征标签,那么年轻就被划分到另外一类

老(总数=1) 年轻(总数=4)
类别 不见 见、不见、见、不见


帅(总数=1) 一般、丑(总数=4)
类别 不见 见、不见、见、不见


一般(总数=3) 帅、丑(总数=2)
类别 见、不见 不见

高(总数=3) 中等、低(总数=2)
类别 见、不见 见、不见

会(总数=2) 不会(总数=3)
类别 不见


因此,特征编程的Gini指数最小,选择该特征作为最优的切分点。

三、小结

  通过比较ID3、C4.5和CART三种决策树的构造准则,在同一个样本集上,表现出不同的划分行为。

  • ID3和C4.5在每个结点上可以产生多个分支,而CART每个结点只会产生两个分支
  • C4.5通过引入信息增益比,弥补了ID3在特征取值比较多时,由于过拟合造成泛化能力变弱的缺陷
  • ID3只能处理离散型变量,而C4.5和CART可以处理连续型变量
  • ID3和C4.5只能用于分类任务,而CART可以用于分类和回归任务

参考文献:
[1]诸葛越,葫芦娃.百面机器学习
[2]周志华.机器学习

你可能感兴趣的:(机器学习之决策树ID3/C4.5/CART学习算法比较)