Use ellpack for prediction only when sparsepage doesn't exist. (#5504)

This commit is contained in:
Jiaming Yuan
2020-04-10 12:15:46 +08:00
committed by GitHub
parent ad826e913f
commit 6671b42dd4
35 changed files with 166 additions and 116 deletions

View File

@@ -26,7 +26,7 @@ TEST(CpuPredictor, Basic) {
gbm::GBTreeModel model = CreateTestModel(&param);
auto dmat = RandomDataGenerator(kRows, kCols, 0).GenerateDMatix();
auto dmat = RandomDataGenerator(kRows, kCols, 0).GenerateDMatrix();
// Test predict batch
PredictionCacheEntry out_predictions;

View File

@@ -31,7 +31,7 @@ TEST(GPUPredictor, Basic) {
for (size_t i = 1; i < 33; i *= 2) {
int n_row = i, n_col = i;
auto dmat = RandomDataGenerator(n_row, n_col, 0).GenerateDMatix();
auto dmat = RandomDataGenerator(n_row, n_col, 0).GenerateDMatrix();
LearnerModelParam param;
param.num_feature = n_col;
@@ -58,16 +58,33 @@ TEST(GPUPredictor, Basic) {
}
TEST(GPUPredictor, EllpackBasic) {
size_t constexpr kCols {8};
for (size_t bins = 2; bins < 258; bins += 16) {
size_t rows = bins * 16;
TestPredictionFromGradientIndex<EllpackPage>("gpu_predictor", rows, bins);
TestPredictionFromGradientIndex<EllpackPage>("gpu_predictor", bins, bins);
auto p_m = RandomDataGenerator{rows, kCols, 0.0}
.Bins(bins)
.Device(0)
.GenerateDeviceDMatrix(true);
TestPredictionFromGradientIndex<EllpackPage>("gpu_predictor", rows, kCols, p_m);
TestPredictionFromGradientIndex<EllpackPage>("gpu_predictor", bins, kCols, p_m);
}
}
TEST(GPUPredictor, EllpackTraining) {
size_t constexpr kRows { 128 };
TestTrainingPrediction(kRows, "gpu_hist");
size_t constexpr kRows { 128 }, kCols { 16 }, kBins { 64 };
auto p_ellpack = RandomDataGenerator{kRows, kCols, 0.0}
.Bins(kBins)
.Device(0)
.GenerateDeviceDMatrix(true);
std::vector<HostDeviceVector<float>> storage(kCols);
auto columnar = RandomDataGenerator{kRows, kCols, 0.0}
.Device(0)
.GenerateColumnarArrayInterface(&storage);
auto adapter = data::CudfAdapter(columnar);
std::shared_ptr<DMatrix> p_full {
DMatrix::Create(&adapter, std::numeric_limits<float>::quiet_NaN(), 1)
};
TestTrainingPrediction(kRows, "gpu_hist", p_full, p_ellpack);
}
TEST(GPUPredictor, ExternalMemoryTest) {

View File

@@ -21,7 +21,7 @@ TEST(Predictor, PredictionCache) {
DMatrix* m;
// Add a cache that is immediately expired.
auto add_cache = [&]() {
auto p_dmat = RandomDataGenerator(kRows, kCols, 0).GenerateDMatix();
auto p_dmat = RandomDataGenerator(kRows, kCols, 0).GenerateDMatrix();
container.Cache(p_dmat, GenericParameter::kCpuId);
m = p_dmat.get();
};
@@ -32,17 +32,16 @@ TEST(Predictor, PredictionCache) {
EXPECT_ANY_THROW(container.Entry(m));
}
// Only run this test when CUDA is enabled.
void TestTrainingPrediction(size_t rows, std::string tree_method) {
void TestTrainingPrediction(size_t rows, std::string tree_method,
std::shared_ptr<DMatrix> p_full,
std::shared_ptr<DMatrix> p_hist) {
size_t constexpr kCols = 16;
size_t constexpr kClasses = 3;
size_t constexpr kIters = 3;
std::unique_ptr<Learner> learner;
auto train = [&](std::string predictor, HostDeviceVector<float>* out) {
auto p_m = RandomDataGenerator(rows, kCols, 0).GenerateDMatix();
auto &h_label = p_m->Info().labels_.HostVector();
auto train = [&](std::string predictor, HostDeviceVector<float> *out) {
auto &h_label = p_hist->Info().labels_.HostVector();
h_label.resize(rows);
for (size_t i = 0; i < rows; ++i) {
@@ -52,30 +51,31 @@ void TestTrainingPrediction(size_t rows, std::string tree_method) {
learner.reset(Learner::Create({}));
learner->SetParam("tree_method", tree_method);
learner->SetParam("objective", "multi:softprob");
learner->SetParam("predictor", predictor);
learner->SetParam("num_feature", std::to_string(kCols));
learner->SetParam("num_class", std::to_string(kClasses));
learner->Configure();
for (size_t i = 0; i < kIters; ++i) {
learner->UpdateOneIter(i, p_m);
learner->UpdateOneIter(i, p_hist);
}
HostDeviceVector<float> from_full;
learner->Predict(p_full, false, &from_full);
HostDeviceVector<float> from_hist;
learner->Predict(p_hist, false, &from_hist);
for (size_t i = 0; i < rows; ++i) {
EXPECT_NEAR(from_hist.ConstHostVector()[i],
from_full.ConstHostVector()[i], kRtEps);
}
learner->Predict(p_m, false, out);
};
// Alternate the predictor, CPU predictor can not use ellpack while GPU predictor can
// not use CPU histogram index. So it's guaranteed one of the following is not
// predicting from histogram index. Note: As of writing only GPU supports predicting
// from gradient index, the test is written for future portability.
HostDeviceVector<float> predictions_0;
train("cpu_predictor", &predictions_0);
HostDeviceVector<float> predictions_1;
train("gpu_predictor", &predictions_1);
for (size_t i = 0; i < rows; ++i) {
EXPECT_NEAR(predictions_1.ConstHostVector()[i],
predictions_0.ConstHostVector()[i], kRtEps);
}
}
void TestInplacePrediction(dmlc::any x, std::string predictor,
@@ -83,7 +83,7 @@ void TestInplacePrediction(dmlc::any x, std::string predictor,
int32_t device) {
size_t constexpr kClasses { 4 };
auto gen = RandomDataGenerator{rows, cols, 0.5}.Device(device);
std::shared_ptr<DMatrix> m = gen.GenerateDMatix(true, false, kClasses);
std::shared_ptr<DMatrix> m = gen.GenerateDMatrix(true, false, kClasses);
std::unique_ptr<Learner> learner {
Learner::Create({m})

View File

@@ -8,11 +8,12 @@
namespace xgboost {
template <typename Page>
void TestPredictionFromGradientIndex(std::string name, size_t rows, int32_t bins) {
constexpr size_t kCols { 8 }, kClasses { 3 };
void TestPredictionFromGradientIndex(std::string name, size_t rows, size_t cols,
std::shared_ptr<DMatrix> p_hist) {
constexpr size_t kClasses { 3 };
LearnerModelParam param;
param.num_feature = kCols;
param.num_feature = cols;
param.num_output_group = kClasses;
param.base_score = 0.5;
@@ -25,16 +26,10 @@ void TestPredictionFromGradientIndex(std::string name, size_t rows, int32_t bins
gbm::GBTreeModel model = CreateTestModel(&param, kClasses);
{
auto p_ellpack = RandomDataGenerator(rows, kCols, 0).GenerateDMatix();
// Use same number of bins as rows.
for (auto const &page DMLC_ATTRIBUTE_UNUSED :
p_ellpack->GetBatches<Page>({0, static_cast<int32_t>(bins), 0})) {
}
auto p_precise = RandomDataGenerator(rows, kCols, 0).GenerateDMatix();
auto p_precise = RandomDataGenerator(rows, cols, 0).GenerateDMatrix();
PredictionCacheEntry approx_out_predictions;
predictor->PredictBatch(p_ellpack.get(), &approx_out_predictions, model, 0);
predictor->PredictBatch(p_hist.get(), &approx_out_predictions, model, 0);
PredictionCacheEntry precise_out_predictions;
predictor->PredictBatch(p_precise.get(), &precise_out_predictions, model, 0);
@@ -49,14 +44,17 @@ void TestPredictionFromGradientIndex(std::string name, size_t rows, int32_t bins
// Predictor should never try to create the histogram index by itself. As only
// histogram index from training data is valid and predictor doesn't known which
// matrix is used for training.
auto p_dmat = RandomDataGenerator(rows, kCols, 0).GenerateDMatix();
auto p_dmat = RandomDataGenerator(rows, cols, 0).GenerateDMatrix();
PredictionCacheEntry precise_out_predictions;
predictor->PredictBatch(p_dmat.get(), &precise_out_predictions, model, 0);
ASSERT_FALSE(p_dmat->PageExists<Page>());
}
}
void TestTrainingPrediction(size_t rows, std::string tree_method);
// p_full and p_hist should come from the same data set.
void TestTrainingPrediction(size_t rows, std::string tree_method,
std::shared_ptr<DMatrix> p_full,
std::shared_ptr<DMatrix> p_hist);
void TestInplacePrediction(dmlc::any x, std::string predictor,
bst_row_t rows, bst_feature_t cols,