Py学习  »  Python

【Python数据科学手册】专题:决策树与随机森林

天池大数据科研平台 • 4 年前 • 426 次点击  

本文将介绍一种强大的算法——无参数算法随机森林。随机森林是一种集成方法,通过集成多个比较简单的评估器形成累积效果。这种集成方法的学习效果经常出人意料,往往能超过各个组成部分的总和;也就是说,若干评估器的多数投票(majority vote)的最终效果往往优于单个评估器投票的效果!后面将通过示例来演示,首先还是导入标准的程序库:

%matplotlib inlineimport numpy as npimport matplotlib.pyplot as pltimport seaborn as sns; sns.set()

随机森林的诱因:决策树

随机森林是建立在决策树基础上的集成学习器。因此,首先来介绍一下决策树。

决策树采用非常直观的方式对事物进行分类或打标签:你只需问一系列问题就可以进行分类了。例如,如果你想建一棵决策树来判断旅行时遇到的一只动物的种类,你就可以提出如图所示的问题。

二叉树分支方法可以非常有效地进行分类:在一棵结构合理的决策树中,每个问题基本上都可将种类可能性减半;即使是对大量种类进行决策时,也可以很快地缩小选择范围。

不过,决策树的难点在于如何设计每一步的问题。在实现决策树的机器学习算法中,问题通常因分类边界是与特征轴平行5 的形式分割数据而造成的;也就是说,决策树的每个节点都根据一个特征的阈值将数据分成两组。下面通过示例来演示。

1

创建一棵决策树

看看下面的二维数据,它一共有四种标签

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');

在这组数据上构建的简单决策树不断将数据的一个特征或另一个特征按照某种判定条件进行分割。每分割一次,都将新区域内点的多数投票结果标签分配到该区域上。下图展示了决策树对这组数据前四次分割的可视化结果。

import numpy as npimport matplotlib.pyplot as pltfrom sklearn.tree import DecisionTreeClassifierfrom ipywidgets import interact

def visualize_tree(estimator, X, y, boundaries=True, xlim=None, ylim=None, ax=None): ax = ax or plt.gca() # Plot the training points ax.scatter(X[:, 0], X[:, 1], c=y, s=30, cmap='viridis', clim=(y.min(), y.max()), zorder=3) ax.axis('tight') ax.axis('off') if xlim is None: xlim = ax.get_xlim() if ylim is None: ylim = ax.get_ylim() # fit the estimator estimator.fit(X, y) xx, yy = np.meshgrid(np.linspace(*xlim, num=200), np.linspace(*ylim, num=200)) Z = estimator.predict(np.c_[xx.ravel(), yy.ravel()])
# Put the result into a color plot n_classes = len(np.unique(y)) Z = Z.reshape(xx.shape) contours = ax.contourf(xx, yy, Z, alpha=0.3, levels=np.arange(n_classes + 1) - 0.5, cmap='viridis', clim=(y.min(), y.max()), zorder=1)
ax.set(xlim=xlim, ylim=ylim) # Plot the decision boundaries def plot_boundaries(i, xlim, ylim): if i >= 0: tree = estimator.tree_ if tree.feature[i] == 0: ax.plot([tree.threshold[i], tree.threshold[i]], ylim, '-k', zorder=2) plot_boundaries(tree.children_left[i], [xlim[0], tree.threshold[i]], ylim) plot_boundaries(tree.children_right[i], [tree.threshold[i], xlim[1]], ylim) elif tree.feature[i] == 1: ax.plot(xlim, [tree.threshold[i], tree.threshold[i]], '-k', zorder=2) plot_boundaries(tree.children_left[i], xlim, [ylim[0], tree.threshold[i]]) plot_boundaries(tree.children_right[i], xlim, [tree.threshold[i], ylim[1]]) if boundaries: plot_boundaries(0, xlim, ylim)

def plot_tree_interactive(X, y): def interactive_tree(depth=5): clf = DecisionTreeClassifier(max_depth=depth, random_state=0) visualize_tree(clf, X, y)
return interact(interactive_tree, depth=[1, 5])

def randomized_tree_interactive(X, y): N = int(0.75 * X.shape[0]) xlim = (X[:, 0].min(), X[:, 0].max()) ylim = (X[:, 1].min(), X[:, 1].max()) def fit_randomized_tree(random_state=0): clf = DecisionTreeClassifier(max_depth=15) i = np.arange(len(y)) rng = np.random.RandomState(random_state) rng.shuffle(i) visualize_tree(clf, X[i[:N]], y[i[:N]], boundaries=False, xlim=xlim, ylim=ylim) interact(fit_randomized_tree, random_state=[0, 100]);
#from helpers_05_08 import visualize_treefrom sklearn.tree import DecisionTreeClassifierfrom sklearn.datasets import make_blobs
fig, ax = plt.subplots(1, 4, figsize=(16, 3))fig.subplots_adjust(left=0.02, right=0.98, wspace=0.1)
X, y = make_blobs(n_samples=300, centers=4, random_state=0, cluster_std=1.0)
for axi, depth in zip(ax, range(1, 5)): model = DecisionTreeClassifier(max_depth=depth) visualize_tree(model, X, y, ax=axi) axi.set_title('depth = {0}'.format(depth))

需要注意的是,在第一次分割之后,上半个分支里的所有数据点都没有变化,因此这个分支不需要继续分割。除非一个节点只包含一种颜色,那么每次分割都需要按照两种特征中的一种对每个区域进行分割。

如果想在Scikit-Learn 中使用决策树拟合数据,可以用DecisionTreeClassifier 评估器:

from sklearn.tree import DecisionTreeClassifiertree = 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_08plot_tree_interactive(X, y);

请注意,随着决策树深度的不断增加,我们可能会看到形状非常奇怪的分类区域。例如,在深度为5 的时候,在黄色与蓝色区域中间有一个狭长的浅紫色区域。这显然不是根据数据本身的分布情况生成的正确分类结果,而更像是一个特殊的数据样本或数据噪音形成的干扰结果。也就是说,这棵决策树刚刚分到第5 层,数据就出现了过拟合。

2

决策树与过拟合

这种过拟合其实正是决策树的一般属性——决策树非常容易陷得很深,因此往往会拟合局部数据,而没有对整个数据分布的大局观。换个角度看这种过拟合,可以认为模型训练的是数据的不同子集。例如,在图中我们训练了两棵不同的决策树,每棵树拟合一半数据。

model = DecisionTreeClassifier()
fig, ax = plt.subplots(1, 2, figsize=(16, 6))fig.subplots_adjust(left=0.0625, right=0.95, wspace=0.1)visualize_tree(model, X[::2], y[::2], boundaries=False, ax=ax[0])visualize_tree(model, X[1::2], y[1::2], boundaries=False, ax=ax[1])

显然,在一些区域,两棵树产生了一致的结果(例如4个角上);而在另一些区域,两棵树的分类结果差异很大(例如两类接壤的区域)。不一致往往都发生在分类比较模糊的地方,因此假如将两棵树的结果组合起来,可能就会获得更好的结果!

randomized_tree_interactive(X, y)

就像用两棵决策树的信息改善分类结果一样,我们可能想用更多决策树的信息来改善分类结果。

评估器集成算法:随机森林

通过组合多个过拟合评估器来降低过拟合程度的想法其实是一种集成学习方法,称为装袋算法。装袋算法使用并行评估器对数据进行有放回抽取集成(也可以说是大杂烩),每个评估器都对数据过拟合,通过求均值可以获得更好的分类结果。随机决策树的集成算法就是随机森林

我们可以用Scikit-Learn 的BaggingClassifier 元评估器来实现这种装袋分类器

from sklearn.tree import DecisionTreeClassifierfrom 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% 的随机数。其实,如果我们用随机方法(stochasticity)确定数据的分割方式,决策树拟合的随机性会更有效;这样做可以让所有数据在每次训练时都被拟合,但拟合的结果却仍然是随机的。例如,当需要确定对哪个特征进行分割时,随机树可能会从最前面的几个特征中挑选。关于随机策略选择的更多技术细节,请参考Scikit-Learn 文档(http://scikit-learn.org/stable/modules/ensemble.html#forest)。

在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');

通过随机森林回归器,可以获得下面的最佳拟合曲线

from sklearn.ensemble import RandomForestRegressorforest = RandomForestRegressor(200)forest.fit(x[:, None], y)
xfit = np.linspace(0, 10, 1000)yfit = forest.predict(xfit[:, None])ytrue = model(xfit, sigma=0)
plt.errorbar(x, y, 0.3, fmt='o', alpha=0.5)plt.plot(xfit, yfit, '-r');plt.plot(xfit, ytrue, '-k', alpha=0.5);

真实模型是平滑曲线,而随机森林模型是锯齿线。从图中可以看出,无参数的随机森林模型非常适合处理多周期数据,不需要我们配置多周期模型!

用随机森林识别手写数字

前面简单介绍过手写数字数据集。再一次使用这些数据,看看随机森林分类器如何解决这个问题

from sklearn.datasets import load_digitsdigits = load_digits()digits.keys()

显示前几个数字图像,看看分类的对象

# set up the figurefig = plt.figure(figsize=(6, 6))  # figure size in inchesfig.subplots_adjust(left=0, right=1, bottom=0, top=1, hspace=0.05, wspace=0.05)
# plot the digits: each image is 8x8 pixelsfor 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 metricsprint(metrics.classification_report(ypred, ytest))

为了更好地验证结果,画出混淆矩阵

from sklearn.metrics import confusion_matrixmat = 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() 方法)。

  • 无参数模型很灵活,在其他评估器都欠拟合的任务中表现突出。

随机森林的主要缺点在于其结果不太容易解释,也就是说,如果你想要总结分类模型的意义,随机森林可能不是最佳选择。





如果你也有想分享的干货,可以登录天池实验室(notebook),包括赛题的理解、数据分析及可视化、算法模型的分析以及一些核心的思路等内容

小天会根据你分享内容的数量以及程度,给予丰富的神秘天池大礼以及粮票奖励。分享成功后你也可以通过下方钉钉群👇主动联系我们的社区运营同学(钉钉号:yiwen1991

天池宝贝们有任何问题,可在戳“留言”评论或加入钉钉群留言,小天会认真倾听每一个你的建议!


点击下方图片即可阅读

那么如何了解线性回归的原理和算法呢?

分类算法评估中的“它们”有什么用处?

学好Scikit-Learn,

于是你默默地推了一下酷酷的墨镜【上篇】


学会PyTorch,你就是这条街上最靓的仔~



如果你也有想分享的干货,可以登录天池实验室(notebook),包括赛题的理解、数据分析及可视化、算法模型的分析以及一些核心的思路等内容

小天会根据你分享内容的数量以及程度,给予丰富的神秘天池大礼以及粮票奖励。分享成功后你也可以通过下方钉钉群👇主动联系我们的社区运营同学(钉钉号: yiwen1991


👇「阅读原文」,PC端fork学习更容易!

Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/33138
 
426 次点击