前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >机器学习–组合分类方法之随机森林算法原理和实现(RF)

机器学习–组合分类方法之随机森林算法原理和实现(RF)

作者头像
全栈程序员站长
发布2022-07-25 12:36:06
1.3K0
发布2022-07-25 12:36:06
举报
文章被收录于专栏:全栈程序员必看

大家好,又见面了,我是你们的朋友全栈君。

上一节我们详细的介绍了组合分类方法中的boosting提升算法中经典的adaboost提升算法,当然还有其他的提升算法例如:前向分步算法(adaboost算法是该算法的一个特殊情况,)、提升树算法(基于加法模型和前向分布算法),其中提升树的学习算法即损失函数有:平方误差损失函数、指数损失函数、梯度损失函数等在这里就不细讲他们了,因为他们的算法思想都是基于boost提升的,只是学习算法不同罢了,有兴趣的同学可以参考李航的《统计学习方法》,这本书有提到上面的算法。

装袋(bagging,表示自助聚集(bootstrap aggregation)):

这一节我们主要介绍的是组合分类方法中的另一个大类基于bagging的方法,他所代表的最典型的的算法就是随机森林算法,其中bagging算法和boosting系列算法的最大不同在于:基学习器(子分类器)和 数据选择上不同

boosting是基于弱学习器(如上节最简单的单一决策树)为基础学习器,而且各个弱学习之间是有依赖的即串联关系,给训练数据分配权值,每次训练一个弱学习器,并给该弱学习器分配权值,同时这个弱学习器分类错的数据将在下一个训练弱学习器加强权值

bagging是基于强学习器(如CART决策树)为基础学习器,每个子学习器没有依赖关系,可以并行拟合,只是在训练上和boosting不同,不同主要表现在:bagging算法是在原始的数据集上采用有放回的随机取样的方式来抽取m个子样本,从而利用这m个子样本训练m个基学习器,从而降低了模型的方差。

我们先看看bagging的算法,再引入随机深林的算法下面给出伪代码然后解释:

下面把伪代码每行解释一下:

(1)k是指创建k个基学习器

(2)这里是个关键点,和boosting的不同也在这里,这里是通过在样本集D中随机的有放回的抽出d个样本作为训练基分类器的样本数据,这里需要强调的是,每个基学习器的样本都是从D中有放回的随机抽取d个样本(和原始数据集的个数保持一致),那么这样做的,每个基学习器的样本可能就含有多个相同的样本,同时也可能不含有D中的样本,这个大家需要好好理解,下面的随机森林会在此基础上继续改变。

(3)使用创建的自助样本去训练基学习器,并导出(这里虽然是for循环说明是一个一个训练的,但是可以同时训练,因为基学习器和训练数据没有必然的联系,可以同时训练并导出模型,这里需要注意,这是和boosting最本质的区别,boosting是一个一个的训练学习器,同时把本次训练的学习器分类错误的数据提高权重,下一个学习器会着重训练该数据,这是最大的不同,大家需要深入理解,这也是以后选择模型的关键区别了)

(4)训练结束

上面就是bagging算法了,大家应该能理解他和boosting的不同了,那么下面就引入我们本节的主角:随机森林

随机森林:

其实随机森林没什么高深的地方,他就是在bagging算法的基础上进一步演化而来,此时的基学习器就是决策树(CART),而选择训练数据时比bagging更具有特点,特点表现在随机上,稍后会详解这个特点,下面给出随机森林的定义:

随机森林就是通过集成学习的思想将多棵树集成的一种算法,它的基本单元是决策树,而它的本质属于机器学习的一大分支——集成学习(Ensemble Learning)方法(组合方法)

在讲解随机森林之前,大家需要理解基础知识:

决策树:不理解的请看我的这篇文章,决策树不难,难点在于决策树的依据那些信息进行决策,这是难点,大家务必理解了,一旦理解了,所有的决策树最大的区别就在这里,例如我那篇文章讲的主要是基于信息熵、信息增益的,还有是基于基尼纯度的,还有我们上节的adaboost是基于单一决策树是基于阈值的,所以一旦理解了,你会发现不同决策树的的最大不同就是在决策依据这一块,不理解的请好好理解,别拖,拖到最后你还是要理解,那时候就晚了。

好,先默认大家都深入理解了决策树,下面先总体看一下随机森林的工作过程,然后在详解:

随机森林通过自助法(bootstrap)重采样技术,从原始训练样本集N中有放回地重复随机抽取k个样本生成新的训练样本集合,然后根据自助样本集生成k个分类树组成随机森林,新数据的分类结果按分类树投票多少形成的分数而定。其实质是对决策树算法的一种改进,将多个决策树合并在一起,每棵树的建立依赖于一个独立抽取的样品,森林中的每棵树具有相同的分布,分类误差取决于每一棵树的分类能力和它们之间的相关性。特征选择采用随机的方法去分裂每一个节点,然后比较不同情况下产生的误差。能够检测到的内在估计误差、分类能力和相关性决定选择特征的数目。单棵树的分类能力可能很小,但在随机产生大量的决策树后,一个测试样品可以通过每一棵树的分类结果经统计后选择最可能的分类。

下面我们详细说明一下随机森林的工作过程:

和上面的bagging算法很类似,不同的是第二步上,下面我们详细解说一下一个基学习器的学习过程:

1)如果训练集大小为N,对于每棵树而言,随机且有放回地从训练集中的抽取N个训练样本(这种采样方式称为bootstrap sample方法),作为该树的训练集;

   从这里我们可以知道:每棵树的训练集都是不同的,而且里面包含重复的训练样本(理解这点很重要),这一步和bagging相同。

2)如果每个样本的特征维度为M,指定一个常数m<<M,随机地从M个特征中选取m个特征子集,每次树进行分裂时,从这m个特征中选择最优的;

3)每棵树都尽最大程度的生长,并且没有剪枝过程。

到这里我们和bagging算法对比一下,首先bagging的每个基学习器样本也是这样进行抽样的,随机深林不同的是在此基础上进一步随机了,即在每个样本的特征进行进一步的随机性选择,这样两个随机就出了,这也是随机深林的随机之处了,那么这样做有什好处呢?和bagging相比有什么优势呢?

我们知道bagging算法是在原始的数据集上采用有放回的随机取样的方式来抽取m个子样本,从而利用这m个子样本训练m个基学习器,从而降低了模型的方差。而我们的随机森林的改动有两处,第一:不仅随机的从原始数据集中随机的抽取m个子样本,而且在训练每个基学习器的时候,不是从所有特征中选择最优特征来进行节点的切分,而是随机的选取k个特征,从这k个特征中选择最优特征来切分节点,从而更进一步的降低了模型的方差;第二:随机森林使用的基学习器是CART决策树。两个随机性的引入对随机森林的分类性能至关重要。由于它们的引入,使得随机森林不容易陷入过拟合,并且具有很好得抗噪能力(比如:对缺省值不敏感)。

随机森林随机选择的样本子集大小m越小模型的方差就会越小,但是偏差会越大,所以在实际应用中,我们一般会通过交叉验证的方式来调参,从而获取一个合适的样本子集的大小。所以随机森林除了基学习器使用CART决策树和特征的随机选择以外,其他方面与bagging方法没有什么不同。

随机森林分类效果(错误率)与两个因素有关:

  • 森林中任意两棵树的相关性:相关性越大,错误率越大;
  • 森林中每棵树的分类能力:每棵树的分类能力越强,整个森林的错误率越低。

  减小特征选择个数m,树的相关性和分类能力也会相应的降低;增大m,两者也会随之增大。所以关键问题是如何选择最优的m(或者是范围),这也是随机森林唯一的一个参数。

到这里我们基本上就讲完了随机森林的算法,现在我们总结一下随机森林的特点:

  • 在当前所有算法中,具有极好的准确率
  • 能够有效地运行在大数据集上
  • 能够处理具有高维特征的输入样本,而且不需要降维
  • 能够评估各个特征在分类问题上的重要性
  • 在生成过程中,能够获取到内部生成误差的一种无偏估计
  • 对于缺省值问题也能够获得很好得结果
  • … …

  实际上,随机森林的特点不只有这六点,它就相当于机器学习领域的Leatherman(多面手),你几乎可以把任何东西扔进去,它基本上都是可供使用的。在估计推断映射方面特别好用,以致都不需要像SVM那样做很多参数的调试。具体的随机森林介绍可以参见随机森林主页:Random Forest

到这里基本就结束了,当然还有很多其他类型的随机森林请参考sklearn官方网站和森林主页进行查看:

Extremely Randomized Trees(极限随机树):

(1)对于每个决策树的训练集,RF采用的是随机采样bootstrap来选择子集作为每个决策树的训练集,而extra trees一般不采用随机采样,即每个决策树采用原始训练集。

   (2)在选定了划分特征后,RF的决策树会基于信息增益,基尼系数,均方差之类的原则,选择一个最优的特征值划分点,这和传统的决策树相同。但是extra trees比较的激进,他会随机的选择一个特征值来划分决策树。

   从第二点可以看出,由于随机选择了特征值的划分点位,而不是最优点位,这样会导致生成的决策树的规模一般会大于RF所生成的决策树。也就是说,模型的方差相对于RF进一步减少,但是偏差相对于RF进一步增大。在某些时候,extra trees的泛化能力比RF更好。

还有一个是 Isolation Forest,请自行百度了解。

下面给出sklearn随机森林的代码:

输出信息:

代码语言:javascript
复制
Automatically created module for IPython interactive environment
DecisionTree with features [0, 1] has a score of 0.9266666666666666
RandomForest with 30 estimators with features [0, 1] has a score of 0.9266666666666666
ExtraTrees with 30 estimators with features [0, 1] has a score of 0.9266666666666666
AdaBoost with 30 estimators with features [0, 1] has a score of 0.84
DecisionTree with features [0, 2] has a score of 0.9933333333333333
RandomForest with 30 estimators with features [0, 2] has a score of 0.9933333333333333
ExtraTrees with 30 estimators with features [0, 2] has a score of 0.9933333333333333
AdaBoost with 30 estimators with features [0, 2] has a score of 0.9933333333333333
DecisionTree with features [2, 3] has a score of 0.9933333333333333
RandomForest with 30 estimators with features [2, 3] has a score of 0.9933333333333333
ExtraTrees with 30 estimators with features [2, 3] has a score of 0.9933333333333333
AdaBoost with 30 estimators with features [2, 3] has a score of 0.9933333333333333

分析:

该图比较决策树分类器(第一列),随机森林分类器(第二列),外树分类器(第三列)和AdaBoost分类器(第四列)学习的决策表面。在第一行中,分类器仅使用萼片宽度和萼片长度特征构建,仅使用花瓣长度和萼片长度在第二行上构建,并且仅使用花瓣宽度和花瓣长度在第三行上构建。

按照质量的降序,当使用30个估算器对所有4个特征进行训练(在此示例之外)并使用10倍交叉验证进行评分时,我们看到:

代码语言:javascript
复制
ExtraTreesClassifier()  #0.95得分
RandomForestClassifier()  #0.94得分
AdaBoost (DecisionTree (max_depth = 3 ))  #0.94得分
DecisionTree (max_depth = 无)  #0.94得分

增加AdaBoost的max_depth会降低分数的标准偏差(但平均分数不会提高)。细看上面的信息输出,下面给出sklearn的源码:

代码语言:javascript
复制
print(__doc__)

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap

from sklearn import clone
from sklearn.datasets import load_iris
from sklearn.ensemble import (RandomForestClassifier, ExtraTreesClassifier,
                              AdaBoostClassifier)
from sklearn.tree import DecisionTreeClassifier

# Parameters
n_classes = 3
n_estimators = 30
cmap = plt.cm.RdYlBu
plot_step = 0.02  # fine step width for decision surface contours
plot_step_coarser = 0.5  # step widths for coarse classifier guesses
RANDOM_SEED = 13  # fix the seed on each iteration

# Load data
iris = load_iris()

plot_idx = 1

models = [DecisionTreeClassifier(max_depth=None),
          RandomForestClassifier(n_estimators=n_estimators),
          ExtraTreesClassifier(n_estimators=n_estimators),
          AdaBoostClassifier(DecisionTreeClassifier(max_depth=3),
                             n_estimators=n_estimators)]

for pair in ([0, 1], [0, 2], [2, 3]):
    for model in models:
        # We only take the two corresponding features
        X = iris.data[:, pair]
        y = iris.target

        # Shuffle
        idx = np.arange(X.shape[0])
        np.random.seed(RANDOM_SEED)
        np.random.shuffle(idx)
        X = X[idx]
        y = y[idx]

        # Standardize
        mean = X.mean(axis=0)
        std = X.std(axis=0)
        X = (X - mean) / std

        # Train
        clf = clone(model)
        clf = model.fit(X, y)

        scores = clf.score(X, y)
        # Create a title for each column and the console by using str() and
        # slicing away useless parts of the string
        model_title = str(type(model)).split(
            ".")[-1][:-2][:-len("Classifier")]

        model_details = model_title
        if hasattr(model, "estimators_"):
            model_details += " with {} estimators".format(
                len(model.estimators_))
        print(model_details + " with features", pair,
              "has a score of", scores)

        plt.subplot(3, 4, plot_idx)
        if plot_idx <= len(models):
            # Add a title at the top of each column
            plt.title(model_title)

        # Now plot the decision boundary using a fine mesh as input to a
        # filled contour plot
        x_min, x_max = X[:, 0].min() - 1, X[:, 0].max() + 1
        y_min, y_max = X[:, 1].min() - 1, X[:, 1].max() + 1
        xx, yy = np.meshgrid(np.arange(x_min, x_max, plot_step),
                             np.arange(y_min, y_max, plot_step))

        # Plot either a single DecisionTreeClassifier or alpha blend the
        # decision surfaces of the ensemble of classifiers
        if isinstance(model, DecisionTreeClassifier):
            Z = model.predict(np.c_[xx.ravel(), yy.ravel()])
            Z = Z.reshape(xx.shape)
            cs = plt.contourf(xx, yy, Z, cmap=cmap)
        else:
            # Choose alpha blend level with respect to the number
            # of estimators
            # that are in use (noting that AdaBoost can use fewer estimators
            # than its maximum if it achieves a good enough fit early on)
            estimator_alpha = 1.0 / len(model.estimators_)
            for tree in model.estimators_:
                Z = tree.predict(np.c_[xx.ravel(), yy.ravel()])
                Z = Z.reshape(xx.shape)
                cs = plt.contourf(xx, yy, Z, alpha=estimator_alpha, cmap=cmap)

        # Build a coarser grid to plot a set of ensemble classifications
        # to show how these are different to what we see in the decision
        # surfaces. These points are regularly space and do not have a
        # black outline
        xx_coarser, yy_coarser = np.meshgrid(
            np.arange(x_min, x_max, plot_step_coarser),
            np.arange(y_min, y_max, plot_step_coarser))
        Z_points_coarser = model.predict(np.c_[xx_coarser.ravel(),
                                         yy_coarser.ravel()]
                                         ).reshape(xx_coarser.shape)
        cs_points = plt.scatter(xx_coarser, yy_coarser, s=15,
                                c=Z_points_coarser, cmap=cmap,
                                edgecolors="none")

        # Plot the training points, these are clustered together and have a
        # black outline
        plt.scatter(X[:, 0], X[:, 1], c=y,
                    cmap=ListedColormap(['r', 'y', 'b']),
                    edgecolor='k', s=20)
        plot_idx += 1  # move on to the next plot in sequence

plt.suptitle("Classifiers on feature subsets of the Iris dataset")
plt.axis("tight")

plt.show()

注:主要参考了如下文章:

https://www.cnblogs.com/maybe2030/p/4585705.html

https://blog.csdn.net/qq_24519677/article/details/82117406

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/127297.html原文链接:https://javaforall.cn

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022年4月9,如有侵权请联系 cloudcommunity@tencent.com 删除

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

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档