决策树(一)决策树分类

决策树

与SVM类似,决策树在机器学习算法中是一个功能非常全面的算法,它可以执行分类与回归任务,甚至是多输出任务。决策树的算法非常强大,即使是一些复杂的问题,也可以良好地拟合复杂数据集。决策树同时也是随机森林的基础组件,随机森林在当前是最强大的机器学习算法之一。

在这章我们会先讨论如何使用决策树训练、可视化、以及做预测。然后我们会使用sk-learn过一遍CART训练算法。接着我们会讨论如何正则化树,并将它们用于回归任务。最后,我们会讨论决策树的一些不足。

 

训练与可视化决策树

为了理解决策树,我们首先构造一个决策时并看看它如何做预测。下面的代码在iris 数据集上训练一个DecisionTreeClassifier:

from sklearn.datasets import load_iris
from sklearn.tree import DecisionTreeClassifier

iris = load_iris()
X = iris.data[:, 2:] # petal length and width
y = iris.target

tree_clf = DecisionTreeClassifier(max_depth=2)
tree_clf.fit(X, y)

 

接下来可视化这个训练好的决策树。首先使用export_graphviz() 方法输出一个图定义文件,命名为iris_tree.dot:

from sklearn.tree import export_graphviz

export_graphviz(
        tree_clf,
        out_file="iris_tree.dot",
        feature_names=iris.feature_names[2:],
        class_names=iris.target_names,
        rounded=True,
        filled=True
)

 

然后我们可以使用dot命令行工具(出自Graphviz包)将这个.dot 文件转化为一个其他的格式,如PDF或PNG等。下面的命令会将这个.dot 文件转换为一个 .png 文件:

$ dot -Tpng iris_tree.dot -o iris_tree.png

 

生成的图为:

 

做预测

根据生成的图,我们看一下决策树如何做决策。假设我们现在有了一条 iris flower数据并希望对它进行分类。我们首先从根节点开始(深度为0,在最顶端):这个节点会判断这支花的petal length 是否小于2.45cm。假设它是,则移动到左子节点(深度1,左)。在这个例子中,这个左子节点是一个叶子节点,所以它不会继续进行判断,仅检查这个节点的类别,然后决策树便会预测这支花是一个Iris setosa类被(class=setosa)。

可以看到,决策树一个很好的特性就是:它们需要的数据准备操作很少。实际上,它们也不需要进行特征缩放或是数据中心化。

每个节点的sample属性统计的是:有多少条训练数据符合此节点。例如,100条训练数据的pental length 大于2.45cm(深度1,右)。

每个节点的value属性可以告诉我们:在每个类别中,有多少条训练数据符合这个节点。例如,在最底层右边的节点中,有0条数据属于Iris setosa,1条数据属于Iris versicolor,以及45条数据属于virginica。

最后,每个节点的gini属性衡量的是它的不纯度(impurity):如果节点中所有的训练集数据均属于同一个类别,则这个节点的gini指数为0(gini=0),说明此节点为一个“纯”(pure)节点。例如,在深度1的左边节点中,所有的训练数据都属于Iris setosa类别,所以这个节点是“纯”的,并且gini分数为0。下面是训练算法计算gini分数的公式,Gi 为第 ith 个节点的gini分数:

 

在这个公式中,pi,k 是:在节点ith属于类别k的实例数占(这个节点中)训练数据实例数的比例。在深度2的左节点中,gini分数为:1 – (0/54)2 – (49/54)2 – (5/54)2 ≈ 0.168

需要注意的是,sk-learn使用的是CART算法,它仅生成二叉树(也就是节点仅会判断“是/否“问题)。不过其他算法如ID3可以生成多叉树。

下图是决策数的决策边界。竖着的那条实现代表的是根节点(深度0)的决策边界:petal length=2.45cm这条线。由于左边区域是“纯“的(仅有Iris setosa类别),所以它无法再分割。不过右边的区域是”不纯“的,所以深度为1 的右节点可以在petal width=1.75cm处再次分割(由横着的虚线表示)。由于在训练时参数max_depth 设置为2,这个决策树会在此停止。如果设置max_depth=3,则两个深度为2的节点还会增加其他的决策边界(下图中虚线点表示)。

 

 

决策树是非常直观的,并且决策边界也非常容易解释,类似这种模型一般称为白盒模型。相反的,也有黑盒模型,例如我们之后会看到的随机森林以及神经网络。这些模型的预测表现非常好,但是一般很难以一种简洁的方式去解释它们这样做预测的原因。例如,假设有一个神经网络判断到某张图片上有某个人,我们很难知道到底是什么促成了这个预测:是这个模型预测到了这个人的眼睛?耳朵?还是鼻子?亦或是他们坐的那张椅子?与之相反的是,决策树可以提供一个非常好、且简单的分类规则。

 

估计类别概率

决策树也可以用于估计:一个实例属于某个特定类别k的概率。首先它会遍历树,以在叶子节点中找到这个实例,然后返回类别k的训练数据在这个节点中的比例。例如,假设我们有支花,它的petal length = 5cm,petal width=1.5cm。则它对应的叶子节点为:深度为2的左节点。所以此时决策树会输出下面的概率:

  • 此花属于Iris setosa类别(0/54)的概率为0
  • 此花属于Iris versicolor类别(49/54)的概率为90.7%
  • 此花属于Iris virginica类别(5/54)的概率为9.3%

如果用此模型做预测,则它会输出Iris versicolor 类别(class 1),因为它的概率最高。下面我们验证一下:

tree_clf.predict_proba([[5, 1.5]])
>array([[0.        , 0.90740741, 0.09259259]])

tree_clf.predict([[5, 1.5]])
>array([1])

 

可以看到与计算结果一致,并且这个推测的概率也可以直接在之前的决策边界图上计算到(右下角的那个图里各个类别的概率)。

 

CART训练算法

Sk-learn使用的是分类回归树算法(Classification and Regression Tree,CART)训练的决策树(也称为“生长“树(growing tree))。这个算法首先使用一个单个特征k以及一个阈值tk(例如petal length <= 2.45cm)将训练集分割为两个子集。它如何选择k与tk呢?它会搜索(k, tk) 对,找出那个生成最纯(purest)子集(根据它们的大小附加权重)的(k, tk) 对。下面是算法尝试去最小化的损失函数:

在CART算法成功地将训练接分成两个子集后,它会再次使用同样的逻辑分割剩下的子集,依次类推。直到达到了指定的最大深度(由max_depth 超参数指定),或者是无法再找到一个分割使得“不纯度“更低时,则会停止。还有其他几个超参数用于控制停止条件,分别是:min_samples_split, min_samples_leaf, min_weight_fraction_leaf, 以及 max_leaf_nodes)。

很明显CART是一个贪心算法,它并不会检查是否有另一套分割方法使得整体的“不纯度“更低,而仅是检查每层,让这层的分割结果的”不纯度“最低。贪心算法一般都能产生一个解,这种结果尚可,但是它不会保证结果是最优的。

找到最优树是一个NP-完全问题(NP-Complete problem):它需要的时间复杂度是O(exp(m))。所以即使是一个较小的数据集,所花费的时间也会非常多。所以我们才会采用“结果尚可“的解法。

 

计算复杂度

在做预测时,从根节点遍历数到叶子节点是一个必须的操作。决策树一般是(近似)平衡的,所以遍历决策树需要遍历大约O(log2(m)) 个节点。因为每个节点仅需要检查一个特征的值,所以整个预测的复杂度是O(log2(m)),独立于特征的数量。所以即使训练集非常大,决策树的预测操作也非常快。

训练算法会比较每个节点上所有samples(见最开始的决策树生成图)的所有特征(如果设置了max_features 的话,会少一些),导致的训练复杂度为O(n × m log2(m)) 。对于小的训练集(几千条数据)来说,sk-learn可以通过对数据进行预排序(presort,设置参数 presort=True)加快训练速度。不过如果是更大型的数据集的话,则相反会减慢它的训练速度。

 

Gini不纯度还是熵

默认情况下会使用Gini不纯度进行衡量,不过我们也可以选择熵不纯度(entropy impurity)进行衡量,设置超参数criterion 为entropy 即可。熵的概念来自于热力学,用于衡量分子紊乱程度:如果分子静止并且尽然有序,则熵接近于0。熵后来被引入到各个不同的领域,包括香农的信息论。在信息论中,熵被用于衡量一条消息中包含的平均信息量。如果所有的消息都是一样的,则熵为0。在机器学习中,熵频繁地用于衡量不纯度:如果一个集合中包含的所有实例均为同一个类别,则这个集合的熵为0。下面的公式定义了第ith个节点的熵:

 

例如,对于深度为2的左节点(参考最开始训练的决策树),它的熵等于–(49/54) log2 (49/54) – (5/54) log2 (5/54) ≈ 0.445。

那到底是使用Gini不纯度还是熵?实际上,大多数时候这两个并没有太大的区别:它们会生成相似的数。Gini不纯度计算起来稍微快一些,所以它作为默认值是比较合适的。不过,当它们生成的树有不同时,Gini不纯度会倾向于将最频繁的类别分离成一个枝干,而熵倾向于产生稍微更平衡一点的树。

 

正则化超参数

 决策树很少会对训练数据做一些假设(做假设的例如线性模型,例如它会假设数据是线性的,决策树与它不一样)。如果无约束的话,树的结构会自适应于训练数据,并拟合地非常接近训练集——也即是说很容易出现过拟合。这种模型一般称为非参数模型(nonparametric model),不是因为它们没有任何参数(它们一般有很多参数),而是因为参数的数量在训练前是未知的。而例如线性模型这种,它的参数是预先决定的,所以它的自由度有限,也便减少了过拟合的风险(不过同样也增加了欠拟合的风险)。

为了避免对训练数据过拟合,我们需要在训练时限制决策树的自由度,这个便是我们常说的正则化。正则化的超参数取决于使用的算法,但是一般我们至少可以限制决策树的深度。在sk-learn中,这个由超参数max_depth 控制(默认为 None,也就是无限制)。减少max_depth 的值会正则化模型,并因此减少过拟合的风险。

DecisionTreeClassifier类还有几个其他参数可以简单地限制决策树的形状:min_samples_split(一个节点必须最少要有这些数量的samples,才可以进行分裂),min_samples_leaf(一个叶子节点必须要有的sample个数),min_weight_fraction_leaf(与min_samples_leaf 一样,不过是以所有带权数据实例总数的比例表示的),max_leaf_nodes(叶子节点的最大数目),以及max_features(在每个节点中进行分割时,考虑的最多的特征数目)。增加 min_* 的超参数或者减少 max_* 的超参数可以对模型实施正则化。

其他有些算法会先训练决策树,训练时不给任何限制,然后再进行剪支(pruning),也就是删除不必要的节点。如果一个节点的所有子节点均为叶子节点,且它提供的纯度并没有一个显著地提升的话,则这个节点被认为是不必要的节点。

下图是在moons 训练集上训练的两颗决策树,左边的决策树是用默认的超参数训练(也就是说没有限制),右边的指定了超参数min_samples_leaf=4。可以明显看到左边的存在过拟合,而右边的泛化性能会更好:

 

 决策树分类器介绍完之后,我们会继续引入决策树回归。

 

posted @ 2020-02-27 17:08  ZacksTang  阅读(6536)  评论(0编辑  收藏  举报