add poisson regression
This commit is contained in:
parent
a310db86a1
commit
667a752e04
@ -28,13 +28,13 @@ extern "C" {
|
|||||||
void (*Check)(int exp, const char *fmt, ...) = XGBoostCheck_R;
|
void (*Check)(int exp, const char *fmt, ...) = XGBoostCheck_R;
|
||||||
void (*Error)(const char *fmt, ...) = error;
|
void (*Error)(const char *fmt, ...) = error;
|
||||||
}
|
}
|
||||||
} // namespace utils
|
bool CheckNAN(double v) {
|
||||||
|
|
||||||
namespace wrapper {
|
|
||||||
bool CheckNAN(float v) {
|
|
||||||
return ISNAN(v);
|
return ISNAN(v);
|
||||||
}
|
}
|
||||||
} // namespace wrapper
|
bool LogGamma(double v) {
|
||||||
|
return lgammafn(v);
|
||||||
|
}
|
||||||
|
} // namespace utils
|
||||||
|
|
||||||
namespace random {
|
namespace random {
|
||||||
void Seed(unsigned seed) {
|
void Seed(unsigned seed) {
|
||||||
|
|||||||
@ -8,6 +8,7 @@
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#include <Rinternals.h>
|
#include <Rinternals.h>
|
||||||
#include <R_ext/Random.h>
|
#include <R_ext/Random.h>
|
||||||
|
#include <Rmath.h>
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
|||||||
@ -55,6 +55,8 @@ From xgboost-unity, the ```bst:``` prefix is no longer needed for booster parame
|
|||||||
- "reg:logistic" --logistic regression
|
- "reg:logistic" --logistic regression
|
||||||
- "binary:logistic" --logistic regression for binary classification, output probability
|
- "binary:logistic" --logistic regression for binary classification, output probability
|
||||||
- "binary:logitraw" --logistic regression for binary classification, output score before logistic transformation
|
- "binary:logitraw" --logistic regression for binary classification, output score before logistic transformation
|
||||||
|
- "count:poisson" --poisson regression for count data, output mean of poisson distribution
|
||||||
|
- max_delta_step is set to 1 by default in poisson regression(used to safeguard optimization)
|
||||||
- "multi:softmax" --set XGBoost to do multiclass classification using the softmax objective, you also need to set num_class(number of classes)
|
- "multi:softmax" --set XGBoost to do multiclass classification using the softmax objective, you also need to set num_class(number of classes)
|
||||||
- "multi:softprob" --same as softmax, but output a vector of ndata * nclass, which can be further reshaped to ndata, nclass matrix. The result contains predicted probability of each data point belonging to each class.
|
- "multi:softprob" --same as softmax, but output a vector of ndata * nclass, which can be further reshaped to ndata, nclass matrix. The result contains predicted probability of each data point belonging to each class.
|
||||||
- "rank:pairwise" --set XGBoost to do ranking task by minimizing the pairwise loss
|
- "rank:pairwise" --set XGBoost to do ranking task by minimizing the pairwise loss
|
||||||
|
|||||||
@ -12,6 +12,7 @@
|
|||||||
#include <climits>
|
#include <climits>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include "../sync/sync.h"
|
#include "../sync/sync.h"
|
||||||
|
#include "../utils/math.h"
|
||||||
#include "./evaluation.h"
|
#include "./evaluation.h"
|
||||||
#include "./helper_utils.h"
|
#include "./helper_utils.h"
|
||||||
|
|
||||||
@ -106,6 +107,18 @@ struct EvalError : public EvalEWiseBase<EvalError> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*! \brief loglikelihood of poission distribution */
|
||||||
|
struct EvalPoissionNegLogLik : public EvalEWiseBase<EvalPoissionNegLogLik> {
|
||||||
|
virtual const char *Name(void) const {
|
||||||
|
return "poisson-nloglik";
|
||||||
|
}
|
||||||
|
inline static float EvalRow(float y, float py) {
|
||||||
|
const float eps = 1e-16f;
|
||||||
|
if (py < eps) py = eps;
|
||||||
|
return utils::LogGamma(y + 1.0f) + py - std::log(py) * y;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief base class of multi-class evaluation
|
* \brief base class of multi-class evaluation
|
||||||
* \tparam Derived the name of subclass
|
* \tparam Derived the name of subclass
|
||||||
|
|||||||
@ -46,6 +46,7 @@ inline IEvaluator* CreateEvaluator(const char *name) {
|
|||||||
if (!strcmp(name, "merror")) return new EvalMatchError();
|
if (!strcmp(name, "merror")) return new EvalMatchError();
|
||||||
if (!strcmp(name, "logloss")) return new EvalLogLoss();
|
if (!strcmp(name, "logloss")) return new EvalLogLoss();
|
||||||
if (!strcmp(name, "mlogloss")) return new EvalMultiLogLoss();
|
if (!strcmp(name, "mlogloss")) return new EvalMultiLogLoss();
|
||||||
|
if (!strcmp(name, "poisson-nloglik")) return new EvalPoissionNegLogLik();
|
||||||
if (!strcmp(name, "auc")) return new EvalAuc();
|
if (!strcmp(name, "auc")) return new EvalAuc();
|
||||||
if (!strncmp(name, "ams@", 4)) return new EvalAMS(name);
|
if (!strncmp(name, "ams@", 4)) return new EvalAMS(name);
|
||||||
if (!strncmp(name, "pre@", 4)) return new EvalPrecision(name);
|
if (!strncmp(name, "pre@", 4)) return new EvalPrecision(name);
|
||||||
|
|||||||
@ -107,7 +107,9 @@ class BoostLearner : public rabit::Serializable {
|
|||||||
}
|
}
|
||||||
if (!strcmp("seed_per_iter", name)) seed_per_iteration = atoi(val);
|
if (!strcmp("seed_per_iter", name)) seed_per_iteration = atoi(val);
|
||||||
if (!strcmp("save_base64", name)) save_base64 = atoi(val);
|
if (!strcmp("save_base64", name)) save_base64 = atoi(val);
|
||||||
if (!strcmp(name, "num_class")) this->SetParam("num_output_group", val);
|
if (!strcmp(name, "num_class")) {
|
||||||
|
this->SetParam("num_output_group", val);
|
||||||
|
}
|
||||||
if (!strcmp(name, "nthread")) {
|
if (!strcmp(name, "nthread")) {
|
||||||
omp_set_num_threads(atoi(val));
|
omp_set_num_threads(atoi(val));
|
||||||
}
|
}
|
||||||
@ -383,7 +385,8 @@ class BoostLearner : public rabit::Serializable {
|
|||||||
utils::Assert(gbm_ == NULL, "GBM and obj should be NULL");
|
utils::Assert(gbm_ == NULL, "GBM and obj should be NULL");
|
||||||
obj_ = CreateObjFunction(name_obj_.c_str());
|
obj_ = CreateObjFunction(name_obj_.c_str());
|
||||||
gbm_ = gbm::CreateGradBooster(name_gbm_.c_str());
|
gbm_ = gbm::CreateGradBooster(name_gbm_.c_str());
|
||||||
|
this->InitAdditionDefaultParam();
|
||||||
|
// set parameters
|
||||||
for (size_t i = 0; i < cfg_.size(); ++i) {
|
for (size_t i = 0; i < cfg_.size(); ++i) {
|
||||||
obj_->SetParam(cfg_[i].first.c_str(), cfg_[i].second.c_str());
|
obj_->SetParam(cfg_[i].first.c_str(), cfg_[i].second.c_str());
|
||||||
gbm_->SetParam(cfg_[i].first.c_str(), cfg_[i].second.c_str());
|
gbm_->SetParam(cfg_[i].first.c_str(), cfg_[i].second.c_str());
|
||||||
@ -392,6 +395,15 @@ class BoostLearner : public rabit::Serializable {
|
|||||||
evaluator_.AddEval(obj_->DefaultEvalMetric());
|
evaluator_.AddEval(obj_->DefaultEvalMetric());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/*!
|
||||||
|
* \brief additional default value for specific objs
|
||||||
|
*/
|
||||||
|
inline void InitAdditionDefaultParam(void) {
|
||||||
|
if (name_obj_ == "count:poisson") {
|
||||||
|
obj_->SetParam("max_delta_step", "0.7");
|
||||||
|
gbm_->SetParam("max_delta_step", "0.7");
|
||||||
|
}
|
||||||
|
}
|
||||||
/*!
|
/*!
|
||||||
* \brief get un-transformed prediction
|
* \brief get un-transformed prediction
|
||||||
* \param data training data matrix
|
* \param data training data matrix
|
||||||
|
|||||||
@ -114,7 +114,7 @@ struct LossType {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/*! \brief objective function that only need to */
|
/*! \brief objective function that only need to */
|
||||||
class RegLossObj : public IObjFunction{
|
class RegLossObj : public IObjFunction {
|
||||||
public:
|
public:
|
||||||
explicit RegLossObj(int loss_type) {
|
explicit RegLossObj(int loss_type) {
|
||||||
loss.loss_type = loss_type;
|
loss.loss_type = loss_type;
|
||||||
@ -173,6 +173,72 @@ class RegLossObj : public IObjFunction{
|
|||||||
LossType loss;
|
LossType loss;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// poisson regression for count
|
||||||
|
class PoissonRegression : public IObjFunction {
|
||||||
|
public:
|
||||||
|
explicit PoissonRegression(void) {
|
||||||
|
max_delta_step = 0.0f;
|
||||||
|
}
|
||||||
|
virtual ~PoissonRegression(void) {}
|
||||||
|
|
||||||
|
virtual void SetParam(const char *name, const char *val) {
|
||||||
|
using namespace std;
|
||||||
|
if (!strcmp( "max_delta_step", name )) {
|
||||||
|
max_delta_step = static_cast<float>(atof(val));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
virtual void GetGradient(const std::vector<float> &preds,
|
||||||
|
const MetaInfo &info,
|
||||||
|
int iter,
|
||||||
|
std::vector<bst_gpair> *out_gpair) {
|
||||||
|
utils::Check(max_delta_step != 0.0f,
|
||||||
|
"PoissonRegression: need to set max_delta_step");
|
||||||
|
utils::Check(info.labels.size() != 0, "label set cannot be empty");
|
||||||
|
utils::Check(preds.size() == info.labels.size(),
|
||||||
|
"labels are not correctly provided");
|
||||||
|
std::vector<bst_gpair> &gpair = *out_gpair;
|
||||||
|
gpair.resize(preds.size());
|
||||||
|
// check if label in range
|
||||||
|
bool label_correct = true;
|
||||||
|
// start calculating gradient
|
||||||
|
const long ndata = static_cast<bst_omp_uint>(preds.size());
|
||||||
|
#pragma omp parallel for schedule(static)
|
||||||
|
for (long i = 0; i < ndata; ++i) {
|
||||||
|
float p = preds[i];
|
||||||
|
float w = info.GetWeight(i);
|
||||||
|
float y = info.labels[i];
|
||||||
|
if (y >= 0.0f) {
|
||||||
|
gpair[i] = bst_gpair((std::exp(p) - y) * w,
|
||||||
|
std::exp(p + max_delta_step) * w);
|
||||||
|
} else {
|
||||||
|
label_correct = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
utils::Check(label_correct,
|
||||||
|
"PoissonRegression: label must be nonnegative");
|
||||||
|
}
|
||||||
|
virtual void PredTransform(std::vector<float> *io_preds) {
|
||||||
|
std::vector<float> &preds = *io_preds;
|
||||||
|
const long ndata = static_cast<long>(preds.size());
|
||||||
|
#pragma omp parallel for schedule(static)
|
||||||
|
for (long j = 0; j < ndata; ++j) {
|
||||||
|
preds[j] = std::exp(preds[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
virtual void EvalTransform(std::vector<float> *io_preds) {
|
||||||
|
PredTransform(io_preds);
|
||||||
|
}
|
||||||
|
virtual float ProbToMargin(float base_score) const {
|
||||||
|
return std::log(base_score);
|
||||||
|
}
|
||||||
|
virtual const char* DefaultEvalMetric(void) const {
|
||||||
|
return "poisson-nloglik";
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
float max_delta_step;
|
||||||
|
};
|
||||||
|
|
||||||
// softmax multi-class classification
|
// softmax multi-class classification
|
||||||
class SoftmaxMultiClassObj : public IObjFunction {
|
class SoftmaxMultiClassObj : public IObjFunction {
|
||||||
public:
|
public:
|
||||||
|
|||||||
@ -72,6 +72,7 @@ inline IObjFunction* CreateObjFunction(const char *name) {
|
|||||||
if (!strcmp("reg:logistic", name)) return new RegLossObj(LossType::kLogisticNeglik);
|
if (!strcmp("reg:logistic", name)) return new RegLossObj(LossType::kLogisticNeglik);
|
||||||
if (!strcmp("binary:logistic", name)) return new RegLossObj(LossType::kLogisticClassify);
|
if (!strcmp("binary:logistic", name)) return new RegLossObj(LossType::kLogisticClassify);
|
||||||
if (!strcmp("binary:logitraw", name)) return new RegLossObj(LossType::kLogisticRaw);
|
if (!strcmp("binary:logitraw", name)) return new RegLossObj(LossType::kLogisticRaw);
|
||||||
|
if (!strcmp("count:poisson", name)) return new PoissonRegression();
|
||||||
if (!strcmp("multi:softmax", name)) return new SoftmaxMultiClassObj(0);
|
if (!strcmp("multi:softmax", name)) return new SoftmaxMultiClassObj(0);
|
||||||
if (!strcmp("multi:softprob", name)) return new SoftmaxMultiClassObj(1);
|
if (!strcmp("multi:softprob", name)) return new SoftmaxMultiClassObj(1);
|
||||||
if (!strcmp("rank:pairwise", name )) return new PairwiseRankObj();
|
if (!strcmp("rank:pairwise", name )) return new PairwiseRankObj();
|
||||||
|
|||||||
36
src/utils/math.h
Normal file
36
src/utils/math.h
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
#ifndef XGBOOST_UTILS_MATH_H_
|
||||||
|
#define XGBOOST_UTILS_MATH_H_
|
||||||
|
/*!
|
||||||
|
* \file math.h
|
||||||
|
* \brief support additional math
|
||||||
|
* \author Tianqi Chen
|
||||||
|
*/
|
||||||
|
#include <cmath>
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
extern "C" {
|
||||||
|
#include <amp_math.h>
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
namespace xgboost {
|
||||||
|
namespace utils {
|
||||||
|
#ifdef XGBOOST_STRICT_CXX98_
|
||||||
|
// check nan
|
||||||
|
bool CheckNAN(double v);
|
||||||
|
double LogGamma(double v);
|
||||||
|
#else
|
||||||
|
template<typename T>
|
||||||
|
inline bool CheckNAN(T v) {
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
return (_isnan(x) != 0);
|
||||||
|
#else
|
||||||
|
return isnan(v);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
template<typename T>
|
||||||
|
inline T LogGamma(T v) {
|
||||||
|
return lgamma(v);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
} // namespace utils
|
||||||
|
} // namespace xgboost
|
||||||
|
#endif // XGBOOST_UTILS_MATH_H_
|
||||||
@ -9,16 +9,12 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
// include all std functions
|
// include all std functions
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
#define isnan(x) (_isnan(x) != 0)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "./xgboost_wrapper.h"
|
#include "./xgboost_wrapper.h"
|
||||||
#include "../src/data.h"
|
#include "../src/data.h"
|
||||||
#include "../src/learner/learner-inl.hpp"
|
#include "../src/learner/learner-inl.hpp"
|
||||||
#include "../src/io/io.h"
|
#include "../src/io/io.h"
|
||||||
#include "../src/utils/utils.h"
|
#include "../src/utils/utils.h"
|
||||||
|
#include "../src/utils/math.h"
|
||||||
#include "../src/utils/group_data.h"
|
#include "../src/utils/group_data.h"
|
||||||
#include "../src/io/simple_dmatrix-inl.hpp"
|
#include "../src/io/simple_dmatrix-inl.hpp"
|
||||||
|
|
||||||
@ -97,14 +93,6 @@ class Booster: public learner::BoostLearner {
|
|||||||
private:
|
private:
|
||||||
bool init_model;
|
bool init_model;
|
||||||
};
|
};
|
||||||
#if !defined(XGBOOST_STRICT_CXX98_)
|
|
||||||
inline bool CheckNAN(float v) {
|
|
||||||
return isnan(v);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
// redirect to defs in R
|
|
||||||
bool CheckNAN(float v);
|
|
||||||
#endif
|
|
||||||
} // namespace wrapper
|
} // namespace wrapper
|
||||||
} // namespace xgboost
|
} // namespace xgboost
|
||||||
|
|
||||||
@ -175,7 +163,7 @@ extern "C"{
|
|||||||
bst_ulong nrow,
|
bst_ulong nrow,
|
||||||
bst_ulong ncol,
|
bst_ulong ncol,
|
||||||
float missing) {
|
float missing) {
|
||||||
bool nan_missing = CheckNAN(missing);
|
bool nan_missing = utils::CheckNAN(missing);
|
||||||
DMatrixSimple *p_mat = new DMatrixSimple();
|
DMatrixSimple *p_mat = new DMatrixSimple();
|
||||||
DMatrixSimple &mat = *p_mat;
|
DMatrixSimple &mat = *p_mat;
|
||||||
mat.info.info.num_row = nrow;
|
mat.info.info.num_row = nrow;
|
||||||
@ -183,7 +171,7 @@ extern "C"{
|
|||||||
for (bst_ulong i = 0; i < nrow; ++i, data += ncol) {
|
for (bst_ulong i = 0; i < nrow; ++i, data += ncol) {
|
||||||
bst_ulong nelem = 0;
|
bst_ulong nelem = 0;
|
||||||
for (bst_ulong j = 0; j < ncol; ++j) {
|
for (bst_ulong j = 0; j < ncol; ++j) {
|
||||||
if (CheckNAN(data[j])) {
|
if (utils::CheckNAN(data[j])) {
|
||||||
utils::Check(nan_missing,
|
utils::Check(nan_missing,
|
||||||
"There are NAN in the matrix, however, you did not set missing=NAN");
|
"There are NAN in the matrix, however, you did not set missing=NAN");
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user