xgboost/tests/cpp/predictor/test_predictor.cc
Jiaming Yuan 6601a641d7
Thread safe, inplace prediction. (#5389)
Normal prediction with DMatrix is now thread safe with locks.  Added inplace prediction is lock free thread safe.

When data is on device (cupy, cudf), the returned data is also on device.

* Implementation for numpy, csr, cudf and cupy.

* Implementation for dask.

* Remove sync in simple dmatrix.
2020-03-30 15:35:28 +08:00

136 lines
4.5 KiB
C++

/*!
* Copyright 2020 by Contributors
*/
#include <gtest/gtest.h>
#include <xgboost/predictor.h>
#include <xgboost/data.h>
#include <xgboost/host_device_vector.h>
#include <xgboost/generic_parameters.h>
#include "test_predictor.h"
#include "../helpers.h"
#include "../../../src/common/io.h"
namespace xgboost {
TEST(Predictor, PredictionCache) {
size_t constexpr kRows = 16, kCols = 4;
PredictionContainer container;
DMatrix* m;
// Add a cache that is immediately expired.
auto add_cache = [&]() {
auto p_dmat = RandomDataGenerator(kRows, kCols, 0).GenerateDMatix();
container.Cache(p_dmat, GenericParameter::kCpuId);
m = p_dmat.get();
};
add_cache();
ASSERT_EQ(container.Container().size(), 0);
add_cache();
EXPECT_ANY_THROW(container.Entry(m));
}
// Only run this test when CUDA is enabled.
void TestTrainingPrediction(size_t rows, std::string tree_method) {
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();
h_label.resize(rows);
for (size_t i = 0; i < rows; ++i) {
h_label[i] = i % kClasses;
}
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->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,
bst_row_t rows, bst_feature_t cols,
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::unique_ptr<Learner> learner {
Learner::Create({m})
};
learner->SetParam("num_parallel_tree", "4");
learner->SetParam("num_class", std::to_string(kClasses));
learner->SetParam("seed", "0");
learner->SetParam("subsample", "0.5");
learner->SetParam("gpu_id", std::to_string(device));
learner->SetParam("predictor", predictor);
for (int32_t it = 0; it < 4; ++it) {
learner->UpdateOneIter(it, m);
}
HostDeviceVector<float> *p_out_predictions_0{nullptr};
learner->InplacePredict(x, "margin", std::numeric_limits<float>::quiet_NaN(),
&p_out_predictions_0, 0, 2);
CHECK(p_out_predictions_0);
HostDeviceVector<float> predict_0 (p_out_predictions_0->Size());
predict_0.Copy(*p_out_predictions_0);
HostDeviceVector<float> *p_out_predictions_1{nullptr};
learner->InplacePredict(x, "margin", std::numeric_limits<float>::quiet_NaN(),
&p_out_predictions_1, 2, 4);
CHECK(p_out_predictions_1);
HostDeviceVector<float> predict_1 (p_out_predictions_1->Size());
predict_1.Copy(*p_out_predictions_1);
HostDeviceVector<float>* p_out_predictions{nullptr};
learner->InplacePredict(x, "margin", std::numeric_limits<float>::quiet_NaN(),
&p_out_predictions, 0, 4);
auto& h_pred = p_out_predictions->HostVector();
auto& h_pred_0 = predict_0.HostVector();
auto& h_pred_1 = predict_1.HostVector();
ASSERT_EQ(h_pred.size(), rows * kClasses);
ASSERT_EQ(h_pred.size(), h_pred_0.size());
ASSERT_EQ(h_pred.size(), h_pred_1.size());
for (size_t i = 0; i < h_pred.size(); ++i) {
// Need to remove the global bias here.
ASSERT_NEAR(h_pred[i], h_pred_0[i] + h_pred_1[i] - 0.5f, kRtEps);
}
learner->SetParam("gpu_id", "-1");
learner->Configure();
}
} // namespace xgboost