如何重现H2o GBM类概率计算

时间:2017-06-24 10:43:07

标签: r classification probability h2o gbm

我一直在使用h2o.gbm来解决分类问题,并希望更多地了解它如何计算类概率。作为一个起点,我试图用一棵树重新计算一个gbm的类概率(通过查看叶子中的观察结果),但结果非常令人困惑。

让我们假设我的积极类变量是"购买"和负类变量" not_buy"我有一套名为" dt.train"以及一个名为" dt.test"。

的单独测试集

在正常决策树中,"购买"的等级概率对于新数据行(测试数据)的P(has_bought =" buy")是通过将叶子中的所有观察值除以"购买"来计算的。通过叶子中的观察总数(基于用于种植树木的训练数据)。

然而,h2o.gbm似乎做了不同的事情,即使我模拟了一个正常的'决策树(将n.trees设置为1,将alle.rate设置为1)。我认为说明这种困惑的最好方法是通过逐步的方式告诉我做了什么。

第1步:培训模型

我不关心过度拟合或模型性能。我想让我的生活变得尽可能简单,所以我将n.trees设置为1,并确保通过设置所有样本,为每棵树使用所有训练数据(行和列)并进行拆分。速率参数为1.以下是训练模型的代码。

    base.gbm.model <- h2o.gbm(
      x = predictors,
      y = "has_bought",
      training_frame = dt.train,
      model_id = "2",
      nfolds = 0,
      ntrees = 1,
      learn_rate = 0.001,
      max_depth = 15,
      sample_rate = 1,
      col_sample_rate = 1,
      col_sample_rate_per_tree = 1,
      seed = 123456,
      keep_cross_validation_predictions = TRUE,
      stopping_rounds = 10,
      stopping_tolerance = 0,
      stopping_metric = "AUC",
      score_tree_interval = 0
    )

第2步:获取训练集的叶子分配

我想要做的是使用用于训练模型的相同数据,并了解他们最终进入的叶子.H2.o为此提供了一个功能,如下所示。

    train.leafs <- h2o.predict_leaf_node_assignment(base.gbm.model, dt.train)

这将返回训练数据中每一行的叶节点分配(例如&#34; LLRRLL&#34;)。由于我们只有一棵树,因此该列被称为&#34; T1.C1&#34;我将其重命名为&#34; leaf_node&#34;,我用目标变量&#34; has_bought&#34;的训练数据。这导致下面的输出(从这里开始称为&#34; train.leafs&#34;)。

table "train.leafs"

第3步:对测试集进行预测

对于测试集,我想预测两件事:

  1. 模型本身的预测P(has_bought =&#34; buy&#34;)
  2. 根据模型分配叶节点。

    test.leafs <- h2o.predict_leaf_node_assignment(base.gbm.model, dt.test)
    test.pred <- h2o.predict(base.gbm.model, dt.test)
    
  3. 在找到这个之后,我使用了cbind将这两个预测与测试集的目标变量结合起来。

        test.total <- h2o.cbind(dt.test[, c("has_bought")], test.pred, test.leafs)
    

    结果如下表所示,从这里开始称为&#34; test.total&#34;

      

    不幸的是,我没有足够的重复点来发布2个以上的链接。但是如果你点击&#34; table&#34; test.total&#34;结合手册   概率计算&#34;在第5步中,它基本上是同一个表   没有列&#34; manual_prob_buy&#34;。

    第4步:手动预测概率

    理论上,我应该能够自己预测概率。我这样做是通过编写一个循环,循环遍历&#34; test.total&#34;中的每一行。对于每一行,我都会进行叶节点分配。

    然后我使用该叶节点分配来过滤表&#34; train.leafs&#34;,并检查有多少观察具有正类(has_bought == 1)(posN)以及有多少观测值与测试行相关的叶子中的总数(totalN)。

    我执行(标准)计算posN / totalN,并将其作为一个名为&#34; manual_prob_buy&#34;的新列存储在测试行中,该列应该是P的概率(has_bought =&#34;购买&#34;)为那片叶子。因此,落在这片叶子中的每个测试行应该得到这个概率。 这个for循环如下所示。

        for(i in 1:nrow(dt.test)){
          leaf <-  test.total[i, leaf_node] 
          totalN <- nrow(train.leafs[train.leafs$leaf_node == leaf])
          posN <- nrow(train.leafs[train.leafs$leaf_node == leaf & train.leafs$has_bought == "buy",])
          test.total[i, manual_prob_buy :=  posN / totalN]
        }
    

    第5步:比较概率

    这是我感到困惑的地方。以下是更新的&#34; test.total&#34;表,其中&#34;购买&#34;根据模型和&#34; manual_prob_buy&#34;表示概率P(has_bought =&#34; buy&#34;)。表示从步骤4手动计算的概率。据我所知,这些概率应该相同,因为我知道我只使用了1棵树,并且我已将sample.rates设置为1。

    表&#34; test.total&#34;结合手动概率计算 table "test.total" combined with manual probability calculation

    问题

    我只是不明白为什么这两个概率不一样。据我所知,我已经设置了参数,它应该像一个普通的&#39;分类树。

    所以问题:有谁知道为什么我发现这些概率存在差异?

    我希望有人可以指出我可能做出错误假设的地方。我真的希望我做了一些愚蠢的事情,因为这让我发疯了。

    谢谢!

2 个答案:

答案 0 :(得分:0)

我不建议将R&lt; h2o.predict()的结果与您自己的手写代码进行比较,而是建议您与H2O MOJO进行比较,它应匹配。

请参阅此处的示例:

http://docs.h2o.ai/h2o/latest-stable/h2o-genmodel/javadoc/overview-summary.html#quickstartmojo

您可以自己运行该简单示例,然后根据您自己的模型和新数据行进行修改以进行预测。

一旦你能做到这一点,你可以查看代码并在java环境中调试/单步执行,以确切了解预测的计算方式。

您可以在github上找到MOJO预测代码:

https://github.com/h2oai/h2o-3/blob/master/h2o-genmodel/src/main/java/hex/genmodel/easy/EasyPredictModelWrapper.java

答案 1 :(得分:0)

观察到的概率与h2o的预测之间巨大差异的主要原因是学习率。当您拥有learn_rate = 0.001时,gbm会将概率从总速率中调整为相对较小的量。如果将其调整为learn_rate = 1,您将更接近决策树,并且h2o的预测概率将更接近每个叶节点的速率。

由于您的概率仍然不完全匹配,因此会出现次要差异。这是由于使用了Logistic损失函数的梯度下降方法(GBM中的G),而不是每个叶节点中观察到的数目。