美文网首页学习程序员Python 数据科学手册【部分】
Python 数据科学手册 5.8 决策树和随机森林

Python 数据科学手册 5.8 决策树和随机森林

作者: 布客飞龙 | 来源:发表于2017-07-03 11:01 被阅读298次

    5.8 决策树和随机森林

    原文:In-Depth: Decision Trees and Random Forests

    译者:飞龙

    协议:CC BY-NC-SA 4.0

    译文没有得到原作者授权,不保证与原文的意思严格一致。

    之前,我们深入研究了简单的生成分类器(见朴素贝叶斯分类)和强大的辨别分类器(参见支持向量机)。 这里我们来看看另一个强大的算法的动机 - 一种称为随机森林的非参数算法。 随机森林是组合方法的一个例子,这意味着它依赖于更简单估计器的整体聚合结果。 这种组合方法的结果令人惊讶,总和可以大于部分:即,多个估器中的多数表决最终可能比执行表决的任何个体的估计更好! 我们将在以下部分中看到这个例子。 我们从标准导入开始:

    %matplotlib inline
    import numpy as np
    import matplotlib.pyplot as plt
    import seaborn as sns; sns.set()
    

    随机森林是一个例子,建立在决策树上的组合学习器。 因此,我们将首先讨论决策树本身。

    决策树是分类或标注对象的非常直观的方法:您只需要询问一系列问题,它们为弄清楚分类而设计。 例如,如果您想建立一个决策树,来分类您在远足时遇到的动物,则可以构建如下所示的树:

    二元分割使其非常有效:在一个结构良好的树中,每个问题都会将选项数量减少一半,即使在大量分类中也很快缩小选项。 当然,这个技巧是决定在每个步骤中要问哪些问题。 在决策树的机器学习实现中,问题通常采用数据中轴对齐分割的形式:即,树中的每个节点使用其中一个特征中的分割值将数据分成两组。 现在来看一个例子。

    创建决策树

    考虑以下二维数据,它拥有四个标签之一:

    from sklearn.datasets import make_blobs
    
    X, y = make_blobs(n_samples=300, centers=4,
                      random_state=0, cluster_std=1.0)
    plt.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap='rainbow');
    

    根据这些数据建立的一个简单的决策树,将根据一些定量标准,沿着一个或另一个轴线迭代地分割数据,并且在每个级别,根据其中的多数表决来分配新区域的标签。 该图显示了该数据的决策树分类器的前四个级别的可视化:

    请注意,在第一次拆分之后,上部分支中的每个点保持不变,因此无需进一步细分此分支。 除了包含所有一种颜色的节点,在每个级别,每个区域再次沿着两个特征之一分裂。

    将决策树拟合到我们的数据的这个过程,可以在 Scikit-Learn 中使用DecisionTreeClassifier估计器来完成:

    from sklearn.tree import DecisionTreeClassifier
    tree = DecisionTreeClassifier().fit(X, y)
    

    让我们写一个简单的辅助函数,帮助我们展示分类器的输出:

    def visualize_classifier(model, X, y, ax=None, cmap='rainbow'):
        ax = ax or plt.gca()
        
        # Plot the training points
        ax.scatter(X[:, 0], X[:, 1], c=y, s=30, cmap=cmap,
                   clim=(y.min(), y.max()), zorder=3)
        ax.axis('tight')
        ax.axis('off')
        xlim = ax.get_xlim()
        ylim = ax.get_ylim()
        
        # fit the estimator
        model.fit(X, y)
        xx, yy = np.meshgrid(np.linspace(*xlim, num=200),
                             np.linspace(*ylim, num=200))
        Z = model.predict(np.c_[xx.ravel(), yy.ravel()]).reshape(xx.shape)
    
        # Create a color plot with the results
        n_classes = len(np.unique(y))
        contours = ax.contourf(xx, yy, Z, alpha=0.3,
                               levels=np.arange(n_classes + 1) - 0.5,
                               cmap=cmap, clim=(y.min(), y.max()),
                               zorder=1)
    
        ax.set(xlim=xlim, ylim=ylim)
    

    现在我们可以检测,决策树看起来是什么样子:

    visualize_classifier(DecisionTreeClassifier(), X, y)
    

    如果您现在正在运行这个笔记,您可以使用“在线附录”中包含的帮助脚本,来启动决策树构建过程的交互式可视化:

    # helpers_05_08 is found in the online appendix
    import helpers_05_08
    helpers_05_08.plot_tree_interactive(X, y);
    

    请注意,随着深度的增加,我们倾向于获得非常奇怪的分类区域; 例如,在第五层,黄色和蓝色区域之间有一个高而瘦的紫色区域。 很明显,这不是真实的,固有的数据分布结果,更多的是数据的特定采样或噪声属性的结果。 也就是说,这个决策树,即使只有五个层次的深度,显然对我们的数据过拟合了。

    决策树和过拟合

    这种过度拟合是决策树的一般属性:在树中很容易就走得太深,从而拟合特定数据的细节,而不是抽取它们分布的整体属性。 查看这种过拟合的另一种方法是,查看在不同数据子集上训练的模型 - 例如,在这个图中,我们训练两种不同的树,每种都是原始数据的一半:

    很明显,在一些地方,两棵树产生一致的结果(例如在四个角落),而在其他地方,这两棵树给出非常不同的分类(例如,在任何两个簇之间的区域中)。 关键观察是,分类不太确定的地方,会发生不一致,因此通过使用这两种树的信息,我们可能会得到更好的结果!

    如果您正在运行这个笔记,以下功能允许您交互显示树的拟合,在数据的随机子集上训练:

    # helpers_05_08 is found in the online appendix
    import helpers_05_08
    helpers_05_08.randomized_tree_interactive(X, y)
    

    正如使用来自两棵树的信息,改善了我们的结果,我们可能希望使用来自许多树的信息,进一步改善我们的结果。

    估计器的组合:随机森林

    这个概念 - 多个过拟合估计器可以组合,来减少这种过拟合的影响 - 是一种称为装袋的组合方法。 这个方法使用了一组并行估计器,每个都对数据过拟合,并对结果进行平均以找到更好的分类。 随机决策树的一个组合被称为随机森林。

    这种类型的装袋分类,可以使用 Scikit-Learn 的BaggingClassifier元估计器手动进行,如下所示:

    from sklearn.tree import DecisionTreeClassifier
    from sklearn.ensemble import BaggingClassifier
    
    tree = DecisionTreeClassifier()
    bag = BaggingClassifier(tree, n_estimators=100, max_samples=0.8,
                            random_state=1)
    
    bag.fit(X, y)
    visualize_classifier(bag, X, y)
    

    在这个例子中,我们将每个估计器拟合训练点的 80% 随机子集进行来随机化数据。 在实践中,通过在选择分割的方式中添加一些随机性,来更有效地随机化决策树:这样,所有数据每次都有助于拟合,但是拟合的结果仍然具有所需的随机性。 例如,当确定要分割的特征时,随机化树可以从前几个特征中选择。 您可以在 Scikit-Learn 文档中阅读这些随机策略的更多技术细节和参考。

    在 Scikit-Learn 中,随机决策树的优化组合在RandomForestClassifier估计器中实现,它自动地处理所有的随机化。 所有你需要做的是选择一些估计器,它将很快(如果需要则并行)拟合树的组合:

    from sklearn.ensemble import RandomForestClassifier
    
    model = RandomForestClassifier(n_estimators=100, random_state=0)
    visualize_classifier(model, X, y);
    

    我们看到,通过对超过 100 个随机扰动的模型取平均,我们最终得到一个整体模型,更接近我们关于如何分割参数空间的直觉。

    随机森林回归

    在上一节中,我们在分类范围内考虑了随机森林。 随机森林也可以用于回归(即连续而不是分类变量)。 用于此的估计器是RandomForestRegressor,并且语法与我们之前看到的非常相似。

    考虑以下数据,由快速和慢速振荡的组合产生:

    rng = np.random.RandomState(42)
    x = 10 * rng.rand(200)
    
    def model(x, sigma=0.3):
        fast_oscillation = np.sin(5 * x)
        slow_oscillation = np.sin(0.5 * x)
        noise = sigma * rng.randn(len(x))
    
        return slow_oscillation + fast_oscillation + noise
    
    y = model(x)
    plt.errorbar(x, y, 0.3, fmt='o');
    

    使用随机森林回归器,我们可以找到最佳拟合曲线,

    这里的真实模型以灰色平滑曲线中显示,随机森林模型由红色锯齿曲线显示。 可以看出,非参数随机森林模型足够灵活,可以拟合多周期数据,而不需要指定多周期模型!

    示例:随机森林数字分类

    早些时候我们快速浏览了手写数字数据(参见 Scikit-Learn 介绍)。 让我们再次使用它,来看看如何在这个上下文中使用随机森林分类器。

    from sklearn.datasets import load_digits
    digits = load_digits()
    digits.keys()
    # dict_keys(['target', 'data', 'target_names', 'DESCR', 'images'])
    

    为了提醒我们,我们正在观察什么,我们展示前几个数据点。

    # set up the figure
    fig = plt.figure(figsize=(6, 6))  # figure size in inches
    fig.subplots_adjust(left=0, right=1, bottom=0, top=1, hspace=0.05, wspace=0.05)
    
    # plot the digits: each image is 8x8 pixels
    for i in range(64):
        ax = fig.add_subplot(8, 8, i + 1, xticks=[], yticks=[])
        ax.imshow(digits.images[i], cmap=plt.cm.binary, interpolation='nearest')
        
        # label the image with the target value
        ax.text(0, 7, str(digits.target[i]))
    

    我们可以快速使用随机森林对这些数字分类,像这样:

    from sklearn.cross_validation import train_test_split
    
    Xtrain, Xtest, ytrain, ytest = train_test_split(digits.data, digits.target,
                                                    random_state=0)
    model = RandomForestClassifier(n_estimators=1000)
    model.fit(Xtrain, ytrain)
    ypred = model.predict(Xtest)
    

    我们可以看一看分类器的分类报告:

    from sklearn import metrics
    print(metrics.classification_report(ypred, ytest))
    
                 precision    recall  f1-score   support
    
              0       1.00      0.97      0.99        38
              1       1.00      0.98      0.99        44
              2       0.95      1.00      0.98        42
              3       0.98      0.96      0.97        46
              4       0.97      1.00      0.99        37
              5       0.98      0.96      0.97        49
              6       1.00      1.00      1.00        52
              7       1.00      0.96      0.98        50
              8       0.94      0.98      0.96        46
              9       0.96      0.98      0.97        46
    
    avg / total       0.98      0.98      0.98       450
    

    为了更好的度量,绘制混淆矩阵:

    from sklearn.metrics import confusion_matrix
    mat = confusion_matrix(ytest, ypred)
    sns.heatmap(mat.T, square=True, annot=True, fmt='d', cbar=False)
    plt.xlabel('true label')
    plt.ylabel('predicted label');
    

    我们发现,简单无调整的随机森林,产生了数据的非常准确的分类。

    随机森林总结

    本节简要介绍了组合估计器的概念,特别是随机森林 - 随机决策树的整体。 随机森林是一个强大的方法,具有几个优点:

    • 训练和预测都非常快,因为底层决策树简单。 此外,两个任务都可以直接并行化,因为各个树是完全独立的实体。
    • 多个树提供了概率分类:估计器之间的多数表决提供了概率估计(在 Scikit-Learn 中使用predict_proba()方法来访问)。
    • 非参数模型是非常灵活的,因此可以在其他估计器拟合不足的任务上表现良好。

    随机森林的主要缺点是结果不容易解释:即如果要对分类模型的含义作出总结,随机森林可能不是最佳选择。

    相关文章

      网友评论

        本文标题:Python 数据科学手册 5.8 决策树和随机森林

        本文链接:https://www.haomeiwen.com/subject/kmyacxtx.html