首页 > 解决方案 > PySpark RandomForest 实现中的 rawPrediction 是如何计算的?

问题描述

我已经在 15 个示例的训练集上训练了一个 RF 模型(有 3 棵树,深度为 4)。下面是三棵树的外观图像。我有两个班级(比如 0 和 1)。

三棵决策树

左侧分支中提到了阈值,而圆圈中的数字(例如 7、3 是特征 2 即 f2 的 <= 阈值和 > 阈值的示例数)。

现在,当我尝试将模型应用于 10 个示例的测试集时,我不确定原始预测是如何计算的。

+-----+----+----+----------+-------------------------------------------------------------------
|prediction|features                                                                                                                                                                                                                                                                                                   |rawPrediction|probability                            |
+-----+----+----+----------+-----------------------------------------------------------------------------------------------------------+-------------+---------------------------------------+
|1.0       |[0.07707524933080619,0.03383458646616541,0.017208413001912046,9.0,2.5768015000258258,0.0,-1.0,-1.0,0.0,-1.0,-1.0,-1.0,-1.0,-1.0,0.0014143059186559938,0.0,0.6666666666666667,7.076533785087878E-4,0.0014163090128755495,0.9354143466934853,0.9333333333333333,0.875,0.938888892531395,7.0]                 |[1.0,2.0]    |[0.3333333333333333,0.6666666666666666]|

我已经通过以下链接进行了了解,但我无法理解这一点。

https://forums.databricks.com/questions/14355/how-does-randomforestclassifier-compute-the-rawpre.html

https://github.com/apache/spark/blob/master/mllib/src/main/scala/org/apache/spark/ml/classification/RandomForestClassifier.scala

我确信这并不像你想象的那么简单。例如,根据我的理解,它不像 - 例如,如果两棵树预测为 0,而一棵树预测为 1,那么原始预测将是 [2, 1]。情况并非如此,因为当我在 500 个示例上训练模型时,我看到同一示例的原始预测为 [0.9552544653780279,2.0447455346219723]。

有人可以向我解释一下这是如何在数学上计算的吗?任何帮助都会在这里受到赞赏,因为它有点基本,我想直接了解它是如何工作的。再次提前非常感谢,如果需要任何其他信息来帮助解决此问题,请发布。

编辑: 也从模型中添加数据:

+------+-----------------------------------------------------------------------------------------------------+
|treeID|nodeData                                                                                             |
+------+-----------------------------------------------------------------------------------------------------+
|0     |[0, 0.0, 0.5, [9.0, 9.0], 0.19230769230769235, 1, 4, [2, [0.12519961673586713], -1]]                 |
|0     |[1, 0.0, 0.42603550295857984, [9.0, 4.0], 0.42603550295857984, 2, 3, [20, [0.39610389610389607], -1]]|
|0     |[2, 0.0, 0.0, [9.0, 0.0], -1.0, -1, -1, [-1, [], -1]]                                                |
|0     |[3, 1.0, 0.0, [0.0, 4.0], -1.0, -1, -1, [-1, [], -1]]                                                |
|0     |[4, 1.0, 0.0, [0.0, 5.0], -1.0, -1, -1, [-1, [], -1]]                                                |
|1     |[0, 1.0, 0.4444444444444444, [5.0, 10.0], 0.4444444444444444, 1, 2, [4, [0.9789660448762616], -1]]   |
|1     |[1, 1.0, 0.0, [0.0, 10.0], -1.0, -1, -1, [-1, [], -1]]                                               |
|1     |[2, 0.0, 0.0, [5.0, 0.0], -1.0, -1, -1, [-1, [], -1]]                                                |
|2     |[0, 0.0, 0.48, [3.0, 2.0], 0.48, 1, 2, [20, [0.3246753246753247], -1]]                               |
|2     |[1, 0.0, 0.0, [3.0, 0.0], -1.0, -1, -1, [-1, [], -1]]                                                |
|2     |[2, 1.0, 0.0, [0.0, 2.0], -1.0, -1, -1, [-1, [], -1]]                                                |
+------+-----------------------------------------------------------------------------------------------------+

标签: apache-sparkpysparkclassificationrandom-forestapache-spark-mllib

解决方案


原始预测是每棵树的预测类别概率,对森林中的所有树求和。对于单个树的类概率,在所选叶节点中属于每个类的样本数很重要。

在代码中,我们可以在此处的RandomForestClassifier类中看到此过程,此处引用了相关代码:

override protected def predictRaw(features: Vector): Vector = {
  // TODO: When we add a generic Bagging class, handle transform there: SPARK-7128
  // Classifies using majority votes.
  // Ignore the tree weights since all are 1.0 for now.
  val votes = Array.fill[Double](numClasses)(0.0)
  _trees.view.foreach { tree =>
    val classCounts: Array[Double] = tree.rootNode.predictImpl(features).impurityStats.stats
    val total = classCounts.sum
    if (total != 0) {
      var i = 0
      while (i < numClasses) {
        votes(i) += classCounts(i) / total
        i += 1
      }
    }
  }
  Vectors.dense(votes)
}

对于每棵树,我们找到输入特征对应的叶子节点,并找到每个类的计数(类计数对应于训练期间分配给叶子节点的类的训练样本数)。类计数除以节点的总计数以获得类概率。

现在,对于每棵树,我们都有属于每个类的输入特征的概率。这些概率相加得到原始预测。


对于这个问题更具体,据我了解,主要缺失的元素是类数(以及类概率)。要计算原始预测,这些是必不可少的组成部分。在图像中,您需要在训练期间添加分配给每个叶子的样本数,而不是“Pred 0”和“Pred 1”(“Pred 0”意味着来自类 0 的样本数是多数,反之亦然)。当你知道类数和类概率时,将所有树的这些相加,你就会得到原始预测。


推荐阅读