Replaced std::vector-based interfaces with HostDeviceVector-based interfaces. (#3116)

* Replaced std::vector-based interfaces with HostDeviceVector-based interfaces.

- replacement was performed in the learner, boosters, predictors,
  updaters, and objective functions
- only interfaces used in training were replaced;
  interfaces like PredictInstance() still use std::vector
- refactoring necessary for replacement of interfaces was also performed,
  such as using HostDeviceVector in prediction cache

* HostDeviceVector-based interfaces for custom objective function example plugin.
This commit is contained in:
Andrew V. Adinetz
2018-02-28 01:00:04 +01:00
committed by Rory Mitchell
parent 11bfa8584d
commit d5992dd881
38 changed files with 371 additions and 519 deletions

View File

@@ -35,16 +35,18 @@ class SoftmaxMultiClassObj : public ObjFunction {
void Configure(const std::vector<std::pair<std::string, std::string> >& args) override {
param_.InitAllowUnknown(args);
}
void GetGradient(const std::vector<bst_float>& preds,
void GetGradient(HostDeviceVector<bst_float>* preds,
const MetaInfo& info,
int iter,
std::vector<bst_gpair>* out_gpair) override {
HostDeviceVector<bst_gpair>* out_gpair) override {
CHECK_NE(info.labels.size(), 0U) << "label set cannot be empty";
CHECK(preds.size() == (static_cast<size_t>(param_.num_class) * info.labels.size()))
CHECK(preds->size() == (static_cast<size_t>(param_.num_class) * info.labels.size()))
<< "SoftmaxMultiClassObj: label size and pred size does not match";
out_gpair->resize(preds.size());
std::vector<bst_float>& preds_h = preds->data_h();
out_gpair->resize(preds_h.size());
std::vector<bst_gpair>& gpair = out_gpair->data_h();
const int nclass = param_.num_class;
const omp_ulong ndata = static_cast<omp_ulong>(preds.size() / nclass);
const omp_ulong ndata = static_cast<omp_ulong>(preds_h.size() / nclass);
int label_error = 0;
#pragma omp parallel
@@ -53,7 +55,7 @@ class SoftmaxMultiClassObj : public ObjFunction {
#pragma omp for schedule(static)
for (omp_ulong i = 0; i < ndata; ++i) {
for (int k = 0; k < nclass; ++k) {
rec[k] = preds[i * nclass + k];
rec[k] = preds_h[i * nclass + k];
}
common::Softmax(&rec);
int label = static_cast<int>(info.labels[i]);
@@ -65,9 +67,9 @@ class SoftmaxMultiClassObj : public ObjFunction {
bst_float p = rec[k];
const bst_float h = 2.0f * p * (1.0f - p) * wt;
if (label == k) {
(*out_gpair)[i * nclass + k] = bst_gpair((p - 1.0f) * wt, h);
gpair[i * nclass + k] = bst_gpair((p - 1.0f) * wt, h);
} else {
(*out_gpair)[i * nclass + k] = bst_gpair(p* wt, h);
gpair[i * nclass + k] = bst_gpair(p* wt, h);
}
}
}
@@ -77,10 +79,10 @@ class SoftmaxMultiClassObj : public ObjFunction {
<< " num_class=" << nclass
<< " but found " << label_error << " in label.";
}
void PredTransform(std::vector<bst_float>* io_preds) override {
void PredTransform(HostDeviceVector<bst_float>* io_preds) override {
this->Transform(io_preds, output_prob_);
}
void EvalTransform(std::vector<bst_float>* io_preds) override {
void EvalTransform(HostDeviceVector<bst_float>* io_preds) override {
this->Transform(io_preds, true);
}
const char* DefaultEvalMetric() const override {
@@ -88,8 +90,8 @@ class SoftmaxMultiClassObj : public ObjFunction {
}
private:
inline void Transform(std::vector<bst_float> *io_preds, bool prob) {
std::vector<bst_float> &preds = *io_preds;
inline void Transform(HostDeviceVector<bst_float> *io_preds, bool prob) {
std::vector<bst_float> &preds = io_preds->data_h();
std::vector<bst_float> tmp;
const int nclass = param_.num_class;
const omp_ulong ndata = static_cast<omp_ulong>(preds.size() / nclass);

View File

@@ -25,17 +25,6 @@ ObjFunction* ObjFunction::Create(const std::string& name) {
return (e->body)();
}
void ObjFunction::GetGradient(HostDeviceVector<bst_float>* preds,
const MetaInfo& info,
int iteration,
HostDeviceVector<bst_gpair>* out_gpair) {
GetGradient(preds->data_h(), info, iteration, &out_gpair->data_h());
}
void ObjFunction::PredTransform(HostDeviceVector<bst_float> *io_preds) {
PredTransform(&io_preds->data_h());
}
} // namespace xgboost
namespace xgboost {

View File

@@ -37,13 +37,14 @@ class LambdaRankObj : public ObjFunction {
void Configure(const std::vector<std::pair<std::string, std::string> >& args) override {
param_.InitAllowUnknown(args);
}
void GetGradient(const std::vector<bst_float>& preds,
void GetGradient(HostDeviceVector<bst_float>* preds,
const MetaInfo& info,
int iter,
std::vector<bst_gpair>* out_gpair) override {
CHECK_EQ(preds.size(), info.labels.size()) << "label size predict size not match";
std::vector<bst_gpair>& gpair = *out_gpair;
gpair.resize(preds.size());
HostDeviceVector<bst_gpair>* out_gpair) override {
CHECK_EQ(preds->size(), info.labels.size()) << "label size predict size not match";
auto& preds_h = preds->data_h();
out_gpair->resize(preds_h.size());
std::vector<bst_gpair>& gpair = out_gpair->data_h();
// quick consistency when group is not available
std::vector<unsigned> tgptr(2, 0); tgptr[1] = static_cast<unsigned>(info.labels.size());
const std::vector<unsigned> &gptr = info.group_ptr.size() == 0 ? tgptr : info.group_ptr;
@@ -63,7 +64,7 @@ class LambdaRankObj : public ObjFunction {
for (bst_omp_uint k = 0; k < ngroup; ++k) {
lst.clear(); pairs.clear();
for (unsigned j = gptr[k]; j < gptr[k+1]; ++j) {
lst.push_back(ListEntry(preds[j], info.labels[j], j));
lst.push_back(ListEntry(preds_h[j], info.labels[j], j));
gpair[j] = bst_gpair(0.0f, 0.0f);
}
std::sort(lst.begin(), lst.end(), ListEntry::CmpPred);

View File

@@ -38,18 +38,20 @@ class RegLossObj : public ObjFunction {
const std::vector<std::pair<std::string, std::string> > &args) override {
param_.InitAllowUnknown(args);
}
void GetGradient(const std::vector<bst_float> &preds, const MetaInfo &info,
int iter, std::vector<bst_gpair> *out_gpair) override {
void GetGradient(HostDeviceVector<bst_float> *preds, const MetaInfo &info,
int iter, HostDeviceVector<bst_gpair> *out_gpair) override {
CHECK_NE(info.labels.size(), 0U) << "label set cannot be empty";
CHECK_EQ(preds.size(), info.labels.size())
CHECK_EQ(preds->size(), info.labels.size())
<< "labels are not correctly provided"
<< "preds.size=" << preds.size()
<< "preds.size=" << preds->size()
<< ", label.size=" << info.labels.size();
auto& preds_h = preds->data_h();
this->LazyCheckLabels(info.labels);
out_gpair->resize(preds.size());
const omp_ulong n = static_cast<omp_ulong>(preds.size());
auto gpair_ptr = out_gpair->data();
out_gpair->resize(preds_h.size());
auto& gpair = out_gpair->data_h();
const omp_ulong n = static_cast<omp_ulong>(preds_h.size());
auto gpair_ptr = out_gpair->ptr_h();
avx::Float8 scale(param_.scale_pos_weight);
const omp_ulong remainder = n % 8;
@@ -58,7 +60,7 @@ class RegLossObj : public ObjFunction {
#pragma omp parallel for schedule(static) num_threads(std::min(8, nthread))
for (omp_ulong i = 0; i < n - remainder; i += 8) {
avx::Float8 y(&info.labels[i]);
avx::Float8 p = Loss::PredTransform(avx::Float8(&preds[i]));
avx::Float8 p = Loss::PredTransform(avx::Float8(&preds_h[i]));
avx::Float8 w = info.weights.empty() ? avx::Float8(1.0f)
: avx::Float8(&info.weights[i]);
// Adjust weight
@@ -69,11 +71,11 @@ class RegLossObj : public ObjFunction {
}
for (omp_ulong i = n - remainder; i < n; ++i) {
auto y = info.labels[i];
bst_float p = Loss::PredTransform(preds[i]);
bst_float p = Loss::PredTransform(preds_h[i]);
bst_float w = info.GetWeight(i);
w += y * ((param_.scale_pos_weight * w) - w);
(*out_gpair)[i] = bst_gpair(Loss::FirstOrderGradient(p, y) * w,
Loss::SecondOrderGradient(p, y) * w);
gpair[i] = bst_gpair(Loss::FirstOrderGradient(p, y) * w,
Loss::SecondOrderGradient(p, y) * w);
}
// Reset omp max threads
@@ -82,8 +84,8 @@ class RegLossObj : public ObjFunction {
const char *DefaultEvalMetric() const override {
return Loss::DefaultEvalMetric();
}
void PredTransform(std::vector<bst_float> *io_preds) override {
std::vector<bst_float> &preds = *io_preds;
void PredTransform(HostDeviceVector<bst_float> *io_preds) override {
std::vector<bst_float> &preds = io_preds->data_h();
const bst_omp_uint ndata = static_cast<bst_omp_uint>(preds.size());
#pragma omp parallel for schedule(static)
for (bst_omp_uint j = 0; j < ndata; ++j) {
@@ -143,40 +145,42 @@ class PoissonRegression : public ObjFunction {
param_.InitAllowUnknown(args);
}
void GetGradient(const std::vector<bst_float> &preds,
void GetGradient(HostDeviceVector<bst_float> *preds,
const MetaInfo &info,
int iter,
std::vector<bst_gpair> *out_gpair) override {
HostDeviceVector<bst_gpair> *out_gpair) override {
CHECK_NE(info.labels.size(), 0U) << "label set cannot be empty";
CHECK_EQ(preds.size(), info.labels.size()) << "labels are not correctly provided";
out_gpair->resize(preds.size());
CHECK_EQ(preds->size(), info.labels.size()) << "labels are not correctly provided";
auto& preds_h = preds->data_h();
out_gpair->resize(preds->size());
auto& gpair = out_gpair->data_h();
// check if label in range
bool label_correct = true;
// start calculating gradient
const omp_ulong ndata = static_cast<omp_ulong>(preds.size()); // NOLINT(*)
const omp_ulong ndata = static_cast<omp_ulong>(preds_h.size()); // NOLINT(*)
#pragma omp parallel for schedule(static)
for (omp_ulong i = 0; i < ndata; ++i) { // NOLINT(*)
bst_float p = preds[i];
bst_float p = preds_h[i];
bst_float w = info.GetWeight(i);
bst_float y = info.labels[i];
if (y >= 0.0f) {
(*out_gpair)[i] = bst_gpair((std::exp(p) - y) * w,
std::exp(p + param_.max_delta_step) * w);
gpair[i] = bst_gpair((std::exp(p) - y) * w,
std::exp(p + param_.max_delta_step) * w);
} else {
label_correct = false;
}
}
CHECK(label_correct) << "PoissonRegression: label must be nonnegative";
}
void PredTransform(std::vector<bst_float> *io_preds) override {
std::vector<bst_float> &preds = *io_preds;
void PredTransform(HostDeviceVector<bst_float> *io_preds) override {
std::vector<bst_float> &preds = io_preds->data_h();
const long ndata = static_cast<long>(preds.size()); // NOLINT(*)
#pragma omp parallel for schedule(static)
for (long j = 0; j < ndata; ++j) { // NOLINT(*)
preds[j] = std::exp(preds[j]);
}
}
void EvalTransform(std::vector<bst_float> *io_preds) override {
void EvalTransform(HostDeviceVector<bst_float> *io_preds) override {
PredTransform(io_preds);
}
bst_float ProbToMargin(bst_float base_score) const override {
@@ -202,21 +206,23 @@ class CoxRegression : public ObjFunction {
public:
// declare functions
void Configure(const std::vector<std::pair<std::string, std::string> >& args) override {}
void GetGradient(const std::vector<bst_float> &preds,
void GetGradient(HostDeviceVector<bst_float> *preds,
const MetaInfo &info,
int iter,
std::vector<bst_gpair> *out_gpair) override {
HostDeviceVector<bst_gpair> *out_gpair) override {
CHECK_NE(info.labels.size(), 0U) << "label set cannot be empty";
CHECK_EQ(preds.size(), info.labels.size()) << "labels are not correctly provided";
out_gpair->resize(preds.size());
CHECK_EQ(preds->size(), info.labels.size()) << "labels are not correctly provided";
auto& preds_h = preds->data_h();
out_gpair->resize(preds_h.size());
auto& gpair = out_gpair->data_h();
const std::vector<size_t> &label_order = info.LabelAbsSort();
const omp_ulong ndata = static_cast<omp_ulong>(preds.size()); // NOLINT(*)
const omp_ulong ndata = static_cast<omp_ulong>(preds_h.size()); // NOLINT(*)
// pre-compute a sum
double exp_p_sum = 0; // we use double because we might need the precision with large datasets
for (omp_ulong i = 0; i < ndata; ++i) {
exp_p_sum += std::exp(preds[label_order[i]]);
exp_p_sum += std::exp(preds_h[label_order[i]]);
}
// start calculating grad and hess
@@ -227,7 +233,7 @@ class CoxRegression : public ObjFunction {
double accumulated_sum = 0;
for (omp_ulong i = 0; i < ndata; ++i) { // NOLINT(*)
const size_t ind = label_order[i];
const double p = preds[ind];
const double p = preds_h[ind];
const double exp_p = std::exp(p);
const double w = info.GetWeight(ind);
const double y = info.labels[ind];
@@ -251,21 +257,21 @@ class CoxRegression : public ObjFunction {
const double grad = exp_p*r_k - static_cast<bst_float>(y > 0);
const double hess = exp_p*r_k - exp_p*exp_p * s_k;
out_gpair->at(ind) = bst_gpair(grad * w, hess * w);
gpair.at(ind) = bst_gpair(grad * w, hess * w);
last_abs_y = abs_y;
last_exp_p = exp_p;
}
}
void PredTransform(std::vector<bst_float> *io_preds) override {
std::vector<bst_float> &preds = *io_preds;
void PredTransform(HostDeviceVector<bst_float> *io_preds) override {
std::vector<bst_float> &preds = io_preds->data_h();
const long ndata = static_cast<long>(preds.size()); // NOLINT(*)
#pragma omp parallel for schedule(static)
for (long j = 0; j < ndata; ++j) { // NOLINT(*)
preds[j] = std::exp(preds[j]);
}
}
void EvalTransform(std::vector<bst_float> *io_preds) override {
void EvalTransform(HostDeviceVector<bst_float> *io_preds) override {
PredTransform(io_preds);
}
bst_float ProbToMargin(bst_float base_score) const override {
@@ -288,39 +294,41 @@ class GammaRegression : public ObjFunction {
void Configure(const std::vector<std::pair<std::string, std::string> >& args) override {
}
void GetGradient(const std::vector<bst_float> &preds,
void GetGradient(HostDeviceVector<bst_float> *preds,
const MetaInfo &info,
int iter,
std::vector<bst_gpair> *out_gpair) override {
HostDeviceVector<bst_gpair> *out_gpair) override {
CHECK_NE(info.labels.size(), 0U) << "label set cannot be empty";
CHECK_EQ(preds.size(), info.labels.size()) << "labels are not correctly provided";
out_gpair->resize(preds.size());
CHECK_EQ(preds->size(), info.labels.size()) << "labels are not correctly provided";
auto& preds_h = preds->data_h();
out_gpair->resize(preds_h.size());
auto& gpair = out_gpair->data_h();
// check if label in range
bool label_correct = true;
// start calculating gradient
const omp_ulong ndata = static_cast<omp_ulong>(preds.size()); // NOLINT(*)
const omp_ulong ndata = static_cast<omp_ulong>(preds_h.size()); // NOLINT(*)
#pragma omp parallel for schedule(static)
for (omp_ulong i = 0; i < ndata; ++i) { // NOLINT(*)
bst_float p = preds[i];
bst_float p = preds_h[i];
bst_float w = info.GetWeight(i);
bst_float y = info.labels[i];
if (y >= 0.0f) {
(*out_gpair)[i] = bst_gpair((1 - y / std::exp(p)) * w, y / std::exp(p) * w);
gpair[i] = bst_gpair((1 - y / std::exp(p)) * w, y / std::exp(p) * w);
} else {
label_correct = false;
}
}
CHECK(label_correct) << "GammaRegression: label must be positive";
}
void PredTransform(std::vector<bst_float> *io_preds) override {
std::vector<bst_float> &preds = *io_preds;
void PredTransform(HostDeviceVector<bst_float> *io_preds) override {
std::vector<bst_float> &preds = io_preds->data_h();
const long ndata = static_cast<long>(preds.size()); // NOLINT(*)
#pragma omp parallel for schedule(static)
for (long j = 0; j < ndata; ++j) { // NOLINT(*)
preds[j] = std::exp(preds[j]);
}
}
void EvalTransform(std::vector<bst_float> *io_preds) override {
void EvalTransform(HostDeviceVector<bst_float> *io_preds) override {
PredTransform(io_preds);
}
bst_float ProbToMargin(bst_float base_score) const override {
@@ -353,20 +361,22 @@ class TweedieRegression : public ObjFunction {
param_.InitAllowUnknown(args);
}
void GetGradient(const std::vector<bst_float> &preds,
void GetGradient(HostDeviceVector<bst_float> *preds,
const MetaInfo &info,
int iter,
std::vector<bst_gpair> *out_gpair) override {
HostDeviceVector<bst_gpair> *out_gpair) override {
CHECK_NE(info.labels.size(), 0U) << "label set cannot be empty";
CHECK_EQ(preds.size(), info.labels.size()) << "labels are not correctly provided";
out_gpair->resize(preds.size());
CHECK_EQ(preds->size(), info.labels.size()) << "labels are not correctly provided";
auto& preds_h = preds->data_h();
out_gpair->resize(preds->size());
auto& gpair = out_gpair->data_h();
// check if label in range
bool label_correct = true;
// start calculating gradient
const omp_ulong ndata = static_cast<omp_ulong>(preds.size()); // NOLINT(*)
const omp_ulong ndata = static_cast<omp_ulong>(preds->size()); // NOLINT(*)
#pragma omp parallel for schedule(static)
for (omp_ulong i = 0; i < ndata; ++i) { // NOLINT(*)
bst_float p = preds[i];
bst_float p = preds_h[i];
bst_float w = info.GetWeight(i);
bst_float y = info.labels[i];
float rho = param_.tweedie_variance_power;
@@ -374,15 +384,15 @@ class TweedieRegression : public ObjFunction {
bst_float grad = -y * std::exp((1 - rho) * p) + std::exp((2 - rho) * p);
bst_float hess = -y * (1 - rho) * \
std::exp((1 - rho) * p) + (2 - rho) * std::exp((2 - rho) * p);
(*out_gpair)[i] = bst_gpair(grad * w, hess * w);
gpair[i] = bst_gpair(grad * w, hess * w);
} else {
label_correct = false;
}
}
CHECK(label_correct) << "TweedieRegression: label must be nonnegative";
}
void PredTransform(std::vector<bst_float> *io_preds) override {
std::vector<bst_float> &preds = *io_preds;
void PredTransform(HostDeviceVector<bst_float> *io_preds) override {
std::vector<bst_float> &preds = io_preds->data_h();
const long ndata = static_cast<long>(preds.size()); // NOLINT(*)
#pragma omp parallel for schedule(static)
for (long j = 0; j < ndata; ++j) { // NOLINT(*)

View File

@@ -103,8 +103,8 @@ class GPURegLossObj : public ObjFunction {
// free the old data and allocate the new data
ba_.reset(new bulk_allocator<memory_type::DEVICE>());
data_.reset(new DeviceData(ba_.get(), 0, n));
preds_d_.resize(n, param_.gpu_id);
out_gpair_d_.resize(n, param_.gpu_id);
preds_d_.resize(n, 0.0f, param_.gpu_id);
out_gpair_d_.resize(n, bst_gpair(), param_.gpu_id);
}
public:
@@ -114,23 +114,6 @@ class GPURegLossObj : public ObjFunction {
param_.InitAllowUnknown(args);
CHECK(param_.n_gpus != 0) << "Must have at least one device";
}
void GetGradient(const std::vector<float> &preds,
const MetaInfo &info,
int iter,
std::vector<bst_gpair> *out_gpair) override {
CHECK_NE(info.labels.size(), 0U) << "label set cannot be empty";
CHECK_EQ(preds.size(), info.labels.size())
<< "labels are not correctly provided"
<< "preds.size=" << preds.size() << ", label.size=" << info.labels.size();
size_t ndata = preds.size();
out_gpair->resize(ndata);
LazyResize(ndata);
thrust::copy(preds.begin(), preds.end(), preds_d_.tbegin(param_.gpu_id));
GetGradientDevice(preds_d_.ptr_d(param_.gpu_id), info, iter,
out_gpair_d_.ptr_d(param_.gpu_id), ndata);
thrust::copy_n(out_gpair_d_.tbegin(param_.gpu_id), ndata, out_gpair->begin());
}
void GetGradient(HostDeviceVector<float>* preds,
const MetaInfo &info,
@@ -141,7 +124,7 @@ class GPURegLossObj : public ObjFunction {
<< "labels are not correctly provided"
<< "preds.size=" << preds->size() << ", label.size=" << info.labels.size();
size_t ndata = preds->size();
out_gpair->resize(ndata, param_.gpu_id);
out_gpair->resize(ndata, bst_gpair(), param_.gpu_id);
LazyResize(ndata);
GetGradientDevice(preds->ptr_d(param_.gpu_id), info, iter,
out_gpair->ptr_d(param_.gpu_id), ndata);
@@ -189,13 +172,6 @@ class GPURegLossObj : public ObjFunction {
return Loss::DefaultEvalMetric();
}
void PredTransform(std::vector<float> *io_preds) override {
LazyResize(io_preds->size());
thrust::copy(io_preds->begin(), io_preds->end(), preds_d_.tbegin(param_.gpu_id));
PredTransformDevice(preds_d_.ptr_d(param_.gpu_id), io_preds->size());
thrust::copy_n(preds_d_.tbegin(param_.gpu_id), io_preds->size(), io_preds->begin());
}
void PredTransform(HostDeviceVector<float> *io_preds) override {
PredTransformDevice(io_preds->ptr_d(param_.gpu_id), io_preds->size());
}