Refactor configuration [Part II]. (#4577)

* Refactor configuration [Part II].

* General changes:
** Remove `Init` methods to avoid ambiguity.
** Remove `Configure(std::map<>)` to avoid redundant copying and prepare for
   parameter validation. (`std::vector` is returned from `InitAllowUnknown`).
** Add name to tree updaters for easier debugging.

* Learner changes:
** Make `LearnerImpl` the only source of configuration.

    All configurations are stored and carried out by `LearnerImpl::Configure()`.

** Remove booster in C API.

    Originally kept for "compatibility reason", but did not state why.  So here
    we just remove it.

** Add a `metric_names_` field in `LearnerImpl`.
** Remove `LazyInit`.  Configuration will always be lazy.
** Run `Configure` before every iteration.

* Predictor changes:
** Allocate both cpu and gpu predictor.
** Remove cpu_predictor from gpu_predictor.

    `GBTree` is now used to dispatch the predictor.

** Remove some GPU Predictor tests.

* IO

No IO changes.  The binary model format stability is tested by comparing
hashing value of save models between two commits
This commit is contained in:
Jiaming Yuan
2019-07-20 08:34:56 -04:00
committed by GitHub
parent ad1192e8a3
commit f0064c07ab
69 changed files with 669 additions and 761 deletions

View File

@@ -57,13 +57,13 @@ class GBLinear : public GradientBooster {
cache_[d.get()] = std::move(e);
}
}
void Configure(const std::vector<std::pair<std::string, std::string> >& cfg) override {
void Configure(const Args& cfg) override {
if (model_.weight.size() == 0) {
model_.param.InitAllowUnknown(cfg);
}
param_.InitAllowUnknown(cfg);
updater_.reset(LinearUpdater::Create(param_.updater, learner_param_));
updater_->Init(cfg);
updater_->Configure(cfg);
monitor_.Init("GBLinear");
}
void Load(dmlc::Stream* fi) override {

View File

@@ -13,7 +13,7 @@ DMLC_REGISTRY_ENABLE(::xgboost::GradientBoosterReg);
namespace xgboost {
GradientBooster* GradientBooster::Create(
const std::string& name,
LearnerTrainParam const* learner_param,
GenericParameter const* learner_param,
const std::vector<std::shared_ptr<DMatrix> >& cache_mats,
bst_float base_margin) {
auto *e = ::dmlc::Registry< ::xgboost::GradientBoosterReg>::Get()->Find(name);

View File

@@ -32,12 +32,9 @@ namespace gbm {
DMLC_REGISTRY_FILE_TAG(gbtree);
void GBTree::Configure(const std::vector<std::pair<std::string, std::string> >& cfg) {
void GBTree::Configure(const Args& cfg) {
this->cfg_ = cfg;
tparam_.InitAllowUnknown(cfg);
std::string updater_seq = tparam_.updater_seq;
ConfigureUpdaters({cfg.begin(), cfg.cend()});
model_.Configure(cfg);
@@ -46,15 +43,46 @@ void GBTree::Configure(const std::vector<std::pair<std::string, std::string> >&
model_.InitTreesToUpdate();
}
// configure predictor
predictor_ = std::unique_ptr<Predictor>(
Predictor::Create(tparam_.predictor, this->learner_param_));
predictor_->Init(cfg, cache_);
// configure predictors
if (!cpu_predictor_) {
cpu_predictor_ = std::unique_ptr<Predictor>(
Predictor::Create("cpu_predictor", this->learner_param_));
}
#if defined(XGBOOST_USE_CUDA)
if (!gpu_predictor_) {
gpu_predictor_ = std::unique_ptr<Predictor>(
Predictor::Create("gpu_predictor", this->learner_param_));
}
#endif // defined(XGBOOST_USE_CUDA)
monitor_.Init("GBTree");
configured_ = true;
}
void GBTree::PerformTreeMethodHeuristic(DMatrix* p_train,
std::map<std::string, std::string> cfg) {
// FIXME(trivialfis): This handles updaters and predictor. Because the choice of updaters
// depends on whether external memory is used and how large is dataset. We can remove the
// dependency on DMatrix once `hist` tree method can handle external memory so that we can
// make it default.
void GBTree::ConfigureWithKnownData(std::map<std::string, std::string> const& cfg, DMatrix* fmat) {
std::string updater_seq = tparam_.updater_seq;
tparam_.InitAllowUnknown(cfg);
this->PerformTreeMethodHeuristic({this->cfg_.begin(), this->cfg_.end()}, fmat);
this->ConfigureUpdaters({this->cfg_.begin(), this->cfg_.end()});
LOG(DEBUG) << "Using updaters: " << tparam_.updater_seq;
// initialize the updaters only when needed.
if (updater_seq != tparam_.updater_seq) {
this->updaters_.clear();
}
this->InitUpdater();
cpu_predictor_->Configure({cfg.cbegin(), cfg.cend()}, cache_);
#if defined(XGBOOST_USE_CUDA)
gpu_predictor_->Configure({cfg.cbegin(), cfg.cend()}, cache_);
#endif // defined(XGBOOST_USE_CUDA)
}
void GBTree::PerformTreeMethodHeuristic(std::map<std::string, std::string> const& cfg,
DMatrix* fmat) {
if (cfg.find("updater") != cfg.cend()) {
// This method is disabled when `updater` parameter is explicitly
// set, since only experts are expected to do so.
@@ -71,11 +99,11 @@ void GBTree::PerformTreeMethodHeuristic(DMatrix* p_train,
"Tree method is automatically selected to be 'approx' "
"for distributed training.";
tparam_.tree_method = TreeMethod::kApprox;
} else if (!p_train->SingleColBlock()) {
} else if (!fmat->SingleColBlock()) {
LOG(WARNING) << "Tree method is automatically set to 'approx' "
"since external-memory data matrix is used.";
tparam_.tree_method = TreeMethod::kApprox;
} else if (p_train->Info().num_row_ >= (4UL << 20UL)) {
} else if (fmat->Info().num_row_ >= (4UL << 20UL)) {
/* Choose tree_method='approx' automatically for large data matrix */
LOG(WARNING) << "Tree method is automatically selected to be "
"'approx' for faster speed. To use old behavior "
@@ -91,7 +119,7 @@ void GBTree::PerformTreeMethodHeuristic(DMatrix* p_train,
void GBTree::ConfigureUpdaters(const std::map<std::string, std::string>& cfg) {
// `updater` parameter was manually specified
if (cfg.find("updater") != cfg.cend()) {
if (cfg.find("updater") != cfg.cend()) {
LOG(WARNING) << "DANGER AHEAD: You have manually specified `updater` "
"parameter. The `tree_method` parameter will be ignored. "
"Incorrect sequence of updaters will produce undefined "
@@ -141,17 +169,9 @@ void GBTree::ConfigureUpdaters(const std::map<std::string, std::string>& cfg) {
void GBTree::DoBoost(DMatrix* p_fmat,
HostDeviceVector<GradientPair>* in_gpair,
ObjFunction* obj) {
std::string updater_seq = tparam_.updater_seq;
this->PerformTreeMethodHeuristic(p_fmat, {this->cfg_.begin(), this->cfg_.end()});
this->ConfigureUpdaters({this->cfg_.begin(), this->cfg_.end()});
LOG(DEBUG) << "Using updaters: " << tparam_.updater_seq;
// initialize the updaters only when needed.
if (updater_seq != tparam_.updater_seq) {
this->updaters_.clear();
}
std::vector<std::vector<std::unique_ptr<RegTree> > > new_trees;
const int ngroup = model_.param.num_output_group;
ConfigureWithKnownData({this->cfg_.cbegin(), this->cfg_.cend()}, p_fmat);
monitor_.Start("BoostNewTrees");
if (ngroup == 1) {
std::vector<std::unique_ptr<RegTree> > ret;
@@ -189,7 +209,7 @@ void GBTree::InitUpdater() {
std::vector<std::string> ups = common::Split(tval, ',');
for (const std::string& pstr : ups) {
std::unique_ptr<TreeUpdater> up(TreeUpdater::Create(pstr.c_str(), learner_param_));
up->Init(this->cfg_);
up->Configure(this->cfg_);
updaters_.push_back(std::move(up));
}
}
@@ -198,7 +218,6 @@ void GBTree::BoostNewTrees(HostDeviceVector<GradientPair>* gpair,
DMatrix *p_fmat,
int bst_group,
std::vector<std::unique_ptr<RegTree> >* ret) {
this->InitUpdater();
std::vector<RegTree*> new_trees;
ret->clear();
// create the trees
@@ -230,7 +249,8 @@ void GBTree::CommitModel(std::vector<std::vector<std::unique_ptr<RegTree>>>&& ne
num_new_trees += new_trees[gid].size();
model_.CommitModel(std::move(new_trees[gid]), gid);
}
predictor_->UpdatePredictionCache(model_, &updaters_, num_new_trees);
CHECK(configured_);
GetPredictor()->UpdatePredictionCache(model_, &updaters_, num_new_trees);
}
@@ -239,7 +259,7 @@ class Dart : public GBTree {
public:
explicit Dart(bst_float base_margin) : GBTree(base_margin) {}
void Configure(const std::vector<std::pair<std::string, std::string> >& cfg) override {
void Configure(const Args& cfg) override {
GBTree::Configure(cfg);
if (model_.trees.size() == 0) {
dparam_.InitAllowUnknown(cfg);

View File

@@ -154,13 +154,15 @@ class GBTree : public GradientBooster {
#endif // XGBOOST_USE_CUDA
}
void Configure(const std::vector<std::pair<std::string, std::string> >& cfg) override;
void Configure(const Args& cfg) override;
// Revise `tree_method` and `updater` parameters after seeing the training
// data matrix
void PerformTreeMethodHeuristic(DMatrix* p_train,
std::map<std::string, std::string> cfg);
void PerformTreeMethodHeuristic(std::map<std::string, std::string> const& cfg,
DMatrix* fmat);
/*! \brief Map `tree_method` parameter to `updater` parameter */
void ConfigureUpdaters(const std::map<std::string, std::string>& cfg);
void ConfigureWithKnownData(std::map<std::string, std::string> const& cfg, DMatrix* fmat);
/*! \brief Carry out one iteration of boosting */
void DoBoost(DMatrix* p_fmat,
HostDeviceVector<GradientPair>* in_gpair,
@@ -178,7 +180,7 @@ class GBTree : public GradientBooster {
this->cfg_.clear();
this->cfg_.emplace_back(std::string("num_feature"),
common::ToString(model_.param.num_feature));
common::ToString(model_.param.num_feature));
}
GBTreeTrainParam const& GetTrainParam() const {
@@ -195,37 +197,42 @@ class GBTree : public GradientBooster {
}
void PredictBatch(DMatrix* p_fmat,
HostDeviceVector<bst_float>* out_preds,
unsigned ntree_limit) override {
predictor_->PredictBatch(p_fmat, out_preds, model_, 0, ntree_limit);
HostDeviceVector<bst_float>* out_preds,
unsigned ntree_limit) override {
CHECK(configured_);
GetPredictor()->PredictBatch(p_fmat, out_preds, model_, 0, ntree_limit);
}
void PredictInstance(const SparsePage::Inst& inst,
std::vector<bst_float>* out_preds,
unsigned ntree_limit,
unsigned root_index) override {
predictor_->PredictInstance(inst, out_preds, model_,
ntree_limit, root_index);
CHECK(configured_);
cpu_predictor_->PredictInstance(inst, out_preds, model_,
ntree_limit, root_index);
}
void PredictLeaf(DMatrix* p_fmat,
std::vector<bst_float>* out_preds,
unsigned ntree_limit) override {
predictor_->PredictLeaf(p_fmat, out_preds, model_, ntree_limit);
CHECK(configured_);
cpu_predictor_->PredictLeaf(p_fmat, out_preds, model_, ntree_limit);
}
void PredictContribution(DMatrix* p_fmat,
std::vector<bst_float>* out_contribs,
unsigned ntree_limit, bool approximate, int condition,
unsigned condition_feature) override {
predictor_->PredictContribution(p_fmat, out_contribs, model_, ntree_limit, approximate);
CHECK(configured_);
cpu_predictor_->PredictContribution(p_fmat, out_contribs, model_, ntree_limit, approximate);
}
void PredictInteractionContributions(DMatrix* p_fmat,
std::vector<bst_float>* out_contribs,
unsigned ntree_limit, bool approximate) override {
predictor_->PredictInteractionContributions(p_fmat, out_contribs, model_,
ntree_limit, approximate);
CHECK(configured_);
cpu_predictor_->PredictInteractionContributions(p_fmat, out_contribs, model_,
ntree_limit, approximate);
}
std::vector<std::string> DumpModel(const FeatureMap& fmap,
@@ -244,6 +251,25 @@ class GBTree : public GradientBooster {
int bst_group,
std::vector<std::unique_ptr<RegTree> >* ret);
std::unique_ptr<Predictor> const& GetPredictor() const {
CHECK(configured_);
if (tparam_.predictor == "cpu_predictor") {
CHECK(cpu_predictor_);
return cpu_predictor_;
} else if (tparam_.predictor == "gpu_predictor") {
#if defined(XGBOOST_USE_CUDA)
CHECK(gpu_predictor_);
return gpu_predictor_;
#else
LOG(FATAL) << "XGBoost is not compiled with CUDA support.";
return cpu_predictor_;
#endif // defined(XGBOOST_USE_CUDA)
} else {
LOG(FATAL) << "Unknown predictor: " << tparam_.predictor;
return cpu_predictor_;
}
}
// commit new trees all at once
virtual void CommitModel(
std::vector<std::vector<std::unique_ptr<RegTree>>>&& new_trees);
@@ -253,13 +279,17 @@ class GBTree : public GradientBooster {
// training parameter
GBTreeTrainParam tparam_;
// ----training fields----
bool configured_ {false};
// configurations for tree
std::vector<std::pair<std::string, std::string> > cfg_;
Args cfg_;
// the updaters that can be applied to each of tree
std::vector<std::unique_ptr<TreeUpdater>> updaters_;
// Cached matrices
std::vector<std::shared_ptr<DMatrix>> cache_;
std::unique_ptr<Predictor> predictor_;
std::unique_ptr<Predictor> cpu_predictor_;
#if defined(XGBOOST_USE_CUDA)
std::unique_ptr<Predictor> gpu_predictor_;
#endif // defined(XGBOOST_USE_CUDA)
common::Monitor monitor_;
};

View File

@@ -63,7 +63,7 @@ struct GBTreeModelParam : public dmlc::Parameter<GBTreeModelParam> {
struct GBTreeModel {
explicit GBTreeModel(bst_float base_margin) : base_margin(base_margin) {}
void Configure(const std::vector<std::pair<std::string, std::string> >& cfg) {
void Configure(const Args& cfg) {
// initialize model parameters if not yet been initialized.
if (trees.size() == 0) {
param.InitAllowUnknown(cfg);