前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Python 数据科学手册 5.8 决策树和随机森林

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

作者头像
ApacheCN_飞龙
发布2022-12-01 17:27:16
3350
发布2022-12-01 17:27:16
举报
文章被收录于专栏:信数据得永生信数据得永生

5.8 决策树和随机森林

原文:In-Depth: Decision Trees and Random Forests 译者:飞龙 协议:CC BY-NC-SA 4.0 译文没有得到原作者授权,不保证与原文的意思严格一致。

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

代码语言:javascript
复制
%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns; sns.set()

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

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

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

创建决策树

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

代码语言:javascript
复制
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估计器来完成:

代码语言:javascript
复制
from sklearn.tree import DecisionTreeClassifier
tree = DecisionTreeClassifier().fit(X, y)

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

代码语言:javascript
复制
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)

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

代码语言:javascript
复制
visualize_classifier(DecisionTreeClassifier(), X, y)

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

代码语言:javascript
复制
# helpers_05_08 is found in the online appendix
import helpers_05_08
helpers_05_08.plot_tree_interactive(X, y);

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

决策树和过拟合

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

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

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

代码语言:javascript
复制
# helpers_05_08 is found in the online appendix
import helpers_05_08
helpers_05_08.randomized_tree_interactive(X, y)

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

估计器的组合:随机森林

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

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

代码语言:javascript
复制
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估计器中实现,它自动地处理所有的随机化。 所有你需要做的是选择一些估计器,它将很快(如果需要则并行)拟合树的组合:

代码语言:javascript
复制
from sklearn.ensemble import RandomForestClassifier

model = RandomForestClassifier(n_estimators=100, random_state=0)
visualize_classifier(model, X, y);

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

随机森林回归

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

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

代码语言:javascript
复制
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 介绍)。 让我们再次使用它,来看看如何在这个上下文中使用随机森林分类器。

代码语言:javascript
复制
from sklearn.datasets import load_digits
digits = load_digits()
digits.keys()
# dict_keys(['target', 'data', 'target_names', 'DESCR', 'images'])

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

代码语言:javascript
复制
# 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]))

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

代码语言:javascript
复制
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)

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

代码语言:javascript
复制
from sklearn import metrics
print(metrics.classification_report(ypred, ytest))
代码语言:javascript
复制
             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

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

代码语言:javascript
复制
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()方法来访问)。
  • 非参数模型是非常灵活的,因此可以在其他估计器拟合不足的任务上表现良好。

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

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2017-07-03,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 5.8 决策树和随机森林
    • 创建决策树
      • 决策树和过拟合
        • 估计器的组合:随机森林
          • 随机森林回归
            • 示例:随机森林数字分类
              • 随机森林总结
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档