随机森林&GBDT算法以及在MLlib中的实现

决策树的一个缺点是容易出现过拟合,可以把利用融合的方式把各个弱模型集成起来,解决过拟合,提高模型的泛化能力。决策树和bagging 、boosting的思想结合在一起,诸如随机森林、GBDT,在数据挖掘中的预测分类、推荐广告以及搜索中的排序算法模型、搜索关键词的扩展推荐等等应用的非常广泛。

先讲一下bagging和boosting方式的区别。

Bagging的方式算是比较简单的,训练多个模型,利用每个模型进行投票,每个模型的权重都一样,对于分类问题,取总票数最多作为分类,对于回归,取平均值。利用多个弱分类器,集成一个性能高的分类器。典型代表是随机森林。随机森林在训练每个模型的时,增加随机的因素,对特征和样本进行随机抽样,然后把各颗树训练的结果集成融合起来。随机森林可以进行并行训练多颗树。

Boosting的方式也是训练多个决策树模型,是一种迭代的算法模型,在训练过程中更加关注错分的样本,对于越是容易错分的样本,后续的模型训练约要花更多精力去关注,提高上一次分错的数据权重,越在意那些分错的数据。在集成融合时,每次训练的模型权重也会不一样,最终通过加权的方式融合成最终的模型。Adaboost、GBDT采用的都是boosting的思想。

用一张图来看一下Adaboost方法。


训练过程如下:

1、             初始时,样本的训练权重都是一样,通过一个弱分类器,得到这些样本的分类预测标签。与给出的样本真实标签对比,就可能出现误差(即错误)。如果某个样本预测错误,则它对应的错误值为该样本的权重,如果分类正确,则错误值为0. 最后累加5个样本的错误率之和,记为ε。

2、             通过ε来计算该弱分类器的权重α,公式如下:


3、             通过α来计算训练下一个弱分类器样本的权重D,如果对应样本分类正确,则减小该样本的权重,公式为:


如果样本分类错误,则增加该样本的权重,公式为:


4、             循环步骤1,2,3来继续训练多个分类器,只是其D值不同而已。

预测过程:

输入一个样本到训练好的每个弱分类中,则每个弱分类都对应一个输出标签,然后该标签乘以对应的α,最后求和得到值的符号即为预测标签值。

     还有一种常用的boosting是Gradient Boosting,GBDT,它主要的思想是,每一次建立模型是在之前建立模型损失函数的梯度下降方向。损失函数(loss function)描述的是模型的不靠谱程度,损失函数越大,则说明模型越容易出错(其实这里有一个方差、偏差均衡的问题,但是这里就假设损失函数越大,模型越容易出错)。如果我们的模型能够让损失函数持续的下降,则说明我们的模型在不停的改进,而最好的方式就是让损失函数在其梯度(Gradient)的方向上下降。算法的每一步沿着损失函数下降最快的方向建立新的模型,这样使得算法在每一步均沿着下降最快的方向收敛。直到满足要求,建立满足要求的若干组合加权子模型GradientBoosting,定义loss function


则对于训练样本集合{y, x}我们的任务是寻找最小化loss的函数F*(x)


gradient boosting的思路是将映射模型函数表示为以下形式:


其中h(x;am)为简单函数/模型, am h的参数,此时, belta, a就为我们要预估的最小化loss下的参数:


同时FmF_m-1的关系为


之后可以求beltaa序列参数,求解过程如下:

在第i个样本点,m个模型里边的伪残差求解方法为:


要构建模型h(x,am)最快的方法,就是让所有的样本点处,损失函数都沿着最快的方向下降。

也就是:


利用最小二乘法求解am后,即可求解belta_m


依次求解所有am, belta_m后,即得到最终模型F*(x)

算法的流程如下:


         MLlib中的随机森林RandomForest可以做到每棵树并行的训练,GBDT由于是迭代的算法,无法实现多棵树的并行训练。但是每颗树的训练都是基于DesionTree的实现,所以支持在单棵树级别实现并行的操作,这个在决策树的并行训练提到过。

         MLlib中的RandomForest实现其实就是决策树的实现,只是在最后进行最后模型预测时,加权融合多个模型,比较简单。支持分类和回归支持,分类只支持二元,对于多标签的分类,MLlib中的GBDT目前还不支持。

对于GBDT,在MLlib中实现的算法在上面基本已经进行了介绍,相关的模型训练过程可以参考下面的代码注解。

GradientBoostedTrees 
 private def boost(
      input: RDD[LabeledPoint],
      boostingStrategy: BoostingStrategy): GradientBoostedTreesModel = {

    val timer = new TimeTracker()
    timer.start("total")
    timer.start("init")

    boostingStrategy.assertValid()

// Initialize gradient boosting parameters
//迭代次数
val numIterations = boostingStrategy.numIterations
//每次迭代的决策树模型
    val baseLearners = new Array[DecisionTreeModel](numIterations)
   //每个树模型的权重 
val baseLearnerWeights = new Array[Double](numIterations)
   //损失的计算方式,参考下表
val loss = boostingStrategy.loss
   //学习率,一般不建议对这个参数进行调优,如果算法模型不稳定,则建议降低这个值
  val learningRate = boostingStrategy.learningRate
    // Prepare strategy for individual trees, which use regression with variance impurity.
val treeStrategy = boostingStrategy.treeStrategy.copy
//algo支持classication和Regresion
    treeStrategy.algo = Regression
    treeStrategy.impurity = Variance
    treeStrategy.assertValid()

    // Cache input,缓存样本集合
    if (input.getStorageLevel == StorageLevel.NONE) {
      input.persist(StorageLevel.MEMORY_AND_DISK)
    }

    timer.stop("init")

    logDebug("##########")
    logDebug("Building tree 0")
    logDebug("##########")
    var data = input

    // Initialize tree
    timer.start("building tree 0")
    val firstTreeModel = new DecisionTree(treeStrategy).run(data)
    baseLearners(0) = firstTreeModel
    baseLearnerWeights(0) = 1.0
    val startingModel = new GradientBoostedTreesModel(Regression, Array(firstTreeModel), Array(1.0))
    logDebug("error of gbt = " + loss.computeError(startingModel, input))
    // Note: A model of type regression is used since we require raw prediction
    timer.stop("building tree 0")

    // psuedo-residual for second iteration
    data = input.map(point => LabeledPoint(loss.gradient(startingModel, point),
      point.features))

var m = 1
//迭代训练各树模型
    while (m < numIterations) {
      timer.start(s"building tree $m")
      logDebug("###################################################")
      logDebug("Gradient boosting tree iteration " + m)
      logDebug("###################################################")
      val model = new DecisionTree(treeStrategy).run(data)
      timer.stop(s"building tree $m")
      // Create partial model
      baseLearners(m) = model
      // Note: The setting of baseLearnerWeights is incorrect for losses other than SquaredError.
      //       Technically, the weight should be optimized for the particular loss.
      //       However, the behavior should be reasonable, though not optimal.
//当前数模型的权重weight     
 baseLearnerWeights(m) = learningRate
      // Note: A model of type regression is used since we require raw prediction
   //每个树的模型   
val partialModel = new GradientBoostedTreesModel(
        Regression, baseLearners.slice(0, m + 1), baseLearnerWeights.slice(0, m + 1))
      logDebug("error of gbt = " + loss.computeError(partialModel, input))

//利用残差(梯度方向)更新样本数据集,作为下颗树模型训练的样本
      data = input.map(point => LabeledPoint(-loss.gradient(partialModel, point),
        point.features))
      m += 1
    }

    timer.stop("total")

    logInfo("Internal timing for DecisionTree:")
    logInfo(s"$timer")
//最后融合各颗树的模型
    new GradientBoostedTreesModel(
      boostingStrategy.treeStrategy.algo, baseLearners, baseLearnerWeights)
  }


附GBDT支持的LOSS:
Loss Task Formula Description
Log Loss Classification 2 \sum_{i=1}^{N} \log(1+\exp(-2 y_i F(x_i))) Twice binomial negative log likelihood.
Squared Error Regression \sum_{i=1}^{N} (y_i - F(x_i))^2 Also called L2 loss. Default loss for regression tasks.
Absolute Error Regression \sum_{i=1}^{N} |y_i - F(x_i)| Also called L1 loss. Can be more robust to outliers than Squared Error.


你可能感兴趣的:(spark,机器学习(广告,推荐,数据挖掘))