diff --git a/src/tree/updater_quantile_hist.cc b/src/tree/updater_quantile_hist.cc index 7d5f6efb326a..7e5955dc81f4 100644 --- a/src/tree/updater_quantile_hist.cc +++ b/src/tree/updater_quantile_hist.cc @@ -4,263 +4,160 @@ * \brief use quantized feature values to construct a tree * \author Philip Cho, Tianqi Checn, Egor Smirnov */ -#include "./updater_quantile_hist.h" - -#include -#include -#include -#include -#include -#include - -#include "common_row_partitioner.h" -#include "constraints.h" -#include "hist/evaluate_splits.h" -#include "hist/histogram.h" -#include "hist/sampler.h" -#include "param.h" -#include "xgboost/linalg.h" -#include "xgboost/logging.h" -#include "xgboost/tree_updater.h" - -namespace xgboost { -namespace tree { +#include // for max +#include // for size_t +#include // for uint32_t +#include // for unique_ptr, allocator, make_unique, make_shared +#include // for operator<<, char_traits, basic_ostream +#include // for apply +#include // for move, swap +#include // for vector + +#include "../collective/communicator-inl.h" // for Allreduce, IsDistributed +#include "../collective/communicator.h" // for Operation +#include "../common/hist_util.h" // for HistogramCuts, HistCollection +#include "../common/random.h" // for ColumnSampler +#include "../common/threading_utils.h" // for ParallelFor +#include "../common/timer.h" // for Monitor +#include "../data/gradient_index.h" // for GHistIndexMatrix +#include "common_row_partitioner.h" // for CommonRowPartitioner +#include "dmlc/registry.h" // for DMLC_REGISTRY_FILE_TAG +#include "driver.h" // for Driver +#include "hist/evaluate_splits.h" // for HistEvaluator, UpdatePredictionCacheImpl +#include "hist/expand_entry.h" // for CPUExpandEntry +#include "hist/histogram.h" // for HistogramBuilder, ConstructHistSpace +#include "hist/sampler.h" // for SampleGradient +#include "param.h" // for TrainParam, GradStats +#include "xgboost/base.h" // for GradientPair, GradientPairInternal, bst_node_t +#include "xgboost/context.h" // for Context +#include "xgboost/data.h" // for BatchIterator, BatchSet, DMatrix, MetaInfo +#include "xgboost/host_device_vector.h" // for HostDeviceVector +#include "xgboost/linalg.h" // for TensorView, MatrixView, UnravelIndex, All +#include "xgboost/logging.h" // for LogCheck_EQ, LogCheck_GE, CHECK_EQ, LOG, LOG... +#include "xgboost/span.h" // for Span, operator!=, SpanIterator +#include "xgboost/string_view.h" // for operator<< +#include "xgboost/task.h" // for ObjInfo +#include "xgboost/tree_model.h" // for RegTree, MTNotImplemented, RTreeNodeStat +#include "xgboost/tree_updater.h" // for TreeUpdater, TreeUpdaterReg, XGBOOST_REGISTE... + +namespace xgboost::tree { DMLC_REGISTRY_FILE_TAG(updater_quantile_hist); -void QuantileHistMaker::Update(TrainParam const *param, HostDeviceVector *gpair, - DMatrix *dmat, - common::Span> out_position, - const std::vector &trees) { - // build tree - const size_t n_trees = trees.size(); - if (!pimpl_) { - pimpl_.reset(new Builder(n_trees, param, dmat, *task_, ctx_)); - } - - size_t t_idx{0}; - for (auto p_tree : trees) { - auto &t_row_position = out_position[t_idx]; - this->pimpl_->UpdateTree(gpair, dmat, p_tree, &t_row_position); - ++t_idx; - } -} - -bool QuantileHistMaker::UpdatePredictionCache(const DMatrix *data, - linalg::VectorView out_preds) { - if (pimpl_) { - return pimpl_->UpdatePredictionCache(data, out_preds); - } else { - return false; - } -} - -CPUExpandEntry QuantileHistMaker::Builder::InitRoot( - DMatrix *p_fmat, RegTree *p_tree, const std::vector &gpair_h) { - CPUExpandEntry node(RegTree::kRoot, p_tree->GetDepth(0)); - - size_t page_id = 0; - auto space = ConstructHistSpace(partitioner_, {node}); - for (auto const &gidx : p_fmat->GetBatches(HistBatch(param_))) { - std::vector nodes_to_build{node}; - std::vector nodes_to_sub; - this->histogram_builder_->BuildHist(page_id, space, gidx, p_tree, - partitioner_.at(page_id).Partitions(), nodes_to_build, - nodes_to_sub, gpair_h); - ++page_id; - } - - { - GradientPairPrecise grad_stat; - if (p_fmat->IsDense()) { - /** - * Specialized code for dense data: For dense data (with no missing value), the sum - * of gradient histogram is equal to snode[nid] - */ - auto const &gmat = *(p_fmat->GetBatches(HistBatch(param_)).begin()); - std::vector const &row_ptr = gmat.cut.Ptrs(); - CHECK_GE(row_ptr.size(), 2); - uint32_t const ibegin = row_ptr[0]; - uint32_t const iend = row_ptr[1]; - auto hist = this->histogram_builder_->Histogram()[RegTree::kRoot]; - auto begin = hist.data(); - for (uint32_t i = ibegin; i < iend; ++i) { - GradientPairPrecise const &et = begin[i]; - grad_stat.Add(et.GetGrad(), et.GetHess()); - } - } else { - for (auto const &grad : gpair_h) { - grad_stat.Add(grad.GetGrad(), grad.GetHess()); - } - collective::Allreduce(reinterpret_cast(&grad_stat), 2); - } - - auto weight = evaluator_->InitRoot(GradStats{grad_stat}); - p_tree->Stat(RegTree::kRoot).sum_hess = grad_stat.GetHess(); - p_tree->Stat(RegTree::kRoot).base_weight = weight; - (*p_tree)[RegTree::kRoot].SetLeaf(param_->learning_rate * weight); - - std::vector entries{node}; - monitor_->Start("EvaluateSplits"); - auto ft = p_fmat->Info().feature_types.ConstHostSpan(); - for (auto const &gmat : p_fmat->GetBatches(HistBatch(param_))) { - evaluator_->EvaluateSplits(histogram_builder_->Histogram(), gmat.cut, ft, *p_tree, &entries); - break; - } - monitor_->Stop("EvaluateSplits"); - node = entries.front(); - } - - return node; -} - -void QuantileHistMaker::Builder::BuildHistogram(DMatrix *p_fmat, RegTree *p_tree, - std::vector const &valid_candidates, - std::vector const &gpair) { - std::vector nodes_to_build(valid_candidates.size()); - std::vector nodes_to_sub(valid_candidates.size()); - - size_t n_idx = 0; - for (auto const &c : valid_candidates) { - auto left_nidx = (*p_tree)[c.nid].LeftChild(); - auto right_nidx = (*p_tree)[c.nid].RightChild(); - auto fewer_right = c.split.right_sum.GetHess() < c.split.left_sum.GetHess(); - - auto build_nidx = left_nidx; - auto subtract_nidx = right_nidx; - if (fewer_right) { - std::swap(build_nidx, subtract_nidx); - } - nodes_to_build[n_idx] = CPUExpandEntry{build_nidx, p_tree->GetDepth(build_nidx), {}}; - nodes_to_sub[n_idx] = CPUExpandEntry{subtract_nidx, p_tree->GetDepth(subtract_nidx), {}}; - n_idx++; - } - - size_t page_id{0}; - auto space = ConstructHistSpace(partitioner_, nodes_to_build); - for (auto const &gidx : p_fmat->GetBatches(HistBatch(param_))) { - histogram_builder_->BuildHist(page_id, space, gidx, p_tree, - partitioner_.at(page_id).Partitions(), nodes_to_build, - nodes_to_sub, gpair); - ++page_id; - } -} - -void QuantileHistMaker::Builder::LeafPartition(RegTree const &tree, - common::Span gpair, - std::vector *p_out_position) { - monitor_->Start(__func__); - if (!task_.UpdateTreeLeaf()) { - return; - } - for (auto const &part : partitioner_) { - part.LeafPartition(ctx_, tree, gpair, p_out_position); - } - monitor_->Stop(__func__); -} +BatchParam HistBatch(TrainParam const *param) { return {param->max_bin, param->sparse_threshold}; } -void QuantileHistMaker::Builder::ExpandTree(DMatrix *p_fmat, RegTree *p_tree, - const std::vector &gpair_h, - HostDeviceVector *p_out_position) { +template +void UpdateTree(common::Monitor *monitor_, linalg::MatrixView gpair, + Updater *updater, DMatrix *p_fmat, TrainParam const *param, + HostDeviceVector *p_out_position, RegTree *p_tree) { monitor_->Start(__func__); + updater->InitData(p_fmat, p_tree); - Driver driver(*param_); - driver.Push(this->InitRoot(p_fmat, p_tree, gpair_h)); + Driver driver{*param}; auto const &tree = *p_tree; + driver.Push(updater->InitRoot(p_fmat, gpair, p_tree)); auto expand_set = driver.Pop(); + /** + * Note for update position + * Root: + * Not applied: No need to update position as initialization has got all the rows ordered. + * Applied: Update position is run on applied nodes so the rows are partitioned. + * Non-root: + * Not applied: That node is root of the subtree, same rule as root. + * Applied: Ditto + */ while (!expand_set.empty()) { // candidates that can be further splited. - std::vector valid_candidates; + std::vector valid_candidates; // candidaates that can be applied. - std::vector applied; - int32_t depth = expand_set.front().depth + 1; - for (auto const& candidate : expand_set) { - evaluator_->ApplyTreeSplit(candidate, p_tree); + std::vector applied; + for (auto const &candidate : expand_set) { + updater->ApplyTreeSplit(candidate, p_tree); + CHECK_GT(p_tree->LeftChild(candidate.nid), candidate.nid); applied.push_back(candidate); if (driver.IsChildValid(candidate)) { valid_candidates.emplace_back(candidate); } } - monitor_->Start("UpdatePosition"); - size_t page_id{0}; - for (auto const &page : p_fmat->GetBatches(HistBatch(param_))) { - partitioner_.at(page_id).UpdatePosition(ctx_, page, applied, p_tree); - ++page_id; - } - monitor_->Stop("UpdatePosition"); + updater->UpdatePosition(p_fmat, p_tree, applied); - std::vector best_splits; + std::vector best_splits; if (!valid_candidates.empty()) { - this->BuildHistogram(p_fmat, p_tree, valid_candidates, gpair_h); + updater->BuildHistogram(p_fmat, p_tree, valid_candidates, gpair); for (auto const &candidate : valid_candidates) { - int left_child_nidx = tree[candidate.nid].LeftChild(); - int right_child_nidx = tree[candidate.nid].RightChild(); - CPUExpandEntry l_best{left_child_nidx, depth}; - CPUExpandEntry r_best{right_child_nidx, depth}; + auto left_child_nidx = tree.LeftChild(candidate.nid); + auto right_child_nidx = tree.RightChild(candidate.nid); + ExpandEntry l_best{left_child_nidx, tree.GetDepth(left_child_nidx)}; + ExpandEntry r_best{right_child_nidx, tree.GetDepth(right_child_nidx)}; best_splits.push_back(l_best); best_splits.push_back(r_best); } - auto const &histograms = histogram_builder_->Histogram(); - auto ft = p_fmat->Info().feature_types.ConstHostSpan(); - for (auto const &gmat : p_fmat->GetBatches(HistBatch(param_))) { - evaluator_->EvaluateSplits(histograms, gmat.cut, ft, *p_tree, &best_splits); - break; - } + updater->EvaluateSplits(p_fmat, p_tree, &best_splits); } driver.Push(best_splits.begin(), best_splits.end()); expand_set = driver.Pop(); } auto &h_out_position = p_out_position->HostVector(); - this->LeafPartition(tree, gpair_h, &h_out_position); + updater->LeafPartition(tree, gpair, &h_out_position); monitor_->Stop(__func__); } -void QuantileHistMaker::Builder::UpdateTree(HostDeviceVector *gpair, DMatrix *p_fmat, - RegTree *p_tree, - HostDeviceVector *p_out_position) { - monitor_->Start(__func__); - - std::vector *gpair_ptr = &(gpair->HostVector()); - // in case 'num_parallel_trees != 1' no posibility to change initial gpair - if (GetNumberOfTrees() != 1) { - gpair_local_.resize(gpair_ptr->size()); - gpair_local_ = *gpair_ptr; - gpair_ptr = &gpair_local_; +class HistBuilder { + private: + common::Monitor *monitor_; + TrainParam const *param_; + std::shared_ptr col_sampler_; + std::unique_ptr> evaluator_; + std::vector partitioner_; + + // back pointers to tree and data matrix + const RegTree *p_last_tree_{nullptr}; + DMatrix const *const p_last_fmat_{nullptr}; + + std::unique_ptr> histogram_builder_; + ObjInfo const *task_{nullptr}; + // Context for number of threads + Context const *ctx_{nullptr}; + + public: + explicit HistBuilder(Context const *ctx, std::shared_ptr column_sampler, + TrainParam const *param, DMatrix const *fmat, ObjInfo const *task, + common::Monitor *monitor) + : monitor_{monitor}, + param_{param}, + col_sampler_{std::move(column_sampler)}, + evaluator_{std::make_unique>(ctx, param, fmat->Info(), + col_sampler_)}, + p_last_fmat_(fmat), + histogram_builder_{new HistogramBuilder}, + task_{task}, + ctx_{ctx} { + monitor_->Init(__func__); } - this->InitData(p_fmat, *p_tree, gpair_ptr); - - ExpandTree(p_fmat, p_tree, *gpair_ptr, p_out_position); - monitor_->Stop(__func__); -} - -bool QuantileHistMaker::Builder::UpdatePredictionCache(DMatrix const *data, - linalg::VectorView out_preds) const { - // p_last_fmat_ is a valid pointer as long as UpdatePredictionCache() is called in - // conjunction with Update(). - if (!p_last_fmat_ || !p_last_tree_ || data != p_last_fmat_) { - return false; + bool UpdatePredictionCache(DMatrix const *data, linalg::VectorView out_preds) const { + // p_last_fmat_ is a valid pointer as long as UpdatePredictionCache() is called in + // conjunction with Update(). + if (!p_last_fmat_ || !p_last_tree_ || data != p_last_fmat_) { + return false; + } + monitor_->Start(__func__); + CHECK_EQ(out_preds.Size(), data->Info().num_row_); + UpdatePredictionCacheImpl(ctx_, p_last_tree_, partitioner_, out_preds); + monitor_->Stop(__func__); + return true; } - monitor_->Start(__func__); - CHECK_EQ(out_preds.Size(), data->Info().num_row_); - UpdatePredictionCacheImpl(ctx_, p_last_tree_, partitioner_, out_preds); - monitor_->Stop(__func__); - return true; -} - -size_t QuantileHistMaker::Builder::GetNumberOfTrees() { return n_trees_; } -void QuantileHistMaker::Builder::InitData(DMatrix *fmat, const RegTree &tree, - std::vector *gpair) { - monitor_->Start(__func__); - const auto& info = fmat->Info(); + public: + // initialize temp data structure + void InitData(DMatrix *fmat, RegTree const *p_tree) { + monitor_->Start(__func__); - { size_t page_id{0}; - int32_t n_total_bins{0}; + bst_bin_t n_total_bins{0}; partitioner_.clear(); for (auto const &page : fmat->GetBatches(HistBatch(param_))) { if (n_total_bins == 0) { @@ -273,22 +170,219 @@ void QuantileHistMaker::Builder::InitData(DMatrix *fmat, const RegTree &tree, } histogram_builder_->Reset(n_total_bins, HistBatch(param_), ctx_->Threads(), page_id, collective::IsDistributed(), fmat->IsColumnSplit()); + evaluator_ = std::make_unique>(ctx_, this->param_, fmat->Info(), + col_sampler_); + p_last_tree_ = p_tree; + } - auto m_gpair = linalg::MakeTensorView(ctx_, *gpair, gpair->size(), static_cast(1)); - SampleGradient(ctx_, *param_, m_gpair); + void EvaluateSplits(DMatrix *p_fmat, RegTree const *p_tree, + std::vector *best_splits) { + monitor_->Start(__func__); + auto const &histograms = histogram_builder_->Histogram(); + auto ft = p_fmat->Info().feature_types.ConstHostSpan(); + for (auto const &gmat : p_fmat->GetBatches(HistBatch(param_))) { + evaluator_->EvaluateSplits(histograms, gmat.cut, ft, *p_tree, best_splits); + break; + } + monitor_->Stop(__func__); } - // store a pointer to the tree - p_last_tree_ = &tree; - evaluator_.reset(new HistEvaluator{ctx_, param_, info, column_sampler_}); + void ApplyTreeSplit(CPUExpandEntry const &candidate, RegTree *p_tree) { + this->evaluator_->ApplyTreeSplit(candidate, p_tree); + } - monitor_->Stop(__func__); -} + CPUExpandEntry InitRoot(DMatrix *p_fmat, linalg::MatrixView gpair, + RegTree *p_tree) { + CPUExpandEntry node(RegTree::kRoot, p_tree->GetDepth(0)); + + size_t page_id = 0; + auto space = ConstructHistSpace(partitioner_, {node}); + for (auto const &gidx : p_fmat->GetBatches(HistBatch(param_))) { + std::vector nodes_to_build{node}; + std::vector nodes_to_sub; + this->histogram_builder_->BuildHist(page_id, space, gidx, p_tree, + partitioner_.at(page_id).Partitions(), nodes_to_build, + nodes_to_sub, gpair.Slice(linalg::All(), 0).Values()); + ++page_id; + } + + { + GradientPairPrecise grad_stat; + if (p_fmat->IsDense()) { + /** + * Specialized code for dense data: For dense data (with no missing value), the sum + * of gradient histogram is equal to snode[nid] + */ + auto const &gmat = *(p_fmat->GetBatches(HistBatch(param_)).begin()); + std::vector const &row_ptr = gmat.cut.Ptrs(); + CHECK_GE(row_ptr.size(), 2); + uint32_t const ibegin = row_ptr[0]; + uint32_t const iend = row_ptr[1]; + auto hist = this->histogram_builder_->Histogram()[RegTree::kRoot]; + auto begin = hist.data(); + for (uint32_t i = ibegin; i < iend; ++i) { + GradientPairPrecise const &et = begin[i]; + grad_stat.Add(et.GetGrad(), et.GetHess()); + } + } else { + auto gpair_h = gpair.Slice(linalg::All(), 0).Values(); + for (auto const &grad : gpair_h) { + grad_stat.Add(grad.GetGrad(), grad.GetHess()); + } + collective::Allreduce(reinterpret_cast(&grad_stat), + 2); + } + + auto weight = evaluator_->InitRoot(GradStats{grad_stat}); + p_tree->Stat(RegTree::kRoot).sum_hess = grad_stat.GetHess(); + p_tree->Stat(RegTree::kRoot).base_weight = weight; + (*p_tree)[RegTree::kRoot].SetLeaf(param_->learning_rate * weight); + + std::vector entries{node}; + monitor_->Start("EvaluateSplits"); + auto ft = p_fmat->Info().feature_types.ConstHostSpan(); + for (auto const &gmat : p_fmat->GetBatches(HistBatch(param_))) { + evaluator_->EvaluateSplits(histogram_builder_->Histogram(), gmat.cut, ft, *p_tree, + &entries); + break; + } + monitor_->Stop("EvaluateSplits"); + node = entries.front(); + } + + return node; + } + + void BuildHistogram(DMatrix *p_fmat, RegTree *p_tree, + std::vector const &valid_candidates, + linalg::MatrixView gpair) { + std::vector nodes_to_build(valid_candidates.size()); + std::vector nodes_to_sub(valid_candidates.size()); + + size_t n_idx = 0; + for (auto const &c : valid_candidates) { + auto left_nidx = (*p_tree)[c.nid].LeftChild(); + auto right_nidx = (*p_tree)[c.nid].RightChild(); + auto fewer_right = c.split.right_sum.GetHess() < c.split.left_sum.GetHess(); + + auto build_nidx = left_nidx; + auto subtract_nidx = right_nidx; + if (fewer_right) { + std::swap(build_nidx, subtract_nidx); + } + nodes_to_build[n_idx] = CPUExpandEntry{build_nidx, p_tree->GetDepth(build_nidx), {}}; + nodes_to_sub[n_idx] = CPUExpandEntry{subtract_nidx, p_tree->GetDepth(subtract_nidx), {}}; + n_idx++; + } + + size_t page_id{0}; + auto space = ConstructHistSpace(partitioner_, nodes_to_build); + for (auto const &gidx : p_fmat->GetBatches(HistBatch(param_))) { + histogram_builder_->BuildHist(page_id, space, gidx, p_tree, + partitioner_.at(page_id).Partitions(), nodes_to_build, + nodes_to_sub, gpair.Values()); + ++page_id; + } + } + + void UpdatePosition(DMatrix *p_fmat, RegTree const *p_tree, + std::vector const &applied) { + monitor_->Start(__func__); + std::size_t page_id{0}; + for (auto const &page : p_fmat->GetBatches(HistBatch(this->param_))) { + this->partitioner_.at(page_id).UpdatePosition(this->ctx_, page, applied, p_tree); + page_id++; + } + monitor_->Stop(__func__); + } + + void LeafPartition(RegTree const &tree, linalg::MatrixView gpair, + std::vector *p_out_position) { + monitor_->Start(__func__); + if (!task_->UpdateTreeLeaf()) { + return; + } + for (auto const &part : partitioner_) { + part.LeafPartition(ctx_, tree, gpair, p_out_position); + } + monitor_->Stop(__func__); + } +}; + +/*! \brief construct a tree using quantized feature values */ +class QuantileHistMaker : public TreeUpdater { + std::unique_ptr p_impl_; + std::shared_ptr column_sampler_ = + std::make_shared(); + common::Monitor monitor_; + ObjInfo const *task_; + + public: + explicit QuantileHistMaker(Context const *ctx, ObjInfo const *task) + : TreeUpdater{ctx}, task_{task} {} + void Configure(const Args &) override {} + + void LoadConfig(Json const &) override {} + void SaveConfig(Json *) const override {} + + [[nodiscard]] char const *Name() const override { return "grow_quantile_histmaker"; } + + void Update(TrainParam const *param, HostDeviceVector *gpair, DMatrix *p_fmat, + common::Span> out_position, + const std::vector &trees) override { + if (trees.front()->IsMultiTarget()) { + CHECK(param->monotone_constraints.empty()) << "monotone constraint" << MTNotImplemented(); + LOG(FATAL) << "Not implemented."; + } else { + if (!p_impl_) { + p_impl_ = + std::make_unique(ctx_, column_sampler_, param, p_fmat, task_, &monitor_); + } + } + + bst_target_t n_targets = trees.front()->NumTargets(); + auto h_gpair = + linalg::MakeTensorView(ctx_, gpair->HostSpan(), p_fmat->Info().num_row_, n_targets); + + linalg::Matrix sample_out; + auto h_sample_out = h_gpair; + auto need_copy = [&] { return trees.size() > 1 || n_targets > 1; }; + if (need_copy()) { + // allocate buffer + sample_out = decltype(sample_out){h_gpair.Shape(), ctx_->gpu_id, linalg::Order::kF}; + h_sample_out = sample_out.HostView(); + } + + for (auto tree_it = trees.begin(); tree_it != trees.end(); ++tree_it) { + if (need_copy()) { + // Copy gradient into buffer for sampling. + std::copy(linalg::cbegin(h_gpair), linalg::cend(h_gpair), linalg::begin(h_sample_out)); + } + SampleGradient(ctx_, *param, h_sample_out); + auto *h_out_position = &out_position[tree_it - trees.begin()]; + if ((*tree_it)->IsMultiTarget()) { + LOG(FATAL) << "Not implemented."; + } else { + UpdateTree(&monitor_, h_sample_out, p_impl_.get(), p_fmat, param, + h_out_position, *tree_it); + } + } + } + + bool UpdatePredictionCache(const DMatrix *data, linalg::VectorView out_preds) override { + if (p_impl_) { + return p_impl_->UpdatePredictionCache(data, out_preds); + } else { + return false; + } + } + + [[nodiscard]] bool HasNodePosition() const override { return true; } +}; XGBOOST_REGISTER_TREE_UPDATER(QuantileHistMaker, "grow_quantile_histmaker") .describe("Grow tree using quantized histogram.") .set_body([](Context const *ctx, ObjInfo const *task) { return new QuantileHistMaker(ctx, task); }); -} // namespace tree -} // namespace xgboost +} // namespace xgboost::tree diff --git a/src/tree/updater_quantile_hist.h b/src/tree/updater_quantile_hist.h deleted file mode 100644 index 138d5646affa..000000000000 --- a/src/tree/updater_quantile_hist.h +++ /dev/null @@ -1,133 +0,0 @@ -/*! - * Copyright 2017-2022 by XGBoost Contributors - * \file updater_quantile_hist.h - * \brief use quantized feature values to construct a tree - * \author Philip Cho, Tianqi Chen, Egor Smirnov - */ -#ifndef XGBOOST_TREE_UPDATER_QUANTILE_HIST_H_ -#define XGBOOST_TREE_UPDATER_QUANTILE_HIST_H_ - -#include - -#include -#include -#include -#include -#include -#include - -#include "xgboost/base.h" -#include "xgboost/data.h" -#include "xgboost/json.h" - -#include "hist/evaluate_splits.h" -#include "hist/histogram.h" -#include "hist/expand_entry.h" - -#include "common_row_partitioner.h" -#include "constraints.h" -#include "./param.h" -#include "./driver.h" -#include "../common/random.h" -#include "../common/timer.h" -#include "../common/hist_util.h" -#include "../common/row_set.h" -#include "../common/partition_builder.h" -#include "../common/column_matrix.h" - -namespace xgboost::tree { -inline BatchParam HistBatch(TrainParam const* param) { - return {param->max_bin, param->sparse_threshold}; -} - -/*! \brief construct a tree using quantized feature values */ -class QuantileHistMaker: public TreeUpdater { - public: - explicit QuantileHistMaker(Context const* ctx, ObjInfo const* task) - : TreeUpdater(ctx), task_{task} {} - void Configure(const Args&) override {} - - void Update(TrainParam const* param, HostDeviceVector* gpair, DMatrix* dmat, - common::Span> out_position, - const std::vector& trees) override; - - bool UpdatePredictionCache(const DMatrix *data, - linalg::VectorView out_preds) override; - - void LoadConfig(Json const&) override {} - void SaveConfig(Json*) const override {} - - [[nodiscard]] char const* Name() const override { return "grow_quantile_histmaker"; } - [[nodiscard]] bool HasNodePosition() const override { return true; } - - protected: - // actual builder that runs the algorithm - struct Builder { - public: - // constructor - explicit Builder(const size_t n_trees, TrainParam const* param, DMatrix const* fmat, - ObjInfo task, Context const* ctx) - : n_trees_(n_trees), - param_(param), - p_last_fmat_(fmat), - histogram_builder_{new HistogramBuilder}, - task_{task}, - ctx_{ctx}, - monitor_{std::make_unique()} { - monitor_->Init("Quantile::Builder"); - } - // update one tree, growing - void UpdateTree(HostDeviceVector* gpair, DMatrix* p_fmat, RegTree* p_tree, - HostDeviceVector* p_out_position); - - bool UpdatePredictionCache(DMatrix const* data, linalg::VectorView out_preds) const; - - private: - // initialize temp data structure - void InitData(DMatrix* fmat, const RegTree& tree, std::vector* gpair); - - size_t GetNumberOfTrees(); - - CPUExpandEntry InitRoot(DMatrix* p_fmat, RegTree* p_tree, - const std::vector& gpair_h); - - void BuildHistogram(DMatrix* p_fmat, RegTree* p_tree, - std::vector const& valid_candidates, - std::vector const& gpair); - - void LeafPartition(RegTree const& tree, common::Span gpair, - std::vector* p_out_position); - - void ExpandTree(DMatrix* p_fmat, RegTree* p_tree, const std::vector& gpair_h, - HostDeviceVector* p_out_position); - - private: - const size_t n_trees_; - TrainParam const* param_; - std::shared_ptr column_sampler_{ - std::make_shared()}; - - std::vector gpair_local_; - - std::unique_ptr> evaluator_; - std::vector partitioner_; - - // back pointers to tree and data matrix - const RegTree* p_last_tree_{nullptr}; - DMatrix const* const p_last_fmat_; - - std::unique_ptr> histogram_builder_; - ObjInfo task_; - // Context for number of threads - Context const* ctx_; - - std::unique_ptr monitor_; - }; - - protected: - std::unique_ptr pimpl_; - ObjInfo const* task_; -}; -} // namespace xgboost::tree - -#endif // XGBOOST_TREE_UPDATER_QUANTILE_HIST_H_