[EM] Get quantile cuts from the extmem qdm. (#10860)

This commit is contained in:
Jiaming Yuan 2024-10-01 00:59:28 +08:00 committed by GitHub
parent 8cf2f7aed8
commit 92f1c48a22
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 35 additions and 14 deletions

View File

@ -206,7 +206,7 @@ in-core training is one additional data read when the data is dense.
To run experiments on these platforms, the open source `NVIDIA Linux driver To run experiments on these platforms, the open source `NVIDIA Linux driver
<https://developer.nvidia.com/blog/nvidia-transitions-fully-towards-open-source-gpu-kernel-modules/>`__ <https://developer.nvidia.com/blog/nvidia-transitions-fully-towards-open-source-gpu-kernel-modules/>`__
with version ``>=565.47`` is required. with version ``>=565.47`` is required, it should come with CTK 12.7 and later versions.
************** **************
Best Practices Best Practices

View File

@ -211,6 +211,7 @@ def check_extmem_qdm(
cache="cache", cache="cache",
on_host=on_host, on_host=on_host,
) )
Xy_it = xgb.ExtMemQuantileDMatrix(it) Xy_it = xgb.ExtMemQuantileDMatrix(it)
with pytest.raises(ValueError, match="Only the `hist`"): with pytest.raises(ValueError, match="Only the `hist`"):
booster_it = xgb.train( booster_it = xgb.train(
@ -227,12 +228,10 @@ def check_extmem_qdm(
Xy = xgb.QuantileDMatrix(it) Xy = xgb.QuantileDMatrix(it)
booster = xgb.train({"device": device}, Xy, num_boost_round=8) booster = xgb.train({"device": device}, Xy, num_boost_round=8)
if device == "cpu": cut_it = Xy_it.get_quantile_cut()
# Get cuts from ellpack without CPU-GPU interpolation is not yet supported. cut = Xy.get_quantile_cut()
cut_it = Xy_it.get_quantile_cut() np.testing.assert_allclose(cut_it[0], cut[0])
cut = Xy.get_quantile_cut() np.testing.assert_allclose(cut_it[1], cut[1])
np.testing.assert_allclose(cut_it[0], cut[0])
np.testing.assert_allclose(cut_it[1], cut[1])
predt_it = booster_it.predict(Xy_it) predt_it = booster_it.predict(Xy_it)
predt = booster.predict(Xy) predt = booster.predict(Xy)

View File

@ -58,7 +58,7 @@ ExtMemQuantileDMatrix::~ExtMemQuantileDMatrix() {
} }
BatchSet<ExtSparsePage> ExtMemQuantileDMatrix::GetExtBatches(Context const *, BatchParam const &) { BatchSet<ExtSparsePage> ExtMemQuantileDMatrix::GetExtBatches(Context const *, BatchParam const &) {
LOG(FATAL) << "Not implemented"; LOG(FATAL) << "Not implemented for `ExtMemQuantileDMatrix`.";
auto begin_iter = auto begin_iter =
BatchIterator<ExtSparsePage>(new SimpleBatchIteratorImpl<ExtSparsePage>(nullptr)); BatchIterator<ExtSparsePage>(new SimpleBatchIteratorImpl<ExtSparsePage>(nullptr));
return BatchSet<ExtSparsePage>{begin_iter}; return BatchSet<ExtSparsePage>{begin_iter};
@ -121,7 +121,8 @@ BatchSet<GHistIndexMatrix> ExtMemQuantileDMatrix::GetGradientIndex(Context const
CHECK(!detail::RegenGHist(param, batch_)) << error::InconsistentMaxBin(); CHECK(!detail::RegenGHist(param, batch_)) << error::InconsistentMaxBin();
} }
CHECK(this->ghist_index_source_); CHECK(this->ghist_index_source_)
<< "The `ExtMemQuantileDMatrix` is initialized using GPU data, cannot be used for CPU.";
this->ghist_index_source_->Reset(param); this->ghist_index_source_->Reset(param);
if (!std::isnan(param.sparse_thresh) && if (!std::isnan(param.sparse_thresh) &&

View File

@ -80,7 +80,8 @@ BatchSet<EllpackPage> ExtMemQuantileDMatrix::GetEllpackBatches(Context const *,
std::visit( std::visit(
[this, param](auto &&ptr) { [this, param](auto &&ptr) {
CHECK(ptr); CHECK(ptr)
<< "The `ExtMemQuantileDMatrix` is initialized using CPU data, cannot be used for GPU.";
ptr->Reset(param); ptr->Reset(param);
}, },
this->ellpack_page_source_); this->ellpack_page_source_);

View File

@ -54,7 +54,9 @@ class ExtMemQuantileDMatrix : public QuantileDMatrix {
[[nodiscard]] bool EllpackExists() const override { [[nodiscard]] bool EllpackExists() const override {
return std::visit([](auto &&v) { return static_cast<bool>(v); }, ellpack_page_source_); return std::visit([](auto &&v) { return static_cast<bool>(v); }, ellpack_page_source_);
} }
[[nodiscard]] bool GHistIndexExists() const override { return true; } [[nodiscard]] bool GHistIndexExists() const override {
return static_cast<bool>(ghist_index_source_);
}
[[nodiscard]] BatchSet<ExtSparsePage> GetExtBatches(Context const *ctx, [[nodiscard]] BatchSet<ExtSparsePage> GetExtBatches(Context const *ctx,
BatchParam const &param) override; BatchParam const &param) override;

View File

@ -189,13 +189,13 @@ common::ColumnMatrix const &GHistIndexMatrix::Transpose() const {
bst_bin_t GHistIndexMatrix::GetGindex(size_t ridx, size_t fidx) const { bst_bin_t GHistIndexMatrix::GetGindex(size_t ridx, size_t fidx) const {
auto begin = RowIdx(ridx); auto begin = RowIdx(ridx);
if (IsDense()) { if (IsDense()) {
return static_cast<bst_bin_t>(index[begin + fidx]); return static_cast<bst_bin_t>(this->index[begin + fidx]);
} }
auto end = RowIdx(ridx + 1); auto end = RowIdx(ridx + 1);
auto const& cut_ptrs = cut.Ptrs(); auto const& cut_ptrs = cut.Ptrs();
auto f_begin = cut_ptrs[fidx]; auto f_begin = cut_ptrs[fidx];
auto f_end = cut_ptrs[fidx + 1]; auto f_end = cut_ptrs[fidx + 1];
return BinarySearchBin(begin, end, index, f_begin, f_end); return BinarySearchBin(begin, end, this->index, f_begin, f_end);
} }
float GHistIndexMatrix::GetFvalue(size_t ridx, size_t fidx, bool is_cat) const { float GHistIndexMatrix::GetFvalue(size_t ridx, size_t fidx, bool is_cat) const {

View File

@ -68,17 +68,35 @@ def test_cpu_data_iterator() -> None:
strategies.booleans(), strategies.booleans(),
) )
@settings(deadline=None, max_examples=10, print_blob=True) @settings(deadline=None, max_examples=10, print_blob=True)
@pytest.mark.filterwarnings("ignore")
def test_extmem_qdm( def test_extmem_qdm(
n_samples_per_batch: int, n_features: int, n_batches: int, on_host: bool n_samples_per_batch: int, n_features: int, n_batches: int, on_host: bool
) -> None: ) -> None:
check_extmem_qdm(n_samples_per_batch, n_features, n_batches, "cuda", on_host) check_extmem_qdm(n_samples_per_batch, n_features, n_batches, "cuda", on_host)
@pytest.mark.filterwarnings("ignore")
def test_invalid_device_extmem_qdm() -> None:
it = tm.IteratorForTest(
*tm.make_batches(16, 4, 2, use_cupy=False), cache="cache", on_host=True
)
Xy = xgb.ExtMemQuantileDMatrix(it)
with pytest.raises(ValueError, match="cannot be used for GPU"):
xgb.train({"device": "cuda"}, Xy)
it = tm.IteratorForTest(
*tm.make_batches(16, 4, 2, use_cupy=True), cache="cache", on_host=True
)
Xy = xgb.ExtMemQuantileDMatrix(it)
with pytest.raises(ValueError, match="cannot be used for CPU"):
xgb.train({"device": "cpu"}, Xy)
def test_concat_pages() -> None: def test_concat_pages() -> None:
it = tm.IteratorForTest(*tm.make_batches(64, 16, 4, use_cupy=True), cache=None) it = tm.IteratorForTest(*tm.make_batches(64, 16, 4, use_cupy=True), cache=None)
Xy = xgb.ExtMemQuantileDMatrix(it) Xy = xgb.ExtMemQuantileDMatrix(it)
with pytest.raises(ValueError, match="can not be used with concatenated pages"): with pytest.raises(ValueError, match="can not be used with concatenated pages"):
booster = xgb.train( xgb.train(
{ {
"device": "cuda", "device": "cuda",
"subsample": 0.5, "subsample": 0.5,