带你了解集成学习

集成学习(Ensemble Learning)有时也被笼统地称作提升(Boosting)方法,广泛用于分类和回归任务。它最初的思想很简单:使用一些(不同的)方法改变原始训练样本的分布,从而构建多个不同的分类器,并将这些分类器线性组合得到一个更强大的分类器,来做最后的决策。也就是常说的“三个臭皮匠顶个诸葛亮”的想法。

集成学习的理论基础来自于Kearns和Valiant提出的基于PAC(probably approximately correct)的可学习性理论 ,PAC 定义了学习算法的强弱:

弱学习算法:识别错误率小于1/2(即准确率仅比随机猜测略高的算法) 
强学习算法:识别准确率很高并能在多项式时间内完成的算法

根据这两个概念,后来产生了一个重要的结论: 
强可学习与弱可学习是等价的,即:一个概念是强可学习的充要条件是这个概念是弱可学习的

据此,为了得到一个优秀的强学习模型,我们可以将多个简单的弱学习模型“提升”。

对于集成学习,我们面临两个主要问题: 
1.如何改变数据的分布或权重 
2.如何将多个弱分类器组合成一个强分类器

针对上述问题,目前主流方法有三种: 
1.Boosting方法:包括Adaboosting,提升树(代表是GBDT), XGBoost等 
2.Bagging方法:典型的是随机森林 
3.Stacking算法

首先介绍经典的样本估计方法:Bootstrapping 
Bootstrapping的名字来自成语“pull up by your own bootstraps”,意思是依靠自己的资源,称做自助法,是一种有放回的抽样方法。它是非参数统计中一种重要的通过估计统计量方差进而进行区间估计的统计方法,遵从“在不知道数据总体分布时,对总体分布的最好的猜测便是由数据提供的分布”原则。自助法的要点是: 
1.假定观察值就是数据总体 
2.由这一假定的总体抽取样本,即再抽样

由原始数据经过再抽样所获得的与原始数据集含量相等的样本称为再抽样样本或自助样本。由原始数据集计算所得的统计量称为观察统计量,由再抽样样本计算所得的统计量称为自助统计量。自助法的关键在于自助统计量与观察统计量间的关系,等价于观察统计量与真实值之间的关系,即: 
自助统计量:观察统计量 <==> 观察统计量:真值

具体步骤如下: 
1.采用重抽样方式从原始样本中抽取一定数量(容量m)的数据,重复抽样m次,得到一个自助样本。 
2.根据得到的自助样本计算特定的统计量。 
3.重复上述N次,得到N个自助统计量。 
4.根据上述N个自助统计量估计出真实值。 
   
bootstrap实质上是一种再抽样过程,相对于其他方法,在小样本时也具有较好效果。

Boosting

Boosting方法是一种典型的基于bootstrapping思想的应用,其特点是:每一次迭代时训练集的选择与前面各轮的学习结果有关,而且每次是通过更新各个样本权重的方式来改变数据分布。总结起来如下:

1.分步学习每个弱分类器,最终的强分类器由分步产生的分类器组合而成 
2.根据每步学习到的分类器去改变各个样本的权重(被错分的样本权重加大,反之减小)

Adaboosting:

Adaboosting(adaptive boosting)算法由Schapire于1996年提出,是具体实现boosting方法的最典型代表。

算法过程简述为:前一个分类器改变权重w后,成为后一个新的分类器。 
如果一个训练样本在前一个分类器中被误分,那么它的权重会被加大,相反,被正确分类的样本权重会降低

即:AdaBoost算法通过给已有模型预测错误的样本更高的权重,使得之前的分类模型出错的训练样本在后续受到更多关注的方式来弥补已有模型的不足。通过每一次迭代改变训练数据的权值分布,使得数据在每一个基本分类器的学习中起到不同作用,从而使不同数据发挥各自不同的作用,因此不易发生过拟合

相关的训练误差分析表明,每一次迭代后构建的分类器,其分类误差率随着迭代次数增加而稳定下降

详细算法流程如下: 
这里写图片描述 
AdaBoost算法实际上是一种加法模型,损失函数为指数函数,学习算法为前向分步算法。

AdaBoost的主要优点如下: 
1.是一种有高精度的分类器 
2.可以使用各种方法构建子分类器,adaboost算法提供的是一种框架 
3.基本分类器构建简单,易于理解 
4.算法过程简单,不用做特征筛选 
5.不易发生过拟合

GBDT:

GBDT (Gradient Boosting Decision Tree),即梯度提升决策树,又叫又叫 MART(Multiple Additive Regression Tree),是常用的以回归树作为基本分类器、采用梯度提升作为学习算法、迭代生成决策树的一种解决回归问题的提升模型。

通用的提升树算法(boosting tree)是以决策树作为基本模型,利用前向分步算法迭代生成新的数,最后组合决策,可以用不同的损失函数,回归问题常用平方误差损失函数,分类问题常用指数损失函数。 
这里写图片描述 
其中,T表示决策树

每一次迭代过程中,都是在根据当前模型拟合数据的残差(residual),即预测值与真实值的差值,作为新的数据去拟合,来生成新树。

GBDT则是以损失函数的负梯度值作为残差的近似值,来拟合出一个回归树,使每一步的优化变得简单。(相比通用的平方损失和指数损失函数更易优化),相当于每一次建立的树模型是在之前所建树的损失函数的梯度下降方向上,即:每次模型在梯度方向上的减少的部分,可以看作是一个弱的模型。

GBDT在损失函数中引入了树的复杂度作为正则项,树的复杂度定义为:树的叶子节点的个数T和叶子节点的权重的平方之和: 
这里写图片描述
此时,损失函数即目标函数为: 
这里写图片描述
具体算法流程如下: 
这里写图片描述 
2(a)表示计算损失函数的负梯度在当前模型的值,将其作为残差的估计值 
2(b)表示估计回归树叶子节点的区域,以拟合残差的近似值 
2(c)表示利用线性搜索估计叶子节点区域的值,使得损失函数最小化 
2(d)更新回归树

解释: 
1.初始化,估计使损失函数极小化的常数值,它是只有一个根节点的树,即ganma是一个常数值。 
2.(a)计算损失函数的负梯度在当前模型的值,将它作为残差的估计 
(b)估计回归树叶节点区域,以拟合残差的近似值 
(c)利用线性搜索估计叶节点区域的值,使损失函数极小化 
(d)更新回归树 
3.得到输出的最终模型 f(x)

总结如下: 
1. 算法每次迭代生成一颗新的决策树 
2. 计算损失函数对每个样本的一阶导和二阶导 
3. 通过贪心算法生成新的决策树,同时计算每个叶子节点的权重w 
4. 把新生成的决策树f(x)添加到模型中

GBDT正则化方法: 
1.梯度更新时加入正则化项:即学习率(或叫做步长)([0,1])

2.通过子采样方法每次选取一部分数据拟合:设定一个子采样比例,取值为(0,1]。(这里的子采样不同于随机森林,随机森林使用的是有放回抽样,这里是不放回抽样)如果取值为1,则全部样本都使用,等于没有使用子采样。如果取值小于1,则只有一部分样本会去做GBDT的决策树拟合。

选择小于1的比例可以减少方差,即防止过拟合,但是会增加样本拟合的偏差,因此取值不能太低。推荐在[0.5, 0.8]之间。

使用了无放回子采样的GBDT有时也称作随机梯度提升树(Stochastic Gradient Boosting Tree, SGBT)。由于使用了子采样,程序可以通过采样分发到不同的任务去做boosting的迭代过程,最后形成新树,从而减少弱学习器难以并行学习的弱点,可以在不同分类器层面实现数据的并行训练。(由于对全体数据存在一个随机抽样过程,因此可以并行构建多个不同的梯度提升树,但对于每一个提升树仍然是“串行”生成)

3.剪枝:类似决策树剪枝,在生成一颗树后,最小化损失函数(含有误差项和正则项:树复杂度)来实现剪枝

GBDT主要的优点有: 
1.可灵活处理各种类型的数据,包括连续值和离散值 
2.在相对少的调参时间情况下,预测的准准确率较高。(相对于SVM) 
3.若使用一些健壮的损失函数,使得算法对异常值的鲁棒性非常强。比如 Huber损失函数和Quantile损失函数

GBDT的主要缺点有: 
由于弱学习器之间存在依赖关系,难以并行训练数据。(但可以通过子采样的SGBT实现部分并行训练)

目前GBDT的算法比较好的库是xgboost和scikit-learn。

Bagging

Bagging (bootstrap aggregating) 方法完全基于bootstrapping思想,即把训练集看做全体数据的子集,训练集对全体数据中某参数的估计等价于用训练集子采样获得的数据来估计训练集。其中,每一次迭代前,采用有放回的随机抽样来获取训练数据。这样做直接体现了bagging的一大特点每次迭代不依赖之前建立的模型,即生成的各个弱模型之间没有关联,因此就可以彻底实现数据并行训练。这是bagging方法与上述boosting方法最大的区别。

对于一个样本,在一次含m个样本的训练集的随机采样,每次被抽到的概率是1/m。不被抽到的概率为1−1/m。如果m次采样都没有被抽到的概率是(1−1/m)mm。当m→∞时,(1−1/m)mm→1e≃0.368。就是说,在bagging的每轮随机采样中,训练集中大约有36.8%的数据没有被采样集抽到。这大约36.8%的没有被采样到的数据,我们常常称之为袋外数据(Out Of Bag, OOB)。这些数据没有参与训练集模型的拟合,因此可作为验证集检验模型的效果。

和boosting方法一样,bagging方法常用的基本模型也是决策树和神经网络,而且基本模型越简单,往往最终的集成模型泛化能力越强,越不易发生过拟合。模型组合的方法也很简单,对于分类任务,一般采用多数投票;对于回归任务,则采用算术平均法。

boosting和bagging的区别: 
对于Bagging算法,并行训练多个不同分类器的目的主要是降低方差,采用了相互独立的基本分类器后,模型之间不存在依赖关系,性能较为平均,因此对每个基本分类器来说,目标是如何降低偏差,因此通常会采用深度很深而且不剪枝的决策树。

对于Boosting算法,每一次迭代都是在上一轮的基础上拟合原数据,可以保证偏差持续降低,因此对于每一个基本分类器来说,目标是如何选择方差较小的分类器,也就是选择更简单的分类器,因而通常选择深度很浅的决策树。反之,如果基本模型较复杂,则方差相对较大,这将导致整体模型的方差很大,从而很容易产生过拟合。因此,boosting框架中的基本模型必须为弱模型

总结如下: 
Boosting利用前一轮迭代的误差率(或残差)进行拟合训练(相当于改变原始样本的权重),每轮的训练集分布依赖之前的训练结果,因而每轮模型只能顺序生成。主要关注降低偏差,因此Boosting能基于泛化性能很弱的分类器构建出很强的集成模型

Bagging采取对原始样本有放回的均匀抽样方式获取每一次的数据来训练,每轮训练集相互独立,因而模型可以并行生成。主要关注降低方差,因此它在不剪枝的决策树、神经网络等模型上效果更好

boosting方法的拟合效果(准确度)更高,bagging方法更不易发生过拟合

随机森林:

随机森林(Random Forest)算法由上世纪八十年代Breiman等人提出来,基本思想就是构造多棵相互独立的CART决策树,形成一个森林,利用这些决策树共同决策输出类别。随机森林算法秉承了bagging方法的思想,以构建单一决策树为基础,同时也是单一决策树算法的延伸和改进。

在整个随机森林算法的过程中,有两个随机过程

1.输入数据是随机的:从全体训练数据中选取一部分来构建一棵决策树,并且是有放回的选取 
2.每棵决策树的构建所需的特征是从全体特征中随机选取的

这两个随机过程使得随机森林在很大程度上避免了过拟合的出现。

具体来说:对于普通的决策树,一般会在节点上对所有的n个样本特征中选择一个最优的特征来做决策树左右子树的划分,但随机森林通过随机选择节点上的一部分样本特征:nsubnsub个,然后在这些随机选择的特征中,选择一个最优的特征来做决策树的左右子树划分,这两个随机性使得基本模型之间的相关性显著降低,方差下降,从而进一步增强了模型的泛化能力。 

如果nsub=nnsub=n,则此时随机森林和普通的CART决策树相同。nsubnsub越小,模型越健壮,然而对于训练集的拟合程度会变差。即nsubnsub越小,模型方差越小,但偏差越大。因此,通常会利用交叉验证调参来获取一个合适的nsubnsub值。

随机森林算法具体流程: 
1.从原始训练数据中随机选取n个数据作为训练数据输入(通常n远小于全体训练数据N,这样就存在一部分“袋外数据”始终不被取到,它们可以直接用于测试误差(而无需单独的测试集或验证集)。 
2.选取了要输入的训练数据后,开始构建决策树,具体方法是每一个结点从全体特征集M中选取m个特征进行构建(通常m远小于M)。 
3.在构造每棵决策树时,选取基尼指数最小的特征来分裂节点构建决策树。决策树的其他结点都采取相同的分裂规则进行构建,直到该节点的所有训练样例都属于同一类或者达到树的最大深度。(该步骤与构建单一决策树相同) 
4.重复第2步和第3步多次,每一次输入数据对应一颗决策树,这样就得到了一个随机森林,用于对预测数据进行决策。 
5.对待预测数据进行预测时,多棵决策树同时进行决策,采用多数投票的方式产生最终决策结果。

随机森林算法的注意点: 
1.在构建决策树的过程中不需要剪枝 
2.整个森林中树的数量和每棵树的特征需要人为设定 
3.构建决策树的时候分裂节点的选择依据最小基尼系数 
4.在训练时,对于预选变量个数和随机森林中树的个数这两个参数的调优很重要。

随机森林的优点: 
1.两个随机性的引入,使得随机森林抗噪声能力强,方差小泛化能力强,不易陷入过拟合(因此不需要剪枝) 
2.易于高度并行训练,且训练速度快,适合处理大数据 
3.由于可以随机选择决策树节点划分的特征,因此能够处理高维度(特征多)的数据,事先不用做特征选择;而且连续和离散数据均可,且无需归一化数据。 
4.创建随机森林时,是对参数的无偏估计 
5.训练后,可以给出各个特征对于输出结果的重要性排序,并且能够检测到特征间的相互影响 
6.相对于boosting系列算法,实现简单 
7.对部分特征缺失不敏感 
8.由于随机森林对误差率是无偏估计,因此在算法中不需要再进行交叉验证或者设置单独的测试集来获取测试集上误差的无偏估计

随机森林的缺点: 
对于取值划分比较多的特征,容易对随机森林的决策产生较大影响,从而影响拟合效果。

Stacking

stacking是一种组合多个模型的方法,它主要注重分类器的组合,相对于bagging和boosting使用较少,具体过程如下:

1.划分训练数据集为两个不相交的集合。 
2.在第一个集合上训练多个学习器。 
3.在第二个集合上测试这几个学习器 
4.将第三步得到的预测结果作为输入,将正确的映射作为输出,训练一个更高层的分类器

由此可见,对于模型组合方式,不同于前面两个方法采用多数投票或算术平均的线性组合策略,stacking采用的是基本模型非线性组合的方式。

最后总结一点:bagging和stacking中的基本模型须为强模型(低偏差高方差),boosting中的基本模型为弱模型(低方差高偏差)。

以下讲讲几种常用的集成学习算法

GBDT和xgboost在竞赛和工业界使用都非常频繁,能有效的应用到分类、回归、排序问题,虽然使用起来不难,但是要能完整的理解还是有一点麻烦的。本文尝试一步一步梳理GB、GBDT、xgboost,它们之间有非常紧密的联系,GBDT是以决策树(CART)为基学习器的GB算法,xgboost扩展和改进了GDBT,xgboost算法更快,准确率也相对高一些。    

1. Gradient boosting(GB)

      机器学习中的学习算法的目标是为了优化或者说最小化loss Function, Gradient boosting的思想是迭代生多个(M个)弱的模型,然后将每个弱模型的预测结果相加,后面的模型Fm+1(x)基于前面学习模型的Fm(x)的效果生成的,关系如下:

1 \le m \le M     F_{m+1}(x) = F_m(x) + h(x)

      GB算法的思想很简单,关键是怎么生成h(x)?

      如果目标函数是回归问题的均方误差,很容易想到最理想的h(x)应该是能够完全拟合y - F_m(x) ,这就是常说基于残差的学习。残差学习在回归问题中可以很好的使用,但是为了一般性(分类,排序问题),实际中往往是基于loss Function 在函数空间的的负梯度学习,对于回归问题\frac{1}{2}(y - F(x))^2残差和负梯度也是相同的。L(y, f)中的f,不要理解为传统意义上的函数,而是一个函数向量\! f(x_1), \ldots, f(x_n),向量中元素的个数与训练样本的个数相同,因此基于Loss Function函数空间的负梯度的学习也称为“伪残差”。

GB算法的步骤:

  1.初始化模型为常数值:

    F_0(x) = \underset{\gamma}{\arg\min} \sum_{i=1}^n L(y_i, \gamma).

  2.迭代生成M个基学习器

    1.计算伪残差

      r_{im} = -\left[\frac{\partial L(y_i, F(x_i))}{\partial F(x_i)}\right]_{F(x)=F_{m-1}(x)} \quad \mbox{for } i=1,\ldots,n.

    2.基于\{(x_i, r_{im})\}_{i=1}^n生成基学习器\! h_m(x)

    3.计算最优的\! \gamma_m

      \gamma_m = \underset{\gamma}{\operatorname{arg\,min}} \sum_{i=1}^n L\left(y_i, F_{m-1}(x_i) + \gamma h_m(x_i)\right).

        4.更新模型

      F_m(x) = F_{m-1}(x) + \gamma_m h_m(x).

 2. Gradient boosting Decision Tree(GBDT)

  GB算法中最典型的基学习器是决策树,尤其是CART,正如名字的含义,GBDT是GB和DT的结合。要注意的是这里的决策树是回归树,GBDT中的决策树是个弱模型,深度较小一般不会超过5,叶子节点的数量也不会超过10,对于生成的每棵决策树乘上比较小的缩减系数(学习率<0.1),有些GBDT的实现加入了随机抽样(subsample 0.5<=f <=0.8)提高模型的泛化能力。通过交叉验证的方法选择最优的参数。因此GBDT实际的核心问题变成怎么基于\{(x_i, r_{im})\}_{i=1}^n使用CART回归树生成\! h_m(x)

  CART分类树在很多书籍和资料中介绍比较多,但是再次强调GDBT中使用的是回归树。作为对比,先说分类树,我们知道CART是二叉树,CART分类树在每次分枝时,是穷举每一个feature的每一个阈值,根据GINI系数找到使不纯性降低最大的的feature以及其阀值,然后按照feature<=阈值,和feature>阈值分成的两个分枝,每个分支包含符合分支条件的样本。用同样方法继续分枝直到该分支下的所有样本都属于统一类别,或达到预设的终止条件,若最终叶子节点中的类别不唯一,则以多数人的类别作为该叶子节点的性别。回归树总体流程也是类似,不过在每个节点(不一定是叶子节点)都会得一个预测值,以年龄为例,该预测值等于属于这个节点的所有人年龄的平均值。分枝时穷举每一个feature的每个阈值找最好的分割点,但衡量最好的标准不再是GINI系数,而是最小化均方差--即(每个人的年龄-预测年龄)^2 的总和 / N,或者说是每个人的预测误差平方和 除以 N。这很好理解,被预测出错的人数越多,错的越离谱,均方差就越大,通过最小化均方差能够找到最靠谱的分枝依据。分枝直到每个叶子节点上人的年龄都唯一(这太难了)或者达到预设的终止条件(如叶子个数上限),若最终叶子节点上人的年龄不唯一,则以该节点上所有人的平均年龄做为该叶子节点的预测年龄。

3. Xgboost

  Xgboost是GB算法的高效实现,xgboost中的基学习器除了可以是CART(gbtree)也可以是线性分类器(gblinear)。下面所有的内容来自原始paper,包括公式。

  (1). xgboost在目标函数中显示的加上了正则化项,基学习为CART时,正则化项与树的叶子节点的数量T和叶子节点的值有关。

  (2). GB中使用Loss Function对f(x)的一阶导数计算出伪残差用于学习生成fm(x),xgboost不仅使用到了一阶导数,还使用二阶导数。

    第t次的loss:

    对上式做二阶泰勒展开:g为一阶导数,h为二阶导数

  (3). 上面提到CART回归树中寻找最佳分割点的衡量标准是最小化均方差,xgboost寻找分割点的标准是最大化,lamda,gama与正则化项相关

   xgboost算法的步骤和GB基本相同,都是首先初始化为一个常数,gb是根据一阶导数ri,xgboost是根据一阶导数gi和二阶导数hi,迭代生成基学习器,相加更新学习器。

xgboost与gdbt除了上述三点的不同,xgboost在实现时还做了许多优化

  • 在寻找最佳分割点时,考虑传统的枚举每个特征的所有可能分割点的贪心法效率太低,xgboost实现了一种近似的算法。大致的思想是根据百分位法列举几个可能成为分割点的候选者,然后从候选者中根据上面求分割点的公式计算找出最佳的分割点。
  • xgboost考虑了训练数据为稀疏值的情况,可以为缺失值或者指定的值指定分支的默认方向,这能大大提升算法的效率,paper提到50倍。
  • 特征列排序后以块的形式存储在内存中,在迭代中可以重复使用;虽然boosting算法迭代必须串行,但是在处理每个特征列时可以做到并行。
  • 按照特征列方式存储能优化寻找最佳的分割点,但是当以行计算梯度数据时会导致内存的不连续访问,严重时会导致cache miss,降低算法效率。paper中提到,可先将数据收集到线程内部的buffer,然后再计算,提高算法的效率。
  • xgboost 还考虑了当数据量比较大,内存不够时怎么有效的使用磁盘,主要是结合多线程、数据压缩、分片的方法,尽可能的提高算法的效率。

猜你喜欢

转载自blog.csdn.net/qq_41853758/article/details/82668971