机器学习入门 12-7 决策树的局限性

2021-02-26 16:22:49 浏览数 (1)

决策树第一个局限性

通过前几个小节绘制使用决策树来解决分类问题的决策边界可以看出,决策树的决策边界都是横平竖直的直线。

上图为决策树在鸢尾花数据集(为了可视化方便,只选取后两个特征)上训练后绘制的决策边界。在样本特征只有两个的二维特征平面中(用x轴表示第一个特征维度,用y轴表示第二个特征维度),决策树的决策边界一定是和 x 轴或者 y 轴平行的直线,这是因为对于决策树来说,每一次都是在某个特征维度 d 上选择某一个阈值 v 进行划分:

  • 如果样本点的第 d 个特征维度上的特征值小于等于阈值 v,则将这些样本划分到左子节点上;
  • 如果样本点的第 d 个特征维度上的特征值大于阈值 v,则将这些样本划分到右子节点上;

对于拥有两个特征的数据集来说,特征维度

d = {0, 1}

。当在

d = 0

上选择阈值 v 进行划分,相当于一条平行于 y 轴的边界。当在

d = 1

上选择阈值 v 进行划分,相当于一条平行于 x 轴的边界。

横平竖直的决策边界显然是有局限性的,下面举一个简单的例子。下图中的 4 个圆代表 4 个样本点,不同颜色代表不同类别,其中蓝色类别的样本点有 3 个,红色类别的样本点有 1 个。

使用决策树对这 4 个样本点进行二分类。训练决策树(构建决策树)的过程,可能首先对 4 个样本点进行一个横向的划分,如下图所示。

横向划分之后,4 个样本点被划分成上下两个部分,此时构建的决策树如下所示。

右子节点中的两个样本点全是蓝色类别,所以右子节点的信息熵(或基尼系数)都为 0,不需要进一步划分。而左子节点中的两个样本点,一个为红色类别一个为蓝色类别,可以继续划分。

最终的决策边界可能呈现 "T" 的形状,但是这个决策边界可能并不能反映样本点真实的分布情况。毕竟对于这 4 个样本点来说,有可能真正合理的划分是一根斜线,但是对于我们的决策树来说,是永远不可能产生这样倾斜的决策边界的。

更严重的一种情况,假设现在数据集的分布如下所示。

二维平面上有蓝色和红色两个类别的样本点。显然,使用一根竖直的直线可以很轻松的将这两个类别的样本点划分。决策树可以实现这种竖直直线(平行于y轴)的划分。

如果上面的数据集的分布稍微倾斜一下,再使用决策树进行划分的决策边界可能如下所示。

但是这种平行于 x 轴和 y 轴的决策边界可能是不对的。为了方便叙述,将最左侧和最右侧的划分标为 1 和 2。

上面倾斜的数据集分布可以使用一根倾斜的直线轻松划分,但是如果使用决策树,在标有 1 和 2 个两次划分可能是错误的,因为在二维特征平面中,无限往左的时候,标 1 的决策边界会一直保持。类似,无限往右的时候,标 2 的决策边界也会一直保持。

随着二维特征平面往两边延伸,决策树的决策边界和实际的决策边界差别越来越大。显然,决策树的决策边界错误越来越大。

决策树第二个局限性

决策树的第二个局限性是对个别数据敏感。对于大多数非参数学习算法来说,都存在对个别数据敏感的问题。 下面通过一个具体的例子来认识决策树的这个局限性。首先导入 numpy 和 matplotlib 两个模块。

代码语言:javascript复制
 In[1]: import numpy as np
        import matplotlib.pyplot as plt

本小节使用非常经典的鸢尾花数据集 iris,由于我们要对数据进行可视化,所以只保留鸢尾花样本特征中的两个特征,这里选择后两个特征(iris.data[:, 2:])。

代码语言:javascript复制
 In[2]: from sklearn import datasets
        
        iris = datasets.load_iris()
        X = iris.data[:, 2:]
        y = iris.target

使用 sklearn 封装的决策树对鸢尾花数据集进行分类只需要从 sklearn.tree 包中导入 DecisionTreeClassifier 类即可。

代码语言:javascript复制
 In[3]: from sklearn.tree import DecisionTreeClassifier
        
        tree_clf = DecisionTreeClassifier(max_depth=2,
                                          criterion="entropy",
                                          random_state=42)
        tree_clf.fit(X, y)
Out[3]: DecisionTreeClassifier(class_weight=None, criterion='entropy', max_depth=2,
            max_features=None, max_leaf_nodes=None,
            min_impurity_decrease=0.0, min_impurity_split=None,
            min_samples_leaf=1, min_samples_split=2,
            min_weight_fraction_leaf=0.0, presort=False, random_state=42,
            splitter='best')

构造决策树时传入三个参数:

  • max_depth,决策树的最高深度,此时我们指定 max_depth 为 2;
  • criterion,不纯度的计算方法,此时我们指定 criterion = "entropy",使用信息熵的方式来计算不纯度;
  • random_state,随机种子;

通过 fit 函数拟合训练模型。接下来使用前面一直使用的 plot_decision_boundary 函数绘制使用决策树来解决分类问题的决策边界,与此同时使用 scatter 函数将三个类别的训练样本也绘制出来。

代码语言:javascript复制
 In[4]: import numpy as np
        import matplotlib.pyplot as plt
        
        # 绘制不规则决策边界
        def plot_decision_boundary(model, axis):
            '''
            model: 训练好的模型
            axis: 横纵坐标轴的范围,[x_start, x_end, y_start, y_end ]
            '''
            x0, x1 = np.meshgrid(
                np.linspace(axis[0], axis[1], 
                            int((axis[1] - axis[0]) * 100)).reshape(-1, 1),
                np.linspace(axis[2], axis[3], 
                            int((axis[3] - axis[2]) * 100)).reshape(-1, 1)
            )
            X_new = np.c_[x0.ravel(), x1.ravel()]

            y_predict = model.predict(X_new)
            zz = y_predict.reshape(x0.shape)

            from matplotlib.colors import ListedColormap
            custom_cmap = ListedColormap(['#EF9A9A','#FFF59D','#90CAF9'])

            plt.contourf(x0, x1, zz, linewidth=5, cmap=custom_cmap)
代码语言:javascript复制
 In[5]: plot_decision_boundary(dt_clf, axis=[0.5, 7.5, 0, 3])
        plt.scatter(X[y==0,0], X[y==0,1])
        plt.scatter(X[y==1,0], X[y==1,1])
        plt.scatter(X[y==2,0], X[y==2,1])
        plt.show()

使用 np.delete 函数删除鸢尾花数据集中索引号为 106 的样本点。

代码语言:javascript复制
 In[6]: X_new = np.delete(X, 106, axis = 0)
        y_new = np.delete(y, 106)

原始鸢尾花数据集一共有 150 个样本点,删除一个样本点后,新的鸢尾花数据集一共有 149 个样本点。

代码语言:javascript复制
 In[7]: X_new.shape
Out[7]: (149, 2)
    
 In[8]: y_new.shape
Out[8]: (149, )

接下来使用决策树对包含 149 个样本点的新的鸢尾花数据集进行分类,实例化决策树时传入的参数和之前一样。

代码语言:javascript复制
 In[9]: tree_clf2 = DecisionTreeClassifier(max_depth=2, 
                                           criterion="entropy", 
                                           random_state=42)
        tree_clf2.fit(X_new, y_new)
Out[9]: DecisionTreeClassifier(class_weight=None, criterion='entropy', max_depth=2,
            max_features=None, max_leaf_nodes=None,
            min_impurity_decrease=0.0, min_impurity_split=None,
            min_samples_leaf=1, min_samples_split=2,
            min_weight_fraction_leaf=0.0, presort=False, random_state=42,
            splitter='best')

训练好的决策树,接下来可以绘制决策边界。

代码语言:javascript复制
 In[10]: plot_decision_boundary(tree_clf2, axis=[0.5, 7.5, 0, 3])
         plt.scatter(X_new[y_new==0,0], X_new[y_new==0,1])
         plt.scatter(X_new[y_new==1,0], X_new[y_new==1,1])
         plt.scatter(X_new[y_new==2,0], X_new[y_new==2,1])
         plt.show()

最终的绘制结果如上图所示。仅仅是删除了一个样本点,此时的决策边界和之前使用鸢尾花的 150 个样本点训练的决策边界完全不同,这也再次印证了对于决策树算法来说,对个别样本点敏感,这其实也是所有非参数学习算法的缺点,所以决策树高度依赖调参。

小结

这一小节介绍了决策树两个比较重要的局限性:

  • 第一个局限性:决策树生成的决策边界是横平竖直的。如果数据集的分布发生一些偏斜,使用决策树可能并不能够很好的反映数据集的分布情况;
  • 第二个局限性:决策树作为一个非参数学习算法,它可能对个别样本点非常敏感;

虽然决策树有上面两个局限性,但是决策树本身依然是一个非常有用的机器学习算法。在一些经济学或者相关的领域,很有可能会使用一棵决策树来对现实的问题进行建模。不过在机器学习领域,决策树的用途并不完全是单独使用。在机器学习领域,决策树更重要的应用是使用集成学习的方式,创建一种被称为随机森林的算法,而随机森林的算法可以得到非常好的学习结果。

References:

  1. Python3入门机器学习 经典算法与应用: https://coding.imooc.com/class/chapter/169.html#Anchor

0 人点赞