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:
Philip Hyunsu Cho
2018-11-03 21:45:38 -07:00
committed by GitHub
parent e04ab56b57
commit 91537e7353
7 changed files with 206 additions and 51 deletions

View File

@@ -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 =