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

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

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

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

一、随机森林的诱因:决策树

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

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

image.png

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

不过,决策树的难点在于如何设计每一步的问题。在实现决策树的机器学习算法中,问题通常因分类边界是与特征轴平行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');

image.png

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

import numpy as np
import matplotlib.pyplot as plt
from sklearn.tree import DecisionTreeClassifier
from 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_tree
from sklearn.tree import DecisionTreeClassifier
from 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))

image.png
image.png

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

如果想在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)

image.png
image.png

生成决策树创建过程的交互式可视化

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

image.png
image.png

请注意,随着决策树深度的不断增加,我们可能会看到形状非常奇怪的分类区域。例如,在深度为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])

image.png
image.png

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

randomized_tree_interactive(X, y)

image.png
image.png

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

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

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

我们可以用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)

image.png
image.png

在这个示例中,我们让每个评估器拟合样本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);

image.png
image.png

从图中可以看出,如果使用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');

image.png

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

from sklearn.ensemble import RandomForestRegressor
forest = 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);

image.png

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

四、用随机森林识别手写数字

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

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

image.png

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

# 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]))

image.png

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)

image.png

看看分类器的分类结果报告:

from sklearn import metrics
print(metrics.classification_report(ypred, ytest))

image.png

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

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

image.png

我们会发现,用一个简单、未调优的随机森林对手写数字进行分类,就可以取得非常好的分类准确率。

五、随机森林总结

这一节首先简要介绍了集成评估器的概念,然后重点介绍了随机森林模型——一种随机决策树集成算法。随机森林是一种强大的机器学习方法,它的优势在于以下几点。

  • 因为决策树的原理很简单,所以它的训练和预测速度都非常快。另外,多任务可以直接并行计算,因为每棵树都是完全独立的。
  • 多棵树可以进行概率分类:多个评估器之间的多数投票可以给出概率的估计值(使用Scikit-Learn 的predict_proba() 方法)。
  • 无参数模型很灵活,在其他评估器都欠拟合的任务中表现突出。

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

目录
相关文章
|
28天前
|
机器学习/深度学习 数据可视化 数据处理
从基础到进阶:探索Python在数据科学中的应用
【10月更文挑战第18天】从基础到进阶:探索Python在数据科学中的应用
39 1
|
1月前
|
机器学习/深度学习 数据采集 数据可视化
Python 数据分析:从零开始构建你的数据科学项目
【10月更文挑战第9天】Python 数据分析:从零开始构建你的数据科学项目
55 2
|
11天前
|
机器学习/深度学习 数据采集 数据可视化
Python在数据科学中的应用:从入门到实践
本文旨在为读者提供一个Python在数据科学领域应用的全面概览。我们将从Python的基础语法开始,逐步深入到数据处理、分析和可视化的高级技术。文章不仅涵盖了Python中常用的数据科学库,如NumPy、Pandas和Matplotlib,还探讨了机器学习库Scikit-learn的使用。通过实际案例分析,本文将展示如何利用Python进行数据清洗、特征工程、模型训练和结果评估。此外,我们还将探讨Python在大数据处理中的应用,以及如何通过集成学习和深度学习技术来提升数据分析的准确性和效率。
|
11天前
|
机器学习/深度学习 数据可视化 数据处理
Python数据科学:从基础到实战
Python数据科学:从基础到实战
21 1
|
14天前
|
算法 Python
Python 大神修炼手册:图的深度优先&广度优先遍历,深入骨髓的解析
在 Python 编程中,掌握图的深度优先遍历(DFS)和广度优先遍历(BFS)是进阶的关键。这两种算法不仅理论重要,还能解决实际问题。本文介绍了图的基本概念、邻接表表示方法,并给出了 DFS 和 BFS 的 Python 实现代码示例,帮助读者深入理解并应用这些算法。
28 2
|
22天前
|
机器学习/深度学习 数据可视化 数据处理
Python在数据科学中的应用###
本文探讨了Python语言在数据科学领域的广泛应用及其重要性。通过分析Python的简洁语法、强大的库支持和跨平台特性,阐述了为何Python成为数据科学家的首选工具。文章还介绍了Python在数据处理、分析和可视化方面的具体应用实例,展示了其在提升工作效率和推动科学研究方面的巨大潜力。最后,讨论了未来Python在数据科学领域的发展趋势和挑战。 ###
|
25天前
|
机器学习/深度学习 分布式计算 数据可视化
Python在数据科学中的应用与挑战
本文探讨了Python编程语言在数据科学领域的广泛应用及其面临的主要挑战。Python因其简洁的语法、强大的库支持和活跃的社区,已成为数据科学家的首选工具。然而,随着数据量的激增和复杂性的增加,Python也面临着性能瓶颈、内存管理等问题。本文将通过具体案例分析,展示Python在数据处理、分析和可视化方面的优势,同时讨论如何克服其在大规模数据处理中的局限性,为读者提供实用的解决方案和优化建议。
|
1月前
|
JSON 数据可视化 API
玩转数据科学:Python实战分析天气预报变动趋势
【10月更文挑战第1天】随着气候变化对日常生活的影响日益显著,理解和预测天气模式变得越来越重要。本文将引导您如何使用Python来抓取和分析天气预报数据,从而揭示天气变化的趋势。我们将介绍从获取公开气象API的数据到清洗、处理以及可视化整个过程的技术方法。
179 2
|
1月前
|
机器学习/深度学习 数据采集 算法
一个 python + 数据预处理+随机森林模型 (案列)
本文介绍了一个使用Python进行数据预处理和构建随机森林模型的实际案例。首先,作者通过删除不必要的列和特征编码对数据进行了预处理,然后应用随机森林算法进行模型训练,通过GridSearchCV优化参数,最后展示了模型的评估结果。
41 0
|
2月前
|
大数据 UED 开发者
实战演练:利用Python的Trie树优化搜索算法,性能飙升不是梦!
在数据密集型应用中,高效搜索算法至关重要。Trie树(前缀树/字典树)通过优化字符串处理和搜索效率成为理想选择。本文通过Python实战演示Trie树构建与应用,显著提升搜索性能。Trie树利用公共前缀减少查询时间,支持快速插入、删除和搜索。以下为简单示例代码,展示如何构建及使用Trie树进行搜索与前缀匹配,适用于自动补全、拼写检查等场景,助力提升应用性能与用户体验。
56 2
下一篇
无影云桌面