From ae536756ae857a963093b974347c65b01a473993 Mon Sep 17 00:00:00 2001 From: Jiaming Yuan Date: Fri, 18 Oct 2019 01:56:02 -0400 Subject: [PATCH] Add Model and Configurable interface. (#4945) * Apply Configurable to objective functions. * Apply Model to Learner and Regtree, gbm. * Add Load/SaveConfig to objs. * Refactor obj tests to use smart pointer. * Dummy methods for Save/Load Model. --- include/xgboost/generic_parameters.h | 2 +- include/xgboost/json.h | 10 ++ include/xgboost/learner.h | 3 +- include/xgboost/logging.h | 6 +- include/xgboost/model.h | 44 ++++++ include/xgboost/objective.h | 3 +- .../{enum_class_param.h => parameter.h} | 30 +++- include/xgboost/tree_model.h | 30 +--- plugin/example/custom_obj.cc | 13 +- src/common/json.cc | 8 + src/gbm/gblinear_model.h | 14 +- src/gbm/gbtree.h | 2 +- src/gbm/gbtree_model.h | 16 +- src/learner.cc | 11 ++ src/objective/hinge.cu | 9 +- src/objective/multiclass_obj.cu | 25 +++- src/objective/objective.cc | 8 +- src/objective/rank_obj.cc | 45 +++++- src/objective/regression_loss.h | 11 +- src/objective/regression_obj.cu | 66 +++++++-- src/tree/tree_model.cc | 29 ++++ src/tree/updater_gpu_hist.cu | 4 +- src/tree/updater_sync.cc | 4 +- ..._enum_class_param.cc => test_parameter.cc} | 2 +- tests/cpp/helpers.cc | 42 ++++-- tests/cpp/helpers.h | 27 +++- tests/cpp/objective/test_hinge.cc | 6 +- tests/cpp/objective/test_multiclass_obj.cc | 38 +++-- tests/cpp/objective/test_ranking_obj.cc | 58 ++++++-- tests/cpp/objective/test_regression_obj.cc | 140 ++++++++++-------- tests/cpp/tree/test_tree_model.cc | 2 +- 31 files changed, 521 insertions(+), 187 deletions(-) create mode 100644 include/xgboost/model.h rename include/xgboost/{enum_class_param.h => parameter.h} (75%) rename tests/cpp/common/{test_enum_class_param.cc => test_parameter.cc} (97%) diff --git a/include/xgboost/generic_parameters.h b/include/xgboost/generic_parameters.h index 30fe08882..37833be7e 100644 --- a/include/xgboost/generic_parameters.h +++ b/include/xgboost/generic_parameters.h @@ -7,7 +7,7 @@ #include #include -#include +#include #include diff --git a/include/xgboost/json.h b/include/xgboost/json.h index 790eca8b8..99ad88aa3 100644 --- a/include/xgboost/json.h +++ b/include/xgboost/json.h @@ -322,6 +322,9 @@ class Json { static void Dump(Json json, std::ostream* stream, bool pretty = ConsoleLogger::ShouldLog( ConsoleLogger::LogVerbosity::kDebug)); + static void Dump(Json json, std::string* out, + bool pretty = ConsoleLogger::ShouldLog( + ConsoleLogger::LogVerbosity::kDebug)); Json() : ptr_{new JsonNull} {} @@ -400,6 +403,13 @@ class Json { return *ptr_ == *(rhs.ptr_); } + friend std::ostream& operator<<(std::ostream& os, Json const& j) { + std::string str; + Json::Dump(j, &str); + os << str; + return os; + } + private: std::shared_ptr ptr_; }; diff --git a/include/xgboost/learner.h b/include/xgboost/learner.h index 8897dc831..fbdc68b8a 100644 --- a/include/xgboost/learner.h +++ b/include/xgboost/learner.h @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -41,7 +42,7 @@ namespace xgboost { * * \endcode */ -class Learner : public rabit::Serializable { +class Learner : public Model, public rabit::Serializable { public: /*! \brief virtual destructor */ ~Learner() override = default; diff --git a/include/xgboost/logging.h b/include/xgboost/logging.h index a2e9aad9c..e6d4f9aab 100644 --- a/include/xgboost/logging.h +++ b/include/xgboost/logging.h @@ -1,9 +1,9 @@ /*! * Copyright (c) 2015-2019 by Contributors * \file logging.h - * \brief defines console logging options for xgboost. - * Use to enforce unified print behavior. - * For debug loggers, use LOG(INFO) and LOG(ERROR). + * + * \brief defines console logging options for xgboost. Use to enforce unified print + * behavior. */ #ifndef XGBOOST_LOGGING_H_ #define XGBOOST_LOGGING_H_ diff --git a/include/xgboost/model.h b/include/xgboost/model.h new file mode 100644 index 000000000..8d45c69d5 --- /dev/null +++ b/include/xgboost/model.h @@ -0,0 +1,44 @@ +/*! + * Copyright (c) 2019 by Contributors + * \file model.h + * \brief Defines the abstract interface for different components in XGBoost. + */ +#ifndef XGBOOST_MODEL_H_ +#define XGBOOST_MODEL_H_ + +namespace dmlc { +class Stream; +} // namespace dmlc + +namespace xgboost { + +class Json; + +struct Model { + /*! + * \brief Save the model to stream. + * \param fo output write stream + */ + virtual void SaveModel(dmlc::Stream* fo) const = 0; + /*! + * \brief Load the model from stream. + * \param fi input read stream + */ + virtual void LoadModel(dmlc::Stream* fi) = 0; +}; + +struct Configurable { + /*! + * \brief Load configuration from JSON object + * \param in JSON object containing the configuration + */ + virtual void LoadConfig(Json const& in) = 0; + /*! + * \brief Save configuration to JSON object + * \param out pointer to output JSON object + */ + virtual void SaveConfig(Json* out) const = 0; +}; +} // namespace xgboost + +#endif // XGBOOST_MODEL_H_ diff --git a/include/xgboost/objective.h b/include/xgboost/objective.h index b5b365a0b..8affd8e32 100644 --- a/include/xgboost/objective.h +++ b/include/xgboost/objective.h @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -21,7 +22,7 @@ namespace xgboost { /*! \brief interface of objective function */ -class ObjFunction { +class ObjFunction : public Configurable { protected: GenericParameter const* tparam_; diff --git a/include/xgboost/enum_class_param.h b/include/xgboost/parameter.h similarity index 75% rename from include/xgboost/enum_class_param.h rename to include/xgboost/parameter.h index fa7d452ab..f9130b1fa 100644 --- a/include/xgboost/enum_class_param.h +++ b/include/xgboost/parameter.h @@ -5,10 +5,11 @@ * \author Hyunsu Philip Cho */ -#ifndef XGBOOST_ENUM_CLASS_PARAM_H_ -#define XGBOOST_ENUM_CLASS_PARAM_H_ +#ifndef XGBOOST_PARAMETER_H_ +#define XGBOOST_PARAMETER_H_ #include +#include #include #include @@ -78,4 +79,27 @@ class FieldEntry : public FieldEntry { \ } /* namespace parameter */ \ } /* namespace dmlc */ -#endif // XGBOOST_ENUM_CLASS_PARAM_H_ +namespace xgboost { +template +struct XGBoostParameter : public dmlc::Parameter { + protected: + bool initialised_ {false}; + + public: + template + Args UpdateAllowUnknown(Container const& kwargs, bool* out_changed = nullptr) { + if (initialised_) { + return dmlc::Parameter::UpdateAllowUnknown(kwargs, out_changed); + } else { + auto unknown = dmlc::Parameter::InitAllowUnknown(kwargs); + if (out_changed) { + *out_changed = true; + } + initialised_ = true; + return unknown; + } + } +}; +} // namespace xgboost + +#endif // XGBOOST_PARAMETER_H_ diff --git a/include/xgboost/tree_model.h b/include/xgboost/tree_model.h index 53f1baca6..47fb2d679 100644 --- a/include/xgboost/tree_model.h +++ b/include/xgboost/tree_model.h @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -93,7 +94,7 @@ struct RTreeNodeStat { * \brief define regression tree to be the most common tree model. * This is the data structure used in xgboost's major tree models. */ -class RegTree { +class RegTree : public Model { public: /*! \brief auxiliary statistics of node to help tree building */ using SplitCondT = bst_float; @@ -289,38 +290,17 @@ class RegTree { const RTreeNodeStat& Stat(int nid) const { return stats_[nid]; } + /*! * \brief load model from stream * \param fi input stream */ - void Load(dmlc::Stream* fi) { - CHECK_EQ(fi->Read(¶m, sizeof(TreeParam)), sizeof(TreeParam)); - nodes_.resize(param.num_nodes); - stats_.resize(param.num_nodes); - CHECK_NE(param.num_nodes, 0); - CHECK_EQ(fi->Read(dmlc::BeginPtr(nodes_), sizeof(Node) * nodes_.size()), - sizeof(Node) * nodes_.size()); - CHECK_EQ(fi->Read(dmlc::BeginPtr(stats_), sizeof(RTreeNodeStat) * stats_.size()), - sizeof(RTreeNodeStat) * stats_.size()); - // chg deleted nodes - deleted_nodes_.resize(0); - for (int i = param.num_roots; i < param.num_nodes; ++i) { - if (nodes_[i].IsDeleted()) deleted_nodes_.push_back(i); - } - CHECK_EQ(static_cast(deleted_nodes_.size()), param.num_deleted); - } + void LoadModel(dmlc::Stream* fi) override; /*! * \brief save model to stream * \param fo output stream */ - void Save(dmlc::Stream* fo) const { - CHECK_EQ(param.num_nodes, static_cast(nodes_.size())); - CHECK_EQ(param.num_nodes, static_cast(stats_.size())); - fo->Write(¶m, sizeof(TreeParam)); - CHECK_NE(param.num_nodes, 0); - fo->Write(dmlc::BeginPtr(nodes_), sizeof(Node) * nodes_.size()); - fo->Write(dmlc::BeginPtr(stats_), sizeof(RTreeNodeStat) * nodes_.size()); - } + void SaveModel(dmlc::Stream* fo) const override; bool operator==(const RegTree& b) const { return nodes_ == b.nodes_ && stats_ == b.stats_ && diff --git a/plugin/example/custom_obj.cc b/plugin/example/custom_obj.cc index b6cb174de..f15903256 100644 --- a/plugin/example/custom_obj.cc +++ b/plugin/example/custom_obj.cc @@ -1,5 +1,5 @@ /*! - * Copyright 2015 by Contributors + * Copyright 2015-2019 by Contributors * \file custom_metric.cc * \brief This is an example to define plugin of xgboost. * This plugin defines the additional metric function. @@ -7,6 +7,7 @@ #include #include #include +#include namespace xgboost { namespace obj { @@ -69,6 +70,16 @@ class MyLogistic : public ObjFunction { return -std::log(1.0f / base_score - 1.0f); } + void SaveConfig(Json* p_out) const override { + auto& out = *p_out; + out["name"] = String("my_logistic"); + out["my_logistic_param"] = toJson(param_); + } + + void LoadConfig(Json const& in) override { + fromJson(in["my_logistic_param"], ¶m_); + } + private: MyLogisticParam param_; }; diff --git a/src/common/json.cc b/src/common/json.cc index 5c14db47b..2c8000787 100644 --- a/src/common/json.cc +++ b/src/common/json.cc @@ -718,5 +718,13 @@ void Json::Dump(Json json, std::ostream *stream, bool pretty) { writer.Save(json); } +void Json::Dump(Json json, std::string* str, bool pretty) { + GlobalCLocale guard; + std::stringstream ss; + JsonWriter writer(&ss, pretty); + writer.Save(json); + *str = ss.str(); +} + Json& Json::operator=(Json const &other) = default; } // namespace xgboost diff --git a/src/gbm/gblinear_model.h b/src/gbm/gblinear_model.h index 57daed398..2c059dc2e 100644 --- a/src/gbm/gblinear_model.h +++ b/src/gbm/gblinear_model.h @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -34,7 +35,7 @@ struct GBLinearModelParam : public dmlc::Parameter { }; // model for linear booster -class GBLinearModel { +class GBLinearModel : public Model { public: // parameter GBLinearModelParam param; @@ -57,6 +58,17 @@ class GBLinearModel { CHECK_EQ(fi->Read(¶m, sizeof(param)), sizeof(param)); fi->Read(&weight); } + + void LoadModel(dmlc::Stream* fi) override { + // They are the same right now until we can split up the saved parameter from model. + this->Load(fi); + } + + void SaveModel(dmlc::Stream* fo) const override { + // They are the same right now until we can split up the saved parameter from model. + this->Save(fo); + } + // model bias inline bst_float* bias() { return &weight[param.num_feature * param.num_output_group]; diff --git a/src/gbm/gbtree.h b/src/gbm/gbtree.h index 42a744068..90745bbd4 100644 --- a/src/gbm/gbtree.h +++ b/src/gbm/gbtree.h @@ -14,7 +14,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/gbm/gbtree_model.h b/src/gbm/gbtree_model.h index fff339b59..26ac6cffa 100644 --- a/src/gbm/gbtree_model.h +++ b/src/gbm/gbtree_model.h @@ -4,6 +4,7 @@ #pragma once #include #include +#include #include #include @@ -61,7 +62,7 @@ struct GBTreeModelParam : public dmlc::Parameter { } }; -struct GBTreeModel { +struct GBTreeModel : public Model { explicit GBTreeModel(bst_float base_margin) : base_margin(base_margin) {} void Configure(const Args& cfg) { // initialize model parameters if not yet been initialized. @@ -81,6 +82,15 @@ struct GBTreeModel { } } + void LoadModel(dmlc::Stream* fi) override { + // They are the same right now until we can split up the saved parameter from model. + this->Load(fi); + } + void SaveModel(dmlc::Stream* fo) const override { + // They are the same right now until we can split up the saved parameter from model. + this->Save(fo); + } + void Load(dmlc::Stream* fi) { CHECK_EQ(fi->Read(¶m, sizeof(param)), sizeof(param)) << "GBTree: invalid model file"; @@ -88,7 +98,7 @@ struct GBTreeModel { trees_to_update.clear(); for (int i = 0; i < param.num_trees; ++i) { std::unique_ptr ptr(new RegTree()); - ptr->Load(fi); + ptr->LoadModel(fi); trees.push_back(std::move(ptr)); } tree_info.resize(param.num_trees); @@ -103,7 +113,7 @@ struct GBTreeModel { CHECK_EQ(param.num_trees, static_cast(trees.size())); fo->Write(¶m, sizeof(param)); for (const auto & tree : trees) { - tree->Save(fo); + tree->SaveModel(fo); } if (tree_info.size() != 0) { fo->Write(dmlc::BeginPtr(tree_info), sizeof(int) * tree_info.size()); diff --git a/src/learner.cc b/src/learner.cc index b427405e0..2c90509e8 100644 --- a/src/learner.cc +++ b/src/learner.cc @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -196,6 +197,16 @@ class LearnerImpl : public Learner { } } + void LoadModel(dmlc::Stream* fi) override { + // They are the same right now until we can split up the saved parameter from model. + this->Load(fi); + } + + void SaveModel(dmlc::Stream* fo) const override { + // They are the same right now until we can split up the saved parameter from model. + this->Save(fo); + } + void Load(dmlc::Stream* fi) override { generic_param_.InitAllowUnknown(Args{}); tparam_.Init(std::vector>{}); diff --git a/src/objective/hinge.cu b/src/objective/hinge.cu index f770256d9..9652670aa 100644 --- a/src/objective/hinge.cu +++ b/src/objective/hinge.cu @@ -1,10 +1,11 @@ /*! - * Copyright 2018 by Contributors + * Copyright 2018-2019 by Contributors * \file hinge.cc * \brief Provides an implementation of the hinge loss function * \author Henry Gouk */ #include "xgboost/objective.h" +#include "xgboost/json.h" #include "xgboost/span.h" #include "xgboost/host_device_vector.h" @@ -76,6 +77,12 @@ class HingeObj : public ObjFunction { const char* DefaultEvalMetric() const override { return "error"; } + + void SaveConfig(Json* p_out) const override { + auto& out = *p_out; + out["name"] = String("binary:hinge"); + } + void LoadConfig(Json const& in) override {} }; // register the objective functions diff --git a/src/objective/multiclass_obj.cu b/src/objective/multiclass_obj.cu index c90d927e8..13e152924 100644 --- a/src/objective/multiclass_obj.cu +++ b/src/objective/multiclass_obj.cu @@ -14,6 +14,7 @@ #include #include +#include "xgboost/json.h" #include "../common/common.h" #include "../common/math.h" #include "../common/transform.h" @@ -25,7 +26,7 @@ namespace obj { DMLC_REGISTRY_FILE_TAG(multiclass_obj_gpu); #endif // defined(XGBOOST_USE_CUDA) -struct SoftmaxMultiClassParam : public dmlc::Parameter { +struct SoftmaxMultiClassParam : public XGBoostParameter { int num_class; // declare parameters DMLC_DECLARE_PARAMETER(SoftmaxMultiClassParam) { @@ -37,10 +38,10 @@ struct SoftmaxMultiClassParam : public dmlc::Parameter { class SoftmaxMultiClassObj : public ObjFunction { public: explicit SoftmaxMultiClassObj(bool output_prob) - : output_prob_(output_prob) { - } - void Configure(const std::vector >& args) override { - param_.InitAllowUnknown(args); + : output_prob_(output_prob) {} + + void Configure(Args const& args) override { + param_.UpdateAllowUnknown(args); } void GetGradient(const HostDeviceVector& preds, const MetaInfo& info, @@ -155,6 +156,20 @@ class SoftmaxMultiClassObj : public ObjFunction { } } + void SaveConfig(Json* p_out) const override { + auto& out = *p_out; + if (this->output_prob_) { + out["name"] = String("multi:softprob"); + } else { + out["name"] = String("multi:softmax"); + } + out["softmax_multiclass_param"] = toJson(param_); + } + + void LoadConfig(Json const& in) override { + fromJson(in["softmax_multiclass_param"], ¶m_); + } + private: // output probability bool output_prob_; diff --git a/src/objective/objective.cc b/src/objective/objective.cc index 52da83eab..e445d404a 100644 --- a/src/objective/objective.cc +++ b/src/objective/objective.cc @@ -6,6 +6,8 @@ #include #include +#include + #include "xgboost/host_device_vector.h" namespace dmlc { @@ -17,10 +19,12 @@ namespace xgboost { ObjFunction* ObjFunction::Create(const std::string& name, GenericParameter const* tparam) { auto *e = ::dmlc::Registry< ::xgboost::ObjFunctionReg>::Get()->Find(name); if (e == nullptr) { + std::stringstream ss; for (const auto& entry : ::dmlc::Registry< ::xgboost::ObjFunctionReg>::List()) { - LOG(INFO) << "Objective candidate: " << entry->name; + ss << "Objective candidate: " << entry->name << "\n"; } - LOG(FATAL) << "Unknown objective function " << name; + LOG(FATAL) << "Unknown objective function: `" << name << "`\n" + << ss.str(); } auto pobj = (e->body)(); pobj->tparam_ = tparam; diff --git a/src/objective/rank_obj.cc b/src/objective/rank_obj.cc index bc82d176f..bdd357af4 100644 --- a/src/objective/rank_obj.cc +++ b/src/objective/rank_obj.cc @@ -10,6 +10,10 @@ #include #include #include + +#include "xgboost/json.h" +#include "xgboost/parameter.h" + #include "../common/math.h" #include "../common/random.h" @@ -18,7 +22,7 @@ namespace obj { DMLC_REGISTRY_FILE_TAG(rank_obj); -struct LambdaRankParam : public dmlc::Parameter { +struct LambdaRankParam : public XGBoostParameter { int num_pairsample; float fix_list_weight; // declare parameters @@ -35,7 +39,7 @@ struct LambdaRankParam : public dmlc::Parameter { class LambdaRankObj : public ObjFunction { public: void Configure(const std::vector >& args) override { - param_.InitAllowUnknown(args); + param_.UpdateAllowUnknown(args); } void GetGradient(const HostDeviceVector& preds, @@ -170,7 +174,16 @@ class LambdaRankObj : public ObjFunction { virtual void GetLambdaWeight(const std::vector &sorted_list, std::vector *io_pairs) = 0; - private: + void SaveConfig(Json* p_out) const override { + auto& out = *p_out; + out["name"] = String("LambdaRankObj"); + out["lambda_rank_param"] = Object(); + for (auto const& kv : param_.__DICT__()) { + out["lambda_rank_param"][kv.first] = kv.second; + } + } + + protected: LambdaRankParam param_; }; @@ -178,6 +191,15 @@ class PairwiseRankObj: public LambdaRankObj{ protected: void GetLambdaWeight(const std::vector &sorted_list, std::vector *io_pairs) override {} + + void SaveConfig(Json* p_out) const override { + auto& out = *p_out; + out["name"] = String("rank:pairwise"); + out["lambda_rank_param"] = toJson(LambdaRankObj::param_); + } + void LoadConfig(Json const& in) override { + fromJson(in["lambda_rank_param"], &(LambdaRankObj::param_)); + } }; // beta version: NDCG lambda rank @@ -228,6 +250,14 @@ class LambdaRankObjNDCG : public LambdaRankObj { } return static_cast(sumdcg); } + void SaveConfig(Json* p_out) const override { + auto& out = *p_out; + out["name"] = String("rank:ndcg"); + out["lambda_rank_param"] = toJson(LambdaRankObj::param_); + } + void LoadConfig(Json const& in) override { + fromJson(in["lambda_rank_param"], &(LambdaRankObj::param_)); + } }; class LambdaRankObjMAP : public LambdaRankObj { @@ -315,6 +345,15 @@ class LambdaRankObjMAP : public LambdaRankObj { pair.neg_index, &map_stats); } } + + void SaveConfig(Json* p_out) const override { + auto& out = *p_out; + out["name"] = String("rank:map"); + out["lambda_rank_param"] = toJson(LambdaRankObj::param_); + } + void LoadConfig(Json const& in) override { + fromJson(in["lambda_rank_param"], &(LambdaRankObj::param_)); + } }; // register the objective functions diff --git a/src/objective/regression_loss.h b/src/objective/regression_loss.h index 6688452da..f8ef24fde 100644 --- a/src/objective/regression_loss.h +++ b/src/objective/regression_loss.h @@ -34,6 +34,8 @@ struct LinearSquareLoss { static bst_float ProbToMargin(bst_float base_score) { return base_score; } static const char* LabelErrorMsg() { return ""; } static const char* DefaultEvalMetric() { return "rmse"; } + + static const char* Name() { return "reg:squarederror"; } }; struct SquaredLogError { @@ -57,6 +59,8 @@ struct SquaredLogError { return "label must be greater than -1 for rmsle so that log(label + 1) can be valid."; } static const char* DefaultEvalMetric() { return "rmsle"; } + + static const char* Name() { return "reg:squaredlogerror"; } }; // logistic loss for probability regression task @@ -83,18 +87,21 @@ struct LogisticRegression { } static bst_float ProbToMargin(bst_float base_score) { CHECK(base_score > 0.0f && base_score < 1.0f) - << "base_score must be in (0,1) for logistic loss"; + << "base_score must be in (0,1) for logistic loss, got: " << base_score; return -logf(1.0f / base_score - 1.0f); } static const char* LabelErrorMsg() { return "label must be in [0,1] for logistic regression"; } static const char* DefaultEvalMetric() { return "rmse"; } + + static const char* Name() { return "reg:logistic"; } }; // logistic loss for binary classification task struct LogisticClassification : public LogisticRegression { static const char* DefaultEvalMetric() { return "error"; } + static const char* Name() { return "binary:logistic"; } }; // logistic loss, but predict un-transformed margin @@ -125,6 +132,8 @@ struct LogisticRaw : public LogisticRegression { return std::max(predt * (T(1.0f) - predt), eps); } static const char* DefaultEvalMetric() { return "auc"; } + + static const char* Name() { return "binary:logitraw"; } }; } // namespace obj diff --git a/src/objective/regression_obj.cu b/src/objective/regression_obj.cu index 373d31c87..f04471d1d 100644 --- a/src/objective/regression_obj.cu +++ b/src/objective/regression_obj.cu @@ -12,8 +12,10 @@ #include #include -#include "xgboost/span.h" #include "xgboost/host_device_vector.h" +#include "xgboost/json.h" +#include "xgboost/parameter.h" +#include "xgboost/span.h" #include "../common/transform.h" #include "../common/common.h" @@ -27,7 +29,7 @@ namespace obj { DMLC_REGISTRY_FILE_TAG(regression_obj_gpu); #endif // defined(XGBOOST_USE_CUDA) -struct RegLossParam : public dmlc::Parameter { +struct RegLossParam : public XGBoostParameter { float scale_pos_weight; // declare parameters DMLC_DECLARE_PARAMETER(RegLossParam) { @@ -45,7 +47,7 @@ class RegLossObj : public ObjFunction { RegLossObj() = default; void Configure(const std::vector >& args) override { - param_.InitAllowUnknown(args); + param_.UpdateAllowUnknown(args); } void GetGradient(const HostDeviceVector& preds, @@ -114,6 +116,16 @@ class RegLossObj : public ObjFunction { return Loss::ProbToMargin(base_score); } + void SaveConfig(Json* p_out) const override { + auto& out = *p_out; + out["name"] = String(Loss::Name()); + out["reg_loss_param"] = toJson(param_); + } + + void LoadConfig(Json const& in) override { + fromJson(in["reg_loss_param"], ¶m_); + } + protected: RegLossParam param_; }; @@ -121,23 +133,23 @@ class RegLossObj : public ObjFunction { // register the objective functions DMLC_REGISTER_PARAMETER(RegLossParam); -XGBOOST_REGISTER_OBJECTIVE(SquaredLossRegression, "reg:squarederror") +XGBOOST_REGISTER_OBJECTIVE(SquaredLossRegression, LinearSquareLoss::Name()) .describe("Regression with squared error.") .set_body([]() { return new RegLossObj(); }); -XGBOOST_REGISTER_OBJECTIVE(SquareLogError, "reg:squaredlogerror") +XGBOOST_REGISTER_OBJECTIVE(SquareLogError, SquaredLogError::Name()) .describe("Regression with root mean squared logarithmic error.") .set_body([]() { return new RegLossObj(); }); -XGBOOST_REGISTER_OBJECTIVE(LogisticRegression, "reg:logistic") +XGBOOST_REGISTER_OBJECTIVE(LogisticRegression, LogisticRegression::Name()) .describe("Logistic regression for probability regression task.") .set_body([]() { return new RegLossObj(); }); -XGBOOST_REGISTER_OBJECTIVE(LogisticClassification, "binary:logistic") +XGBOOST_REGISTER_OBJECTIVE(LogisticClassification, LogisticClassification::Name()) .describe("Logistic regression for binary classification task.") .set_body([]() { return new RegLossObj(); }); -XGBOOST_REGISTER_OBJECTIVE(LogisticRaw, "binary:logitraw") +XGBOOST_REGISTER_OBJECTIVE(LogisticRaw, LogisticRaw::Name()) .describe("Logistic regression for classification, output score " "before logistic transformation.") .set_body([]() { return new RegLossObj(); }); @@ -151,7 +163,7 @@ XGBOOST_REGISTER_OBJECTIVE(LinearRegression, "reg:linear") // End deprecated // declare parameter -struct PoissonRegressionParam : public dmlc::Parameter { +struct PoissonRegressionParam : public XGBoostParameter { float max_delta_step; DMLC_DECLARE_PARAMETER(PoissonRegressionParam) { DMLC_DECLARE_FIELD(max_delta_step).set_lower_bound(0.0f).set_default(0.7f) @@ -165,7 +177,7 @@ class PoissonRegression : public ObjFunction { public: // declare functions void Configure(const std::vector >& args) override { - param_.InitAllowUnknown(args); + param_.UpdateAllowUnknown(args); } void GetGradient(const HostDeviceVector& preds, @@ -227,6 +239,16 @@ class PoissonRegression : public ObjFunction { return "poisson-nloglik"; } + void SaveConfig(Json* p_out) const override { + auto& out = *p_out; + out["name"] = String("count:poisson"); + out["poisson_regression_param"] = toJson(param_); + } + + void LoadConfig(Json const& in) override { + fromJson(in["poisson_regression_param"], ¶m_); + } + private: PoissonRegressionParam param_; HostDeviceVector label_correct_; @@ -321,6 +343,12 @@ class CoxRegression : public ObjFunction { const char* DefaultEvalMetric() const override { return "cox-nloglik"; } + + void SaveConfig(Json* p_out) const override { + auto& out = *p_out; + out["name"] = String("survival:cox"); + } + void LoadConfig(Json const&) override {} }; // register the objective function @@ -391,6 +419,11 @@ class GammaRegression : public ObjFunction { const char* DefaultEvalMetric() const override { return "gamma-nloglik"; } + void SaveConfig(Json* p_out) const override { + auto& out = *p_out; + out["name"] = String("reg:gamma"); + } + void LoadConfig(Json const&) override {} private: HostDeviceVector label_correct_; @@ -403,7 +436,7 @@ XGBOOST_REGISTER_OBJECTIVE(GammaRegression, "reg:gamma") // declare parameter -struct TweedieRegressionParam : public dmlc::Parameter { +struct TweedieRegressionParam : public XGBoostParameter { float tweedie_variance_power; DMLC_DECLARE_PARAMETER(TweedieRegressionParam) { DMLC_DECLARE_FIELD(tweedie_variance_power).set_range(1.0f, 2.0f).set_default(1.5f) @@ -416,7 +449,7 @@ class TweedieRegression : public ObjFunction { public: // declare functions void Configure(const std::vector >& args) override { - param_.InitAllowUnknown(args); + param_.UpdateAllowUnknown(args); std::ostringstream os; os << "tweedie-nloglik@" << param_.tweedie_variance_power; metric_ = os.str(); @@ -485,6 +518,15 @@ class TweedieRegression : public ObjFunction { return metric_.c_str(); } + void SaveConfig(Json* p_out) const override { + auto& out = *p_out; + out["name"] = String("reg:tweedie"); + out["tweedie_regression_param"] = toJson(param_); + } + void LoadConfig(Json const& in) override { + fromJson(in["tweedie_regression_param"], ¶m_); + } + private: std::string metric_; TweedieRegressionParam param_; diff --git a/src/tree/tree_model.cc b/src/tree/tree_model.cc index 8b4ddd7c7..8a578cf8c 100644 --- a/src/tree/tree_model.cc +++ b/src/tree/tree_model.cc @@ -617,6 +617,35 @@ std::string RegTree::DumpModel(const FeatureMap& fmap, return result; } +void RegTree::LoadModel(dmlc::Stream* fi) { + CHECK_EQ(fi->Read(¶m, sizeof(TreeParam)), sizeof(TreeParam)); + nodes_.resize(param.num_nodes); + stats_.resize(param.num_nodes); + CHECK_NE(param.num_nodes, 0); + CHECK_EQ(fi->Read(dmlc::BeginPtr(nodes_), sizeof(Node) * nodes_.size()), + sizeof(Node) * nodes_.size()); + CHECK_EQ(fi->Read(dmlc::BeginPtr(stats_), sizeof(RTreeNodeStat) * stats_.size()), + sizeof(RTreeNodeStat) * stats_.size()); + // chg deleted nodes + deleted_nodes_.resize(0); + for (int i = param.num_roots; i < param.num_nodes; ++i) { + if (nodes_[i].IsDeleted()) deleted_nodes_.push_back(i); + } + CHECK_EQ(static_cast(deleted_nodes_.size()), param.num_deleted); +} +/*! + * \brief save model to stream + * \param fo output stream + */ +void RegTree::SaveModel(dmlc::Stream* fo) const { + CHECK_EQ(param.num_nodes, static_cast(nodes_.size())); + CHECK_EQ(param.num_nodes, static_cast(stats_.size())); + fo->Write(¶m, sizeof(TreeParam)); + CHECK_NE(param.num_nodes, 0); + fo->Write(dmlc::BeginPtr(nodes_), sizeof(Node) * nodes_.size()); + fo->Write(dmlc::BeginPtr(stats_), sizeof(RTreeNodeStat) * nodes_.size()); +} + void RegTree::FillNodeMeanValues() { size_t num_nodes = this->param.num_nodes; if (this->node_mean_values_.size() == num_nodes) { diff --git a/src/tree/updater_gpu_hist.cu b/src/tree/updater_gpu_hist.cu index cbc3b12bb..10eda9ce5 100755 --- a/src/tree/updater_gpu_hist.cu +++ b/src/tree/updater_gpu_hist.cu @@ -1053,12 +1053,12 @@ class GPUHistMakerSpecialised { common::MemoryBufferStream fs(&s_model); int rank = rabit::GetRank(); if (rank == 0) { - local_trees.front().Save(&fs); + local_trees.front().SaveModel(&fs); } fs.Seek(0); rabit::Broadcast(&s_model, 0); RegTree reference_tree{}; - reference_tree.Load(&fs); + reference_tree.LoadModel(&fs); for (const auto& tree : local_trees) { CHECK(tree == reference_tree); } diff --git a/src/tree/updater_sync.cc b/src/tree/updater_sync.cc index c4f976453..be4e0d02a 100644 --- a/src/tree/updater_sync.cc +++ b/src/tree/updater_sync.cc @@ -35,13 +35,13 @@ class TreeSyncher: public TreeUpdater { int rank = rabit::GetRank(); if (rank == 0) { for (auto tree : trees) { - tree->Save(&fs); + tree->SaveModel(&fs); } } fs.Seek(0); rabit::Broadcast(&s_model, 0); for (auto tree : trees) { - tree->Load(&fs); + tree->LoadModel(&fs); } } }; diff --git a/tests/cpp/common/test_enum_class_param.cc b/tests/cpp/common/test_parameter.cc similarity index 97% rename from tests/cpp/common/test_enum_class_param.cc rename to tests/cpp/common/test_parameter.cc index d224899d6..00177151f 100644 --- a/tests/cpp/common/test_enum_class_param.cc +++ b/tests/cpp/common/test_parameter.cc @@ -1,6 +1,6 @@ #include #include -#include +#include enum class Foo : int { kBar = 0, kFrog = 1, kCat = 2, kDog = 3 diff --git a/tests/cpp/helpers.cc b/tests/cpp/helpers.cc index 3bd194fca..d16ab4647 100644 --- a/tests/cpp/helpers.cc +++ b/tests/cpp/helpers.cc @@ -3,6 +3,10 @@ */ #include #include +#include + +#include + #include #include #include "./helpers.h" @@ -36,7 +40,7 @@ void CreateBigTestData(const std::string& filename, size_t n_entries) { } } -void CheckObjFunctionImpl(xgboost::ObjFunction * obj, +void CheckObjFunctionImpl(std::unique_ptr const& obj, std::vector preds, std::vector labels, std::vector weights, @@ -59,7 +63,7 @@ void CheckObjFunctionImpl(xgboost::ObjFunction * obj, } } -void CheckObjFunction(xgboost::ObjFunction * obj, +void CheckObjFunction(std::unique_ptr const& obj, std::vector preds, std::vector labels, std::vector weights, @@ -73,13 +77,33 @@ void CheckObjFunction(xgboost::ObjFunction * obj, CheckObjFunctionImpl(obj, preds, labels, weights, info, out_grad, out_hess); } -void CheckRankingObjFunction(xgboost::ObjFunction * obj, - std::vector preds, - std::vector labels, - std::vector weights, - std::vector groups, - std::vector out_grad, - std::vector out_hess) { +xgboost::Json CheckConfigReloadImpl(xgboost::Configurable* const configurable, + std::string name) { + xgboost::Json config_0 { xgboost::Object() }; + configurable->SaveConfig(&config_0); + configurable->LoadConfig(config_0); + + xgboost::Json config_1 { xgboost::Object() }; + configurable->SaveConfig(&config_1); + + std::string str_0, str_1; + xgboost::Json::Dump(config_0, &str_0); + xgboost::Json::Dump(config_1, &str_1); + EXPECT_EQ(str_0, str_1); + + if (name != "") { + EXPECT_EQ(xgboost::get(config_1["name"]), name); + } + return config_1; +} + +void CheckRankingObjFunction(std::unique_ptr const& obj, + std::vector preds, + std::vector labels, + std::vector weights, + std::vector groups, + std::vector out_grad, + std::vector out_hess) { xgboost::MetaInfo info; info.num_row_ = labels.size(); info.labels_.HostVector() = labels; diff --git a/tests/cpp/helpers.h b/tests/cpp/helpers.h index 690bcb62f..a3ed85cff 100644 --- a/tests/cpp/helpers.h +++ b/tests/cpp/helpers.h @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -46,20 +47,30 @@ void CreateSimpleTestData(const std::string& filename); void CreateBigTestData(const std::string& filename, size_t n_entries); -void CheckObjFunction(xgboost::ObjFunction * obj, +void CheckObjFunction(std::unique_ptr const& obj, std::vector preds, std::vector labels, std::vector weights, std::vector out_grad, std::vector out_hess); -void CheckRankingObjFunction(xgboost::ObjFunction * obj, - std::vector preds, - std::vector labels, - std::vector weights, - std::vector groups, - std::vector out_grad, - std::vector out_hess); +xgboost::Json CheckConfigReloadImpl(xgboost::Configurable* const configurable, + std::string name); + +template +xgboost::Json CheckConfigReload(std::unique_ptr const& configurable, + std::string name = "") { + return CheckConfigReloadImpl(dynamic_cast(configurable.get()), + name); +} + +void CheckRankingObjFunction(std::unique_ptr const& obj, + std::vector preds, + std::vector labels, + std::vector weights, + std::vector groups, + std::vector out_grad, + std::vector out_hess); xgboost::bst_float GetMetricEval( xgboost::Metric * metric, diff --git a/tests/cpp/objective/test_hinge.cc b/tests/cpp/objective/test_hinge.cc index fb9b24daa..ec54d69aa 100644 --- a/tests/cpp/objective/test_hinge.cc +++ b/tests/cpp/objective/test_hinge.cc @@ -7,7 +7,9 @@ TEST(Objective, DeclareUnifiedTest(HingeObj)) { xgboost::GenericParameter tparam = xgboost::CreateEmptyGenericParam(GPUIDX); - xgboost::ObjFunction * obj = xgboost::ObjFunction::Create("binary:hinge", &tparam); + std::unique_ptr obj { + xgboost::ObjFunction::Create("binary:hinge", &tparam) + }; xgboost::bst_float eps = std::numeric_limits::min(); CheckObjFunction(obj, @@ -24,6 +26,4 @@ TEST(Objective, DeclareUnifiedTest(HingeObj)) { { eps, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, eps }); ASSERT_NO_THROW(obj->DefaultEvalMetric()); - - delete obj; } diff --git a/tests/cpp/objective/test_multiclass_obj.cc b/tests/cpp/objective/test_multiclass_obj.cc index 77d38f09a..de53dee6a 100644 --- a/tests/cpp/objective/test_multiclass_obj.cc +++ b/tests/cpp/objective/test_multiclass_obj.cc @@ -6,12 +6,18 @@ #include "../../src/common/common.h" #include "../helpers.h" +namespace xgboost { + TEST(Objective, DeclareUnifiedTest(SoftmaxMultiClassObjGPair)) { - xgboost::GenericParameter lparam = xgboost::CreateEmptyGenericParam(GPUIDX); + GenericParameter lparam = CreateEmptyGenericParam(GPUIDX); std::vector> args {{"num_class", "3"}}; - xgboost::ObjFunction * obj = xgboost::ObjFunction::Create("multi:softmax", &lparam); + std::unique_ptr obj { + ObjFunction::Create("multi:softmax", &lparam) + }; obj->Configure(args); + CheckConfigReload(obj, "multi:softmax"); + CheckObjFunction(obj, {1.0f, 0.0f, 2.0f, 2.0f, 0.0f, 1.0f}, // preds {1.0f, 0.0f}, // labels @@ -20,21 +26,20 @@ TEST(Objective, DeclareUnifiedTest(SoftmaxMultiClassObjGPair)) { {0.36f, 0.16f, 0.44f, 0.45f, 0.16f, 0.37f}); // hess ASSERT_NO_THROW(obj->DefaultEvalMetric()); - - delete obj; } TEST(Objective, DeclareUnifiedTest(SoftmaxMultiClassBasic)) { - auto lparam = xgboost::CreateEmptyGenericParam(GPUIDX); + auto lparam = CreateEmptyGenericParam(GPUIDX); std::vector> args{ std::pair("num_class", "3")}; - xgboost::ObjFunction * obj = xgboost::ObjFunction::Create("multi:softmax", &lparam); + std::unique_ptr obj { ObjFunction::Create("multi:softmax", &lparam) }; obj->Configure(args); + CheckConfigReload(obj, "multi:softmax"); - xgboost::HostDeviceVector io_preds = {2.0f, 0.0f, 1.0f, - 1.0f, 0.0f, 2.0f}; - std::vector out_preds = {0.0f, 2.0f}; + HostDeviceVector io_preds = {2.0f, 0.0f, 1.0f, + 1.0f, 0.0f, 2.0f}; + std::vector out_preds = {0.0f, 2.0f}; obj->PredTransform(&io_preds); auto& preds = io_preds.HostVector(); @@ -42,20 +47,21 @@ TEST(Objective, DeclareUnifiedTest(SoftmaxMultiClassBasic)) { for (int i = 0; i < static_cast(io_preds.Size()); ++i) { EXPECT_NEAR(preds[i], out_preds[i], 0.01f); } - - delete obj; } TEST(Objective, DeclareUnifiedTest(SoftprobMultiClassBasic)) { - xgboost::GenericParameter lparam = xgboost::CreateEmptyGenericParam(GPUIDX); + GenericParameter lparam = CreateEmptyGenericParam(GPUIDX); std::vector> args { std::pair("num_class", "3")}; - xgboost::ObjFunction * obj = xgboost::ObjFunction::Create("multi:softprob", &lparam); + std::unique_ptr obj { + ObjFunction::Create("multi:softprob", &lparam) + }; obj->Configure(args); + CheckConfigReload(obj, "multi:softprob"); - xgboost::HostDeviceVector io_preds = {2.0f, 0.0f, 1.0f}; - std::vector out_preds = {0.66524096f, 0.09003057f, 0.24472847f}; + HostDeviceVector io_preds = {2.0f, 0.0f, 1.0f}; + std::vector out_preds = {0.66524096f, 0.09003057f, 0.24472847f}; obj->PredTransform(&io_preds); auto& preds = io_preds.HostVector(); @@ -63,5 +69,5 @@ TEST(Objective, DeclareUnifiedTest(SoftprobMultiClassBasic)) { for (int i = 0; i < static_cast(io_preds.Size()); ++i) { EXPECT_NEAR(preds[i], out_preds[i], 0.01f); } - delete obj; } +} // namespace xgboost diff --git a/tests/cpp/objective/test_ranking_obj.cc b/tests/cpp/objective/test_ranking_obj.cc index f9410b9ee..7446e8e23 100644 --- a/tests/cpp/objective/test_ranking_obj.cc +++ b/tests/cpp/objective/test_ranking_obj.cc @@ -2,33 +2,59 @@ #include #include #include "../helpers.h" +#include + +namespace xgboost { TEST(Objective, PairwiseRankingGPair) { xgboost::GenericParameter tparam; std::vector> args; tparam.InitAllowUnknown(args); - xgboost::ObjFunction * obj = - xgboost::ObjFunction::Create("rank:pairwise", &tparam); + std::unique_ptr obj { + xgboost::ObjFunction::Create("rank:pairwise", &tparam) + }; obj->Configure(args); + CheckConfigReload(obj, "rank:pairwise"); + // Test with setting sample weight to second query group CheckRankingObjFunction(obj, - {0, 0.1f, 0, 0.1f}, - {0, 1, 0, 1}, - {2.0f, 0.0f}, - {0, 2, 4}, - {1.9f, -1.9f, 0.0f, 0.0f}, - {1.995f, 1.995f, 0.0f, 0.0f}); + {0, 0.1f, 0, 0.1f}, + {0, 1, 0, 1}, + {2.0f, 0.0f}, + {0, 2, 4}, + {1.9f, -1.9f, 0.0f, 0.0f}, + {1.995f, 1.995f, 0.0f, 0.0f}); CheckRankingObjFunction(obj, - {0, 0.1f, 0, 0.1f}, - {0, 1, 0, 1}, - {1.0f, 1.0f}, - {0, 2, 4}, - {0.95f, -0.95f, 0.95f, -0.95f}, - {0.9975f, 0.9975f, 0.9975f, 0.9975f}); + {0, 0.1f, 0, 0.1f}, + {0, 1, 0, 1}, + {1.0f, 1.0f}, + {0, 2, 4}, + {0.95f, -0.95f, 0.95f, -0.95f}, + {0.9975f, 0.9975f, 0.9975f, 0.9975f}); ASSERT_NO_THROW(obj->DefaultEvalMetric()); - - delete obj; } + +TEST(Objective, NDCG_Json_IO) { + xgboost::GenericParameter tparam; + tparam.InitAllowUnknown(Args{}); + + std::unique_ptr obj { + xgboost::ObjFunction::Create("rank:ndcg", &tparam) + }; + + obj->Configure(Args{}); + Json j_obj {Object()}; + obj->SaveConfig(&j_obj); + + ASSERT_EQ(get(j_obj["name"]), "rank:ndcg");; + + auto const& j_param = j_obj["lambda_rank_param"]; + + ASSERT_EQ(get(j_param["num_pairsample"]), "1"); + ASSERT_EQ(get(j_param["fix_list_weight"]), "0"); +} + +} // namespace xgboost diff --git a/tests/cpp/objective/test_regression_obj.cc b/tests/cpp/objective/test_regression_obj.cc index bcf2bd497..2ead20e76 100644 --- a/tests/cpp/objective/test_regression_obj.cc +++ b/tests/cpp/objective/test_regression_obj.cc @@ -4,14 +4,17 @@ #include #include #include +#include #include "../helpers.h" +namespace xgboost { TEST(Objective, DeclareUnifiedTest(LinearRegressionGPair)) { - xgboost::GenericParameter tparam = xgboost::CreateEmptyGenericParam(GPUIDX); + GenericParameter tparam = CreateEmptyGenericParam(GPUIDX); std::vector> args; - xgboost::ObjFunction * obj = - xgboost::ObjFunction::Create("reg:squarederror", &tparam); + std::unique_ptr obj { + ObjFunction::Create("reg:squarederror", &tparam) + }; obj->Configure(args); CheckObjFunction(obj, @@ -27,17 +30,15 @@ TEST(Objective, DeclareUnifiedTest(LinearRegressionGPair)) { {0, 0.1f, 0.9f, 1.0f, -1.0f, -0.9f, -0.1f, 0}, {1, 1, 1, 1, 1, 1, 1, 1}); ASSERT_NO_THROW(obj->DefaultEvalMetric()); - - delete obj; } TEST(Objective, DeclareUnifiedTest(SquaredLog)) { - xgboost::GenericParameter tparam = xgboost::CreateEmptyGenericParam(GPUIDX); + GenericParameter tparam = CreateEmptyGenericParam(GPUIDX); std::vector> args; - xgboost::ObjFunction * obj = - xgboost::ObjFunction::Create("reg:squaredlogerror", &tparam); + std::unique_ptr obj { ObjFunction::Create("reg:squaredlogerror", &tparam) }; obj->Configure(args); + CheckConfigReload(obj, "reg:squaredlogerror"); CheckObjFunction(obj, {0.1f, 0.2f, 0.4f, 0.8f, 1.6f}, // pred @@ -52,31 +53,33 @@ TEST(Objective, DeclareUnifiedTest(SquaredLog)) { {-0.5435f, -0.4257f, -0.25475f, -0.05855f, 0.1009f}, { 1.3205f, 1.0492f, 0.69215f, 0.34115f, 0.1091f}); ASSERT_EQ(obj->DefaultEvalMetric(), std::string{"rmsle"}); - delete obj; } TEST(Objective, DeclareUnifiedTest(LogisticRegressionGPair)) { - xgboost::GenericParameter tparam = xgboost::CreateEmptyGenericParam(GPUIDX); + GenericParameter tparam = CreateEmptyGenericParam(GPUIDX); std::vector> args; - xgboost::ObjFunction * obj = xgboost::ObjFunction::Create("reg:logistic", &tparam); + std::unique_ptr obj { ObjFunction::Create("reg:logistic", &tparam) }; obj->Configure(args); + CheckConfigReload(obj, "reg:logistic"); + CheckObjFunction(obj, { 0, 0.1f, 0.9f, 1, 0, 0.1f, 0.9f, 1}, // preds { 0, 0, 0, 0, 1, 1, 1, 1}, // labels { 1, 1, 1, 1, 1, 1, 1, 1}, // weights { 0.5f, 0.52f, 0.71f, 0.73f, -0.5f, -0.47f, -0.28f, -0.26f}, // out_grad {0.25f, 0.24f, 0.20f, 0.19f, 0.25f, 0.24f, 0.20f, 0.19f}); // out_hess - - delete obj; } TEST(Objective, DeclareUnifiedTest(LogisticRegressionBasic)) { - xgboost::GenericParameter lparam = xgboost::CreateEmptyGenericParam(GPUIDX); + GenericParameter lparam = CreateEmptyGenericParam(GPUIDX); std::vector> args; - xgboost::ObjFunction * obj = xgboost::ObjFunction::Create("reg:logistic", &lparam); + std::unique_ptr obj { + ObjFunction::Create("reg:logistic", &lparam) + }; obj->Configure(args); + CheckConfigReload(obj, "reg:logistic"); // test label validation EXPECT_ANY_THROW(CheckObjFunction(obj, {0}, {10}, {1}, {0}, {0})) @@ -90,40 +93,42 @@ TEST(Objective, DeclareUnifiedTest(LogisticRegressionBasic)) { << "Expected error when base_score not in range [0,1f] for LogisticRegression"; // test PredTransform - xgboost::HostDeviceVector io_preds = {0, 0.1f, 0.5f, 0.9f, 1}; - std::vector out_preds = {0.5f, 0.524f, 0.622f, 0.710f, 0.731f}; + HostDeviceVector io_preds = {0, 0.1f, 0.5f, 0.9f, 1}; + std::vector out_preds = {0.5f, 0.524f, 0.622f, 0.710f, 0.731f}; obj->PredTransform(&io_preds); auto& preds = io_preds.HostVector(); for (int i = 0; i < static_cast(io_preds.Size()); ++i) { EXPECT_NEAR(preds[i], out_preds[i], 0.01f); } - - delete obj; } TEST(Objective, DeclareUnifiedTest(LogisticRawGPair)) { - xgboost::GenericParameter lparam = xgboost::CreateEmptyGenericParam(GPUIDX); + GenericParameter lparam = CreateEmptyGenericParam(GPUIDX); std::vector> args; - xgboost::ObjFunction * obj = xgboost::ObjFunction::Create("binary:logitraw", &lparam); + std::unique_ptr obj { + ObjFunction::Create("binary:logitraw", &lparam) + }; obj->Configure(args); + CheckObjFunction(obj, { 0, 0.1f, 0.9f, 1, 0, 0.1f, 0.9f, 1}, { 0, 0, 0, 0, 1, 1, 1, 1}, { 1, 1, 1, 1, 1, 1, 1, 1}, { 0.5f, 0.52f, 0.71f, 0.73f, -0.5f, -0.47f, -0.28f, -0.26f}, {0.25f, 0.24f, 0.20f, 0.19f, 0.25f, 0.24f, 0.20f, 0.19f}); - - delete obj; } TEST(Objective, DeclareUnifiedTest(PoissonRegressionGPair)) { - xgboost::GenericParameter lparam = xgboost::CreateEmptyGenericParam(GPUIDX); + GenericParameter lparam = CreateEmptyGenericParam(GPUIDX); std::vector> args; - xgboost::ObjFunction * obj = xgboost::ObjFunction::Create("count:poisson", &lparam); + std::unique_ptr obj { + ObjFunction::Create("count:poisson", &lparam) + }; args.emplace_back(std::make_pair("max_delta_step", "0.1f")); obj->Configure(args); + CheckObjFunction(obj, { 0, 0.1f, 0.9f, 1, 0, 0.1f, 0.9f, 1}, { 0, 0, 0, 0, 1, 1, 1, 1}, @@ -136,15 +141,17 @@ TEST(Objective, DeclareUnifiedTest(PoissonRegressionGPair)) { {}, // Empty weight { 1, 1.10f, 2.45f, 2.71f, 0, 0.10f, 1.45f, 1.71f}, {1.10f, 1.22f, 2.71f, 3.00f, 1.10f, 1.22f, 2.71f, 3.00f}); - delete obj; } TEST(Objective, DeclareUnifiedTest(PoissonRegressionBasic)) { - xgboost::GenericParameter lparam = xgboost::CreateEmptyGenericParam(GPUIDX); + GenericParameter lparam = CreateEmptyGenericParam(GPUIDX); std::vector> args; - xgboost::ObjFunction * obj = xgboost::ObjFunction::Create("count:poisson", &lparam); + std::unique_ptr obj { + ObjFunction::Create("count:poisson", &lparam) + }; obj->Configure(args); + CheckConfigReload(obj, "count:poisson"); // test label validation EXPECT_ANY_THROW(CheckObjFunction(obj, {0}, {-1}, {1}, {0}, {0})) @@ -156,21 +163,21 @@ TEST(Objective, DeclareUnifiedTest(PoissonRegressionBasic)) { EXPECT_NEAR(obj->ProbToMargin(0.9f), -0.10f, 0.01f); // test PredTransform - xgboost::HostDeviceVector io_preds = {0, 0.1f, 0.5f, 0.9f, 1}; - std::vector out_preds = {1, 1.10f, 1.64f, 2.45f, 2.71f}; + HostDeviceVector io_preds = {0, 0.1f, 0.5f, 0.9f, 1}; + std::vector out_preds = {1, 1.10f, 1.64f, 2.45f, 2.71f}; obj->PredTransform(&io_preds); auto& preds = io_preds.HostVector(); for (int i = 0; i < static_cast(io_preds.Size()); ++i) { EXPECT_NEAR(preds[i], out_preds[i], 0.01f); } - - delete obj; } TEST(Objective, DeclareUnifiedTest(GammaRegressionGPair)) { - xgboost::GenericParameter lparam = xgboost::CreateEmptyGenericParam(GPUIDX); + GenericParameter lparam = CreateEmptyGenericParam(GPUIDX); std::vector> args; - xgboost::ObjFunction * obj = xgboost::ObjFunction::Create("reg:gamma", &lparam); + std::unique_ptr obj { + ObjFunction::Create("reg:gamma", &lparam) + }; obj->Configure(args); CheckObjFunction(obj, @@ -185,15 +192,17 @@ TEST(Objective, DeclareUnifiedTest(GammaRegressionGPair)) { {}, // Empty weight {1, 1, 1, 1, 0, 0.09f, 0.59f, 0.63f}, {0, 0, 0, 0, 1, 0.90f, 0.40f, 0.36f}); - delete obj; } TEST(Objective, DeclareUnifiedTest(GammaRegressionBasic)) { - xgboost::GenericParameter lparam = xgboost::CreateEmptyGenericParam(GPUIDX); + GenericParameter lparam = CreateEmptyGenericParam(GPUIDX); std::vector> args; - xgboost::ObjFunction * obj = xgboost::ObjFunction::Create("reg:gamma", &lparam); + std::unique_ptr obj { + ObjFunction::Create("reg:gamma", &lparam) + }; obj->Configure(args); + CheckConfigReload(obj, "reg:gamma"); // test label validation EXPECT_ANY_THROW(CheckObjFunction(obj, {0}, {-1}, {1}, {0}, {0})) @@ -205,24 +214,25 @@ TEST(Objective, DeclareUnifiedTest(GammaRegressionBasic)) { EXPECT_NEAR(obj->ProbToMargin(0.9f), -0.10f, 0.01f); // test PredTransform - xgboost::HostDeviceVector io_preds = {0, 0.1f, 0.5f, 0.9f, 1}; - std::vector out_preds = {1, 1.10f, 1.64f, 2.45f, 2.71f}; + HostDeviceVector io_preds = {0, 0.1f, 0.5f, 0.9f, 1}; + std::vector out_preds = {1, 1.10f, 1.64f, 2.45f, 2.71f}; obj->PredTransform(&io_preds); auto& preds = io_preds.HostVector(); for (int i = 0; i < static_cast(io_preds.Size()); ++i) { EXPECT_NEAR(preds[i], out_preds[i], 0.01f); } - - delete obj; } TEST(Objective, DeclareUnifiedTest(TweedieRegressionGPair)) { - xgboost::GenericParameter lparam = xgboost::CreateEmptyGenericParam(GPUIDX); + GenericParameter lparam = CreateEmptyGenericParam(GPUIDX); std::vector> args; - xgboost::ObjFunction * obj = xgboost::ObjFunction::Create("reg:tweedie", &lparam); + std::unique_ptr obj { + ObjFunction::Create("reg:tweedie", &lparam) + }; args.emplace_back(std::make_pair("tweedie_variance_power", "1.1f")); obj->Configure(args); + CheckObjFunction(obj, { 0, 0.1f, 0.9f, 1, 0, 0.1f, 0.9f, 1}, { 0, 0, 0, 0, 1, 1, 1, 1}, @@ -236,22 +246,21 @@ TEST(Objective, DeclareUnifiedTest(TweedieRegressionGPair)) { { 1, 1.09f, 2.24f, 2.45f, 0, 0.10f, 1.33f, 1.55f}, {0.89f, 0.98f, 2.02f, 2.21f, 1, 1.08f, 2.11f, 2.30f}); ASSERT_EQ(obj->DefaultEvalMetric(), std::string{"tweedie-nloglik@1.1"}); - delete obj; } #if defined(__CUDACC__) TEST(Objective, CPU_vs_CUDA) { - xgboost::GenericParameter lparam = xgboost::CreateEmptyGenericParam(GPUIDX); + GenericParameter lparam = CreateEmptyGenericParam(GPUIDX); - xgboost::ObjFunction * obj = - xgboost::ObjFunction::Create("reg:squarederror", &lparam); - xgboost::HostDeviceVector cpu_out_preds; - xgboost::HostDeviceVector cuda_out_preds; + ObjFunction * obj = + ObjFunction::Create("reg:squarederror", &lparam); + HostDeviceVector cpu_out_preds; + HostDeviceVector cuda_out_preds; constexpr size_t kRows = 400; constexpr size_t kCols = 100; - auto ppdmat = xgboost::CreateDMatrix(kRows, kCols, 0, 0); - xgboost::HostDeviceVector preds; + auto ppdmat = CreateDMatrix(kRows, kCols, 0, 0); + HostDeviceVector preds; preds.Resize(kRows); auto& h_preds = preds.HostVector(); for (size_t i = 0; i < h_preds.size(); ++i) { @@ -285,8 +294,8 @@ TEST(Objective, CPU_vs_CUDA) { sgrad += std::pow(h_cpu_out[i].GetGrad() - h_cuda_out[i].GetGrad(), 2); shess += std::pow(h_cpu_out[i].GetHess() - h_cuda_out[i].GetHess(), 2); } - ASSERT_NEAR(sgrad, 0.0f, xgboost::kRtEps); - ASSERT_NEAR(shess, 0.0f, xgboost::kRtEps); + ASSERT_NEAR(sgrad, 0.0f, kRtEps); + ASSERT_NEAR(shess, 0.0f, kRtEps); delete ppdmat; delete obj; @@ -294,11 +303,14 @@ TEST(Objective, CPU_vs_CUDA) { #endif TEST(Objective, DeclareUnifiedTest(TweedieRegressionBasic)) { - xgboost::GenericParameter lparam = xgboost::CreateEmptyGenericParam(GPUIDX); + GenericParameter lparam = CreateEmptyGenericParam(GPUIDX); std::vector> args; - xgboost::ObjFunction * obj = xgboost::ObjFunction::Create("reg:tweedie", &lparam); + std::unique_ptr obj { + ObjFunction::Create("reg:tweedie", &lparam) + }; obj->Configure(args); + CheckConfigReload(obj, "reg:tweedie"); // test label validation EXPECT_ANY_THROW(CheckObjFunction(obj, {0}, {-1}, {1}, {0}, {0})) @@ -310,25 +322,23 @@ TEST(Objective, DeclareUnifiedTest(TweedieRegressionBasic)) { EXPECT_NEAR(obj->ProbToMargin(0.9f), -0.10f, 0.01f); // test PredTransform - xgboost::HostDeviceVector io_preds = {0, 0.1f, 0.5f, 0.9f, 1}; - std::vector out_preds = {1, 1.10f, 1.64f, 2.45f, 2.71f}; + HostDeviceVector io_preds = {0, 0.1f, 0.5f, 0.9f, 1}; + std::vector out_preds = {1, 1.10f, 1.64f, 2.45f, 2.71f}; obj->PredTransform(&io_preds); auto& preds = io_preds.HostVector(); for (int i = 0; i < static_cast(io_preds.Size()); ++i) { EXPECT_NEAR(preds[i], out_preds[i], 0.01f); } - - delete obj; } - // CoxRegression not implemented in GPU code, no need for testing. #if !defined(__CUDACC__) TEST(Objective, CoxRegressionGPair) { - xgboost::GenericParameter lparam = xgboost::CreateEmptyGenericParam(GPUIDX); + GenericParameter lparam = CreateEmptyGenericParam(GPUIDX); std::vector> args; - xgboost::ObjFunction * obj = - xgboost::ObjFunction::Create("survival:cox", &lparam); + std::unique_ptr obj { + ObjFunction::Create("survival:cox", &lparam) + }; obj->Configure(args); CheckObjFunction(obj, @@ -337,7 +347,7 @@ TEST(Objective, CoxRegressionGPair) { { 1, 1, 1, 1, 1, 1, 1, 1}, { 0, 0, 0, -0.799f, -0.788f, -0.590f, 0.910f, 1.006f}, { 0, 0, 0, 0.160f, 0.186f, 0.348f, 0.610f, 0.639f}); - - delete obj; } #endif + +} // namespace xgboost diff --git a/tests/cpp/tree/test_tree_model.cc b/tests/cpp/tree/test_tree_model.cc index bb7d69966..867fc22cc 100644 --- a/tests/cpp/tree/test_tree_model.cc +++ b/tests/cpp/tree/test_tree_model.cc @@ -77,7 +77,7 @@ TEST(Tree, Load) { std::unique_ptr fi(dmlc::Stream::Create(tmp_file.c_str(), "r")); xgboost::RegTree tree; - tree.Load(fi.get()); + tree.LoadModel(fi.get()); EXPECT_EQ(tree.GetDepth(1), 1); EXPECT_EQ(tree[0].SplitCond(), 0.5f); EXPECT_EQ(tree[0].SplitIndex(), 5);