rxgboosttweedie

using xgboost to model a Tweedie regression


I'm trying to use xgboost to make a tweedie model, however I get an obscure error message.

Here is a reproducible example:

Preparing the data:

library(xgboost)
library(dplyr)

set.seed(123)
xx <- rpois(5000, 0.02)
xx[xx>0] <- rgamma(sum(xx>0), 50)

yy <- matrix(rnorm(15000), 5000,3, dimnames = list(1:5000, c("a", "b", "c")))

train_test <- sample(c(0,1), 5000, replace = T)

Preparing the xgboost, the important thing here is : objective = 'reg:tweedie', eval_metric = "tweedie-nloglik" and tweedie_variance_power = 1.2 :

dtrain <- xgb.DMatrix(
  data = yy %>% subset(train_test == 0),
  label = xx %>% subset(train_test == 0)
)

dtest <- xgb.DMatrix(
  data = yy %>% subset(train_test == 1),
  label = xx %>% subset(train_test == 1)
)

watchlist <- list(eval = dtest, train = dtrain)

param <- list(max.depth = 2,
              eta = 0.3,
              nthread = 1,
              silent = 1,
              objective = 'reg:tweedie',
              eval_metric = "tweedie-nloglik",
              tweedie_variance_power = 1.2)

And finally calling the xgboost:

resBoost <- xgb.train(params = param, data=dtrain, nrounds = 20, watchlist=watchlist)

which gives this obscure error message:

Error in xgb.iter.update(bst$handle, dtrain, iteration - 1, obj) :
  [17:59:18] amalgamation/../src/metric/elementwise_metric.cc:168: Check failed: param != nullptr tweedie-nloglik must be in formattweedie-nloglik@rho

Stack trace returned 10 entries:
[bt] (0) /usr/local/lib/R/site-library/xgboost/libs/xgboost.so(dmlc::StackTrace[abi:cxx11]()+0x1bc) [0x7f1f0ce742ac]
[bt] (1) /usr/local/lib/R/site-library/xgboost/libs/xgboost.so(dmlc::LogMessageFatal::~LogMessageFatal()+0x28) [0x7f1f0ce74e88]
[bt] (2) /usr/local/lib/R/site-library/xgboost/libs/xgboost.so(xgboost::metric::EvalTweedieNLogLik::EvalTweedieNLogLik(char const*)+0x1eb) [0x7f1f0cea00db]
[bt] (3) /usr/local/lib/R/site-library/xgboost/libs/xgboost.so(+0x68ef1) [0x7f1f0ce78ef1]
[bt] (4) /usr/local/lib/R/site-library/xgboost/libs/xgboost.so(xgboost::Metric::Create(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)+0x263) [0x7f1f0ce7ede3]
[bt] (5) /usr/local/lib/R/site-library/xgboost/libs/xgboost.so(xgboost::LearnerImpl::Configure(std::vector<std::pair

The problem seems link with the parameter eval_metric = "tweedie-nloglik" because if I change the eval_metric to logloss it passes:

param$eval_metric <- "logloss"
resBoost <- xgb.train(params = param, data=dtrain, nrounds = 20, watchlist=watchlist)
[1]     eval-logloss:0.634391   train-logloss:0.849734
[2]     eval-logloss:0.634391   train-logloss:0.849734
...

Any idea how to use the eval_metric = "tweedie-nloglik" parameter as it seems like the most appropriate in my context? Thanks


Solution

  • TL;DR: thanks to Frans Rodenburg Comment: use eval_metric="tweedie-nloglik@1.2

    I was looking on the implementation of tweedie eval (I don't even know what tweedie is) and on the logloss eval in the following link

    tweedie:

    struct EvalTweedieNLogLik: public EvalEWiseBase<EvalTweedieNLogLik> {
      explicit EvalTweedieNLogLik(const char* param) {
        CHECK(param != nullptr)
            << "tweedie-nloglik must be in format tweedie-nloglik@rho";
        rho_ = atof(param);
        CHECK(rho_ < 2 && rho_ >= 1)
            << "tweedie variance power must be in interval [1, 2)";
        std::ostringstream os;
        os << "tweedie-nloglik@" << rho_;
        name_ = os.str();
      }
      const char *Name() const override {
        return name_.c_str();
      }
      inline bst_float EvalRow(bst_float y, bst_float p) const {
        bst_float a = y * std::exp((1 - rho_) * std::log(p)) / (1 - rho_);
        bst_float b = std::exp((2 - rho_) * std::log(p)) / (2 - rho_);
        return -a + b;
      }
     protected:
      std::string name_;
      bst_float rho_;
    };
    

    logloss:

    struct EvalLogLoss : public EvalEWiseBase<EvalLogLoss> {
      const char *Name() const override {
        return "logloss";
      }
      inline bst_float EvalRow(bst_float y, bst_float py) const {
        const bst_float eps = 1e-16f;
        const bst_float pneg = 1.0f - py;
        if (py < eps) {
          return -y * std::log(eps) - (1.0f - y)  * std::log(1.0f - eps);
        } else if (pneg < eps) {
          return -y * std::log(1.0f - eps) - (1.0f - y)  * std::log(eps);
        } else {
          return -y * std::log(py) - (1.0f - y) * std::log(pneg);
        }
      }
    };
    

    It looks like EvalTweedieNLogLik should get a parameter named param. Also appear like you get those exact lines:

    CHECK(param != nullptr)
        << "tweedie-nloglik must be in format tweedie-nloglik@rho";
    

    When I'm comparing it to EvalLogLoss the relevance difference is that it doesn't need a variable and that's why its working.

    Thanks to @Frans Rodenburg Comment I kept searching and read an example of how to use it here.

    use eval_metric="tweedie-nloglik@1.2

    I also got it wrong at the first time when read those lines from xgboost documentation:

    tweedie-nloglik: negative log-likelihood for Tweedie regression (at a specified value of the tweedie_variance_power parameter)

    It might be only relevant to python.