Fix #3342 and h2oai/h2o4gpu#625: Save predictor parameters in model file (#3856)
* Fix #3342 and h2oai/h2o4gpu#625: Save predictor parameters in model file This allows pickled models to retain predictor attributes, such as 'predictor' (whether to use CPU or GPU) and 'n_gpu' (number of GPUs to use). Related: h2oai/h2o4gpu#625 Closes #3342. TODO. Write a test. * Fix lint * Do not load GPU predictor into CPU-only XGBoost * Add a test for pickling GPU predictors * Make sample data big enough to pass multi GPU test * Update test_gpu_predictor.cu
This commit is contained in:
committed by
GitHub
parent
e04ab56b57
commit
91537e7353
@@ -2,11 +2,25 @@
|
||||
/*!
|
||||
* Copyright 2017 XGBoost contributors
|
||||
*/
|
||||
#include <dmlc/logging.h>
|
||||
#include <dmlc/filesystem.h>
|
||||
#include <xgboost/c_api.h>
|
||||
#include <xgboost/predictor.h>
|
||||
#include <string>
|
||||
#include "gtest/gtest.h"
|
||||
#include "../helpers.h"
|
||||
|
||||
namespace {
|
||||
|
||||
inline void CheckCAPICall(int ret) {
|
||||
ASSERT_EQ(ret, 0) << XGBGetLastError();
|
||||
}
|
||||
|
||||
} // namespace anonymous
|
||||
|
||||
extern const std::map<std::string, std::string>&
|
||||
QueryBoosterConfigurationArguments(BoosterHandle handle);
|
||||
|
||||
namespace xgboost {
|
||||
namespace predictor {
|
||||
|
||||
@@ -77,6 +91,80 @@ TEST(gpu_predictor, Test) {
|
||||
delete dmat;
|
||||
}
|
||||
|
||||
// Test whether pickling preserves predictor parameters
|
||||
TEST(gpu_predictor, MGPU_PicklingTest) {
|
||||
int ngpu;
|
||||
dh::safe_cuda(cudaGetDeviceCount(&ngpu));
|
||||
|
||||
dmlc::TemporaryDirectory tempdir;
|
||||
const std::string tmp_file = tempdir.path + "/simple.libsvm";
|
||||
CreateBigTestData(tmp_file, 600);
|
||||
|
||||
DMatrixHandle dmat[1];
|
||||
BoosterHandle bst, bst2;
|
||||
std::vector<bst_float> label;
|
||||
for (int i = 0; i < 200; ++i) {
|
||||
label.push_back((i % 2 ? 1 : 0));
|
||||
}
|
||||
|
||||
// Load data matrix
|
||||
CheckCAPICall(XGDMatrixCreateFromFile(tmp_file.c_str(), 0, &dmat[0]));
|
||||
CheckCAPICall(XGDMatrixSetFloatInfo(dmat[0], "label", label.data(), 200));
|
||||
// Create booster
|
||||
CheckCAPICall(XGBoosterCreate(dmat, 1, &bst));
|
||||
// Set parameters
|
||||
CheckCAPICall(XGBoosterSetParam(bst, "seed", "0"));
|
||||
CheckCAPICall(XGBoosterSetParam(bst, "base_score", "0.5"));
|
||||
CheckCAPICall(XGBoosterSetParam(bst, "booster", "gbtree"));
|
||||
CheckCAPICall(XGBoosterSetParam(bst, "learning_rate", "0.01"));
|
||||
CheckCAPICall(XGBoosterSetParam(bst, "max_depth", "8"));
|
||||
CheckCAPICall(XGBoosterSetParam(bst, "objective", "binary:logistic"));
|
||||
CheckCAPICall(XGBoosterSetParam(bst, "seed", "123"));
|
||||
CheckCAPICall(XGBoosterSetParam(bst, "tree_method", "gpu_hist"));
|
||||
CheckCAPICall(XGBoosterSetParam(bst, "n_gpus", std::to_string(ngpu).c_str()));
|
||||
CheckCAPICall(XGBoosterSetParam(bst, "predictor", "gpu_predictor"));
|
||||
|
||||
// Run boosting iterations
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
CheckCAPICall(XGBoosterUpdateOneIter(bst, i, dmat[0]));
|
||||
}
|
||||
|
||||
// Delete matrix
|
||||
CheckCAPICall(XGDMatrixFree(dmat[0]));
|
||||
|
||||
// Pickle
|
||||
const char* dptr;
|
||||
bst_ulong len;
|
||||
std::string buf;
|
||||
CheckCAPICall(XGBoosterGetModelRaw(bst, &len, &dptr));
|
||||
buf = std::string(dptr, len);
|
||||
CheckCAPICall(XGBoosterFree(bst));
|
||||
|
||||
// Unpickle
|
||||
CheckCAPICall(XGBoosterCreate(nullptr, 0, &bst2));
|
||||
CheckCAPICall(XGBoosterLoadModelFromBuffer(bst2, buf.c_str(), len));
|
||||
|
||||
{ // Query predictor
|
||||
const auto& kwargs = QueryBoosterConfigurationArguments(bst2);
|
||||
ASSERT_EQ(kwargs.at("predictor"), "gpu_predictor");
|
||||
ASSERT_EQ(kwargs.at("n_gpus"), std::to_string(ngpu).c_str());
|
||||
}
|
||||
|
||||
{ // Change n_gpus and query again
|
||||
CheckCAPICall(XGBoosterSetParam(bst2, "n_gpus", "1"));
|
||||
const auto& kwargs = QueryBoosterConfigurationArguments(bst2);
|
||||
ASSERT_EQ(kwargs.at("n_gpus"), "1");
|
||||
}
|
||||
|
||||
{ // Change predictor and query again
|
||||
CheckCAPICall(XGBoosterSetParam(bst2, "predictor", "cpu_predictor"));
|
||||
const auto& kwargs = QueryBoosterConfigurationArguments(bst2);
|
||||
ASSERT_EQ(kwargs.at("predictor"), "cpu_predictor");
|
||||
}
|
||||
|
||||
CheckCAPICall(XGBoosterFree(bst2));
|
||||
}
|
||||
|
||||
// multi-GPU predictor test
|
||||
TEST(gpu_predictor, MGPU_Test) {
|
||||
std::unique_ptr<Predictor> gpu_predictor =
|
||||
|
||||
@@ -2,20 +2,10 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include <vector>
|
||||
#include "helpers.h"
|
||||
#include "./test_learner.h"
|
||||
#include "xgboost/learner.h"
|
||||
|
||||
namespace xgboost {
|
||||
|
||||
class LearnerTestHookAdapter {
|
||||
public:
|
||||
static inline std::string GetUpdaterSequence(const Learner* learner) {
|
||||
const LearnerTestHook* hook = dynamic_cast<const LearnerTestHook*>(learner);
|
||||
CHECK(hook) << "LearnerImpl did not inherit from LearnerTestHook";
|
||||
return hook->GetUpdaterSequence();
|
||||
}
|
||||
};
|
||||
|
||||
TEST(learner, Test) {
|
||||
typedef std::pair<std::string, std::string> arg;
|
||||
auto args = {arg("tree_method", "exact")};
|
||||
@@ -35,20 +25,20 @@ TEST(learner, SelectTreeMethod) {
|
||||
|
||||
// Test if `tree_method` can be set
|
||||
learner->Configure({arg("tree_method", "approx")});
|
||||
ASSERT_EQ(LearnerTestHookAdapter::GetUpdaterSequence(learner.get()),
|
||||
ASSERT_EQ(learner->GetConfigurationArguments().at("updater"),
|
||||
"grow_histmaker,prune");
|
||||
learner->Configure({arg("tree_method", "exact")});
|
||||
ASSERT_EQ(LearnerTestHookAdapter::GetUpdaterSequence(learner.get()),
|
||||
ASSERT_EQ(learner->GetConfigurationArguments().at("updater"),
|
||||
"grow_colmaker,prune");
|
||||
learner->Configure({arg("tree_method", "hist")});
|
||||
ASSERT_EQ(LearnerTestHookAdapter::GetUpdaterSequence(learner.get()),
|
||||
ASSERT_EQ(learner->GetConfigurationArguments().at("updater"),
|
||||
"grow_fast_histmaker");
|
||||
#ifdef XGBOOST_USE_CUDA
|
||||
learner->Configure({arg("tree_method", "gpu_exact")});
|
||||
ASSERT_EQ(LearnerTestHookAdapter::GetUpdaterSequence(learner.get()),
|
||||
ASSERT_EQ(learner->GetConfigurationArguments().at("updater"),
|
||||
"grow_gpu,prune");
|
||||
learner->Configure({arg("tree_method", "gpu_hist")});
|
||||
ASSERT_EQ(LearnerTestHookAdapter::GetUpdaterSequence(learner.get()),
|
||||
ASSERT_EQ(learner->GetConfigurationArguments().at("updater"),
|
||||
"grow_gpu_hist");
|
||||
#endif
|
||||
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
/*!
|
||||
* Copyright 2018 by Contributors
|
||||
* \file test_learner.h
|
||||
* \brief Hook to access implementation class of Learner
|
||||
* \author Hyunsu Philip Cho
|
||||
*/
|
||||
|
||||
#ifndef XGBOOST_TESTS_CPP_TEST_LEARNER_H_
|
||||
#define XGBOOST_TESTS_CPP_TEST_LEARNER_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace xgboost {
|
||||
class LearnerTestHook {
|
||||
private:
|
||||
virtual std::string GetUpdaterSequence() const = 0;
|
||||
// allow friend access to C++ tests for Learner
|
||||
friend class LearnerTestHookAdapter;
|
||||
};
|
||||
} // namespace xgboost
|
||||
|
||||
#endif // XGBOOST_TESTS_CPP_TEST_LEARNER_H_
|
||||
Reference in New Issue
Block a user