[CORE] Refactor cache mechanism (#1540)
This commit is contained in:
parent
6dabdd33e3
commit
ecec5f7959
4
NEWS.md
4
NEWS.md
@ -3,6 +3,10 @@ XGBoost Change Log
|
|||||||
|
|
||||||
This file records the changes in xgboost library in reverse chronological order.
|
This file records the changes in xgboost library in reverse chronological order.
|
||||||
|
|
||||||
|
## in progress version
|
||||||
|
* Refactored gbm to allow more friendly cache strategy
|
||||||
|
- Specialized some prediction routine
|
||||||
|
|
||||||
## v0.6 (2016.07.29)
|
## v0.6 (2016.07.29)
|
||||||
* Version 0.5 is skipped due to major improvements in the core
|
* Version 0.5 is skipped due to major improvements in the core
|
||||||
* Major refactor of core library.
|
* Major refactor of core library.
|
||||||
|
|||||||
@ -13,8 +13,10 @@
|
|||||||
#include <utility>
|
#include <utility>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <memory>
|
||||||
#include "./base.h"
|
#include "./base.h"
|
||||||
#include "./data.h"
|
#include "./data.h"
|
||||||
|
#include "./objective.h"
|
||||||
#include "./feature_map.h"
|
#include "./feature_map.h"
|
||||||
|
|
||||||
namespace xgboost {
|
namespace xgboost {
|
||||||
@ -50,13 +52,6 @@ class GradientBooster {
|
|||||||
* \param fo output stream
|
* \param fo output stream
|
||||||
*/
|
*/
|
||||||
virtual void Save(dmlc::Stream* fo) const = 0;
|
virtual void Save(dmlc::Stream* fo) const = 0;
|
||||||
/*!
|
|
||||||
* \brief reset the predict buffer size.
|
|
||||||
* This will invalidate all the previous cached results
|
|
||||||
* and recalculate from scratch
|
|
||||||
* \param num_pbuffer The size of predict buffer.
|
|
||||||
*/
|
|
||||||
virtual void ResetPredBuffer(size_t num_pbuffer) {}
|
|
||||||
/*!
|
/*!
|
||||||
* \brief whether the model allow lazy checkpoint
|
* \brief whether the model allow lazy checkpoint
|
||||||
* return true if model is only updated in DoBoost
|
* return true if model is only updated in DoBoost
|
||||||
@ -68,27 +63,21 @@ class GradientBooster {
|
|||||||
/*!
|
/*!
|
||||||
* \brief perform update to the model(boosting)
|
* \brief perform update to the model(boosting)
|
||||||
* \param p_fmat feature matrix that provide access to features
|
* \param p_fmat feature matrix that provide access to features
|
||||||
* \param buffer_offset buffer index offset of these instances, if equals -1
|
|
||||||
* this means we do not have buffer index allocated to the gbm
|
|
||||||
* \param in_gpair address of the gradient pair statistics of the data
|
* \param in_gpair address of the gradient pair statistics of the data
|
||||||
|
* \param obj The objective function, optional, can be nullptr when use customized version
|
||||||
* the booster may change content of gpair
|
* the booster may change content of gpair
|
||||||
*/
|
*/
|
||||||
virtual void DoBoost(DMatrix* p_fmat,
|
virtual void DoBoost(DMatrix* p_fmat,
|
||||||
int64_t buffer_offset,
|
std::vector<bst_gpair>* in_gpair,
|
||||||
std::vector<bst_gpair>* in_gpair) = 0;
|
ObjFunction* obj = nullptr) = 0;
|
||||||
/*!
|
/*!
|
||||||
* \brief generate predictions for given feature matrix
|
* \brief generate predictions for given feature matrix
|
||||||
* \param dmat feature matrix
|
* \param dmat feature matrix
|
||||||
* \param buffer_offset buffer index offset of these instances, if equals -1
|
|
||||||
* this means we do not have buffer index allocated to the gbm
|
|
||||||
* a buffer index is assigned to each instance that requires repeative prediction
|
|
||||||
* the size of buffer is set by convention using GradientBooster.ResetPredBuffer(size);
|
|
||||||
* \param out_preds output vector to hold the predictions
|
* \param out_preds output vector to hold the predictions
|
||||||
* \param ntree_limit limit the number of trees used in prediction, when it equals 0, this means
|
* \param ntree_limit limit the number of trees used in prediction, when it equals 0, this means
|
||||||
* we do not limit number of trees, this parameter is only valid for gbtree, but not for gblinear
|
* we do not limit number of trees, this parameter is only valid for gbtree, but not for gblinear
|
||||||
*/
|
*/
|
||||||
virtual void Predict(DMatrix* dmat,
|
virtual void Predict(DMatrix* dmat,
|
||||||
int64_t buffer_offset,
|
|
||||||
std::vector<float>* out_preds,
|
std::vector<float>* out_preds,
|
||||||
unsigned ntree_limit = 0) = 0;
|
unsigned ntree_limit = 0) = 0;
|
||||||
/*!
|
/*!
|
||||||
@ -128,9 +117,14 @@ class GradientBooster {
|
|||||||
/*!
|
/*!
|
||||||
* \brief create a gradient booster from given name
|
* \brief create a gradient booster from given name
|
||||||
* \param name name of gradient booster
|
* \param name name of gradient booster
|
||||||
|
* \param cache_mats The cache data matrix of the Booster.
|
||||||
|
* \param base_margin The base margin of prediction.
|
||||||
* \return The created booster.
|
* \return The created booster.
|
||||||
*/
|
*/
|
||||||
static GradientBooster* Create(const std::string& name);
|
static GradientBooster* Create(
|
||||||
|
const std::string& name,
|
||||||
|
const std::vector<std::shared_ptr<DMatrix> >& cache_mats,
|
||||||
|
float base_margin);
|
||||||
};
|
};
|
||||||
|
|
||||||
// implementing configure.
|
// implementing configure.
|
||||||
@ -144,8 +138,10 @@ inline void GradientBooster::Configure(PairIter begin, PairIter end) {
|
|||||||
* \brief Registry entry for tree updater.
|
* \brief Registry entry for tree updater.
|
||||||
*/
|
*/
|
||||||
struct GradientBoosterReg
|
struct GradientBoosterReg
|
||||||
: public dmlc::FunctionRegEntryBase<GradientBoosterReg,
|
: public dmlc::FunctionRegEntryBase<
|
||||||
std::function<GradientBooster* ()> > {
|
GradientBoosterReg,
|
||||||
|
std::function<GradientBooster* (const std::vector<std::shared_ptr<DMatrix> > &cached_mats,
|
||||||
|
float base_margin)> > {
|
||||||
};
|
};
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
|||||||
@ -166,7 +166,7 @@ class Learner : public rabit::Serializable {
|
|||||||
* \param cache_data The matrix to cache the prediction.
|
* \param cache_data The matrix to cache the prediction.
|
||||||
* \return Created learner.
|
* \return Created learner.
|
||||||
*/
|
*/
|
||||||
static Learner* Create(const std::vector<DMatrix*>& cache_data);
|
static Learner* Create(const std::vector<std::shared_ptr<DMatrix> >& cache_data);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/*! \brief internal base score of the model */
|
/*! \brief internal base score of the model */
|
||||||
|
|||||||
@ -22,7 +22,7 @@ namespace xgboost {
|
|||||||
// booster wrapper for backward compatible reason.
|
// booster wrapper for backward compatible reason.
|
||||||
class Booster {
|
class Booster {
|
||||||
public:
|
public:
|
||||||
explicit Booster(const std::vector<DMatrix*>& cache_mats)
|
explicit Booster(const std::vector<std::shared_ptr<DMatrix> >& cache_mats)
|
||||||
: configured_(false),
|
: configured_(false),
|
||||||
initialized_(false),
|
initialized_(false),
|
||||||
learner_(Learner::Create(cache_mats)) {}
|
learner_(Learner::Create(cache_mats)) {}
|
||||||
@ -207,8 +207,7 @@ int XGDMatrixCreateFromFile(const char *fname,
|
|||||||
LOG(CONSOLE) << "XGBoost distributed mode detected, "
|
LOG(CONSOLE) << "XGBoost distributed mode detected, "
|
||||||
<< "will split data among workers";
|
<< "will split data among workers";
|
||||||
}
|
}
|
||||||
*out = DMatrix::Load(
|
*out = new std::shared_ptr<DMatrix>(DMatrix::Load(fname, false, true));
|
||||||
fname, false, true);
|
|
||||||
API_END();
|
API_END();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -224,7 +223,7 @@ int XGDMatrixCreateFromDataIter(
|
|||||||
scache = cache_info;
|
scache = cache_info;
|
||||||
}
|
}
|
||||||
NativeDataIter parser(data_handle, callback);
|
NativeDataIter parser(data_handle, callback);
|
||||||
*out = DMatrix::Create(&parser, scache);
|
*out = new std::shared_ptr<DMatrix>(DMatrix::Create(&parser, scache));
|
||||||
API_END();
|
API_END();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -250,16 +249,16 @@ XGB_DLL int XGDMatrixCreateFromCSR(const xgboost::bst_ulong* indptr,
|
|||||||
}
|
}
|
||||||
mat.info.num_row = nindptr - 1;
|
mat.info.num_row = nindptr - 1;
|
||||||
mat.info.num_nonzero = static_cast<uint64_t>(nelem);
|
mat.info.num_nonzero = static_cast<uint64_t>(nelem);
|
||||||
*out = DMatrix::Create(std::move(source));
|
*out = new std::shared_ptr<DMatrix>(DMatrix::Create(std::move(source)));
|
||||||
API_END();
|
API_END();
|
||||||
}
|
}
|
||||||
|
|
||||||
XGB_DLL int XGDMatrixCreateFromCSC(const xgboost::bst_ulong* col_ptr,
|
XGB_DLL int XGDMatrixCreateFromCSC(const xgboost::bst_ulong* col_ptr,
|
||||||
const unsigned* indices,
|
const unsigned* indices,
|
||||||
const float* data,
|
const float* data,
|
||||||
xgboost::bst_ulong nindptr,
|
xgboost::bst_ulong nindptr,
|
||||||
xgboost::bst_ulong nelem,
|
xgboost::bst_ulong nelem,
|
||||||
DMatrixHandle* out) {
|
DMatrixHandle* out) {
|
||||||
std::unique_ptr<data::SimpleCSRSource> source(new data::SimpleCSRSource());
|
std::unique_ptr<data::SimpleCSRSource> source(new data::SimpleCSRSource());
|
||||||
|
|
||||||
API_BEGIN();
|
API_BEGIN();
|
||||||
@ -292,15 +291,15 @@ XGB_DLL int XGDMatrixCreateFromCSC(const xgboost::bst_ulong* col_ptr,
|
|||||||
mat.info.num_row = mat.row_ptr_.size() - 1;
|
mat.info.num_row = mat.row_ptr_.size() - 1;
|
||||||
mat.info.num_col = static_cast<uint64_t>(ncol);
|
mat.info.num_col = static_cast<uint64_t>(ncol);
|
||||||
mat.info.num_nonzero = nelem;
|
mat.info.num_nonzero = nelem;
|
||||||
*out = DMatrix::Create(std::move(source));
|
*out = new std::shared_ptr<DMatrix>(DMatrix::Create(std::move(source)));
|
||||||
API_END();
|
API_END();
|
||||||
}
|
}
|
||||||
|
|
||||||
XGB_DLL int XGDMatrixCreateFromMat(const float* data,
|
XGB_DLL int XGDMatrixCreateFromMat(const float* data,
|
||||||
xgboost::bst_ulong nrow,
|
xgboost::bst_ulong nrow,
|
||||||
xgboost::bst_ulong ncol,
|
xgboost::bst_ulong ncol,
|
||||||
float missing,
|
float missing,
|
||||||
DMatrixHandle* out) {
|
DMatrixHandle* out) {
|
||||||
std::unique_ptr<data::SimpleCSRSource> source(new data::SimpleCSRSource());
|
std::unique_ptr<data::SimpleCSRSource> source(new data::SimpleCSRSource());
|
||||||
|
|
||||||
API_BEGIN();
|
API_BEGIN();
|
||||||
@ -324,19 +323,19 @@ XGB_DLL int XGDMatrixCreateFromMat(const float* data,
|
|||||||
mat.row_ptr_.push_back(mat.row_ptr_.back() + nelem);
|
mat.row_ptr_.push_back(mat.row_ptr_.back() + nelem);
|
||||||
}
|
}
|
||||||
mat.info.num_nonzero = mat.row_data_.size();
|
mat.info.num_nonzero = mat.row_data_.size();
|
||||||
*out = DMatrix::Create(std::move(source));
|
*out = new std::shared_ptr<DMatrix>(DMatrix::Create(std::move(source)));
|
||||||
API_END();
|
API_END();
|
||||||
}
|
}
|
||||||
|
|
||||||
XGB_DLL int XGDMatrixSliceDMatrix(DMatrixHandle handle,
|
XGB_DLL int XGDMatrixSliceDMatrix(DMatrixHandle handle,
|
||||||
const int* idxset,
|
const int* idxset,
|
||||||
xgboost::bst_ulong len,
|
xgboost::bst_ulong len,
|
||||||
DMatrixHandle* out) {
|
DMatrixHandle* out) {
|
||||||
std::unique_ptr<data::SimpleCSRSource> source(new data::SimpleCSRSource());
|
std::unique_ptr<data::SimpleCSRSource> source(new data::SimpleCSRSource());
|
||||||
|
|
||||||
API_BEGIN();
|
API_BEGIN();
|
||||||
data::SimpleCSRSource src;
|
data::SimpleCSRSource src;
|
||||||
src.CopyFrom(static_cast<DMatrix*>(handle));
|
src.CopyFrom(static_cast<std::shared_ptr<DMatrix>*>(handle)->get());
|
||||||
data::SimpleCSRSource& ret = *source;
|
data::SimpleCSRSource& ret = *source;
|
||||||
|
|
||||||
CHECK_EQ(src.info.group_ptr.size(), 0)
|
CHECK_EQ(src.info.group_ptr.size(), 0)
|
||||||
@ -371,21 +370,21 @@ XGB_DLL int XGDMatrixSliceDMatrix(DMatrixHandle handle,
|
|||||||
ret.info.root_index.push_back(src.info.root_index[ridx]);
|
ret.info.root_index.push_back(src.info.root_index[ridx]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*out = DMatrix::Create(std::move(source));
|
*out = new std::shared_ptr<DMatrix>(DMatrix::Create(std::move(source)));
|
||||||
API_END();
|
API_END();
|
||||||
}
|
}
|
||||||
|
|
||||||
XGB_DLL int XGDMatrixFree(DMatrixHandle handle) {
|
XGB_DLL int XGDMatrixFree(DMatrixHandle handle) {
|
||||||
API_BEGIN();
|
API_BEGIN();
|
||||||
delete static_cast<DMatrix*>(handle);
|
delete static_cast<std::shared_ptr<DMatrix>*>(handle);
|
||||||
API_END();
|
API_END();
|
||||||
}
|
}
|
||||||
|
|
||||||
XGB_DLL int XGDMatrixSaveBinary(DMatrixHandle handle,
|
XGB_DLL int XGDMatrixSaveBinary(DMatrixHandle handle,
|
||||||
const char* fname,
|
const char* fname,
|
||||||
int silent) {
|
int silent) {
|
||||||
API_BEGIN();
|
API_BEGIN();
|
||||||
static_cast<DMatrix*>(handle)->SaveToLocalFile(fname);
|
static_cast<std::shared_ptr<DMatrix>*>(handle)->get()->SaveToLocalFile(fname);
|
||||||
API_END();
|
API_END();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -394,7 +393,8 @@ XGB_DLL int XGDMatrixSetFloatInfo(DMatrixHandle handle,
|
|||||||
const float* info,
|
const float* info,
|
||||||
xgboost::bst_ulong len) {
|
xgboost::bst_ulong len) {
|
||||||
API_BEGIN();
|
API_BEGIN();
|
||||||
static_cast<DMatrix*>(handle)->info().SetInfo(field, info, kFloat32, len);
|
static_cast<std::shared_ptr<DMatrix>*>(handle)
|
||||||
|
->get()->info().SetInfo(field, info, kFloat32, len);
|
||||||
API_END();
|
API_END();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -403,16 +403,17 @@ XGB_DLL int XGDMatrixSetUIntInfo(DMatrixHandle handle,
|
|||||||
const unsigned* info,
|
const unsigned* info,
|
||||||
xgboost::bst_ulong len) {
|
xgboost::bst_ulong len) {
|
||||||
API_BEGIN();
|
API_BEGIN();
|
||||||
static_cast<DMatrix*>(handle)->info().SetInfo(field, info, kUInt32, len);
|
static_cast<std::shared_ptr<DMatrix>*>(handle)
|
||||||
|
->get()->info().SetInfo(field, info, kUInt32, len);
|
||||||
API_END();
|
API_END();
|
||||||
}
|
}
|
||||||
|
|
||||||
XGB_DLL int XGDMatrixSetGroup(DMatrixHandle handle,
|
XGB_DLL int XGDMatrixSetGroup(DMatrixHandle handle,
|
||||||
const unsigned* group,
|
const unsigned* group,
|
||||||
xgboost::bst_ulong len) {
|
xgboost::bst_ulong len) {
|
||||||
API_BEGIN();
|
API_BEGIN();
|
||||||
DMatrix *pmat = static_cast<DMatrix*>(handle);
|
std::shared_ptr<DMatrix> *pmat = static_cast<std::shared_ptr<DMatrix>*>(handle);
|
||||||
MetaInfo& info = pmat->info();
|
MetaInfo& info = pmat->get()->info();
|
||||||
info.group_ptr.resize(len + 1);
|
info.group_ptr.resize(len + 1);
|
||||||
info.group_ptr[0] = 0;
|
info.group_ptr[0] = 0;
|
||||||
for (uint64_t i = 0; i < len; ++i) {
|
for (uint64_t i = 0; i < len; ++i) {
|
||||||
@ -422,11 +423,11 @@ XGB_DLL int XGDMatrixSetGroup(DMatrixHandle handle,
|
|||||||
}
|
}
|
||||||
|
|
||||||
XGB_DLL int XGDMatrixGetFloatInfo(const DMatrixHandle handle,
|
XGB_DLL int XGDMatrixGetFloatInfo(const DMatrixHandle handle,
|
||||||
const char* field,
|
const char* field,
|
||||||
xgboost::bst_ulong* out_len,
|
xgboost::bst_ulong* out_len,
|
||||||
const float** out_dptr) {
|
const float** out_dptr) {
|
||||||
API_BEGIN();
|
API_BEGIN();
|
||||||
const MetaInfo& info = static_cast<const DMatrix*>(handle)->info();
|
const MetaInfo& info = static_cast<std::shared_ptr<DMatrix>*>(handle)->get()->info();
|
||||||
const std::vector<float>* vec = nullptr;
|
const std::vector<float>* vec = nullptr;
|
||||||
if (!std::strcmp(field, "label")) {
|
if (!std::strcmp(field, "label")) {
|
||||||
vec = &info.labels;
|
vec = &info.labels;
|
||||||
@ -443,11 +444,11 @@ XGB_DLL int XGDMatrixGetFloatInfo(const DMatrixHandle handle,
|
|||||||
}
|
}
|
||||||
|
|
||||||
XGB_DLL int XGDMatrixGetUIntInfo(const DMatrixHandle handle,
|
XGB_DLL int XGDMatrixGetUIntInfo(const DMatrixHandle handle,
|
||||||
const char *field,
|
const char *field,
|
||||||
xgboost::bst_ulong *out_len,
|
xgboost::bst_ulong *out_len,
|
||||||
const unsigned **out_dptr) {
|
const unsigned **out_dptr) {
|
||||||
API_BEGIN();
|
API_BEGIN();
|
||||||
const MetaInfo& info = static_cast<const DMatrix*>(handle)->info();
|
const MetaInfo& info = static_cast<std::shared_ptr<DMatrix>*>(handle)->get()->info();
|
||||||
const std::vector<unsigned>* vec = nullptr;
|
const std::vector<unsigned>* vec = nullptr;
|
||||||
if (!std::strcmp(field, "root_index")) {
|
if (!std::strcmp(field, "root_index")) {
|
||||||
vec = &info.root_index;
|
vec = &info.root_index;
|
||||||
@ -460,16 +461,18 @@ XGB_DLL int XGDMatrixGetUIntInfo(const DMatrixHandle handle,
|
|||||||
}
|
}
|
||||||
|
|
||||||
XGB_DLL int XGDMatrixNumRow(const DMatrixHandle handle,
|
XGB_DLL int XGDMatrixNumRow(const DMatrixHandle handle,
|
||||||
xgboost::bst_ulong *out) {
|
xgboost::bst_ulong *out) {
|
||||||
API_BEGIN();
|
API_BEGIN();
|
||||||
*out = static_cast<xgboost::bst_ulong>(static_cast<const DMatrix*>(handle)->info().num_row);
|
*out = static_cast<xgboost::bst_ulong>(
|
||||||
|
static_cast<std::shared_ptr<DMatrix>*>(handle)->get()->info().num_row);
|
||||||
API_END();
|
API_END();
|
||||||
}
|
}
|
||||||
|
|
||||||
XGB_DLL int XGDMatrixNumCol(const DMatrixHandle handle,
|
XGB_DLL int XGDMatrixNumCol(const DMatrixHandle handle,
|
||||||
xgboost::bst_ulong *out) {
|
xgboost::bst_ulong *out) {
|
||||||
API_BEGIN();
|
API_BEGIN();
|
||||||
*out = static_cast<size_t>(static_cast<const DMatrix*>(handle)->info().num_col);
|
*out = static_cast<size_t>(
|
||||||
|
static_cast<std::shared_ptr<DMatrix>*>(handle)->get()->info().num_col);
|
||||||
API_END();
|
API_END();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -478,9 +481,9 @@ XGB_DLL int XGBoosterCreate(const DMatrixHandle dmats[],
|
|||||||
xgboost::bst_ulong len,
|
xgboost::bst_ulong len,
|
||||||
BoosterHandle *out) {
|
BoosterHandle *out) {
|
||||||
API_BEGIN();
|
API_BEGIN();
|
||||||
std::vector<DMatrix*> mats;
|
std::vector<std::shared_ptr<DMatrix> > mats;
|
||||||
for (xgboost::bst_ulong i = 0; i < len; ++i) {
|
for (xgboost::bst_ulong i = 0; i < len; ++i) {
|
||||||
mats.push_back(static_cast<DMatrix*>(dmats[i]));
|
mats.push_back(*static_cast<std::shared_ptr<DMatrix>*>(dmats[i]));
|
||||||
}
|
}
|
||||||
*out = new Booster(mats);
|
*out = new Booster(mats);
|
||||||
API_END();
|
API_END();
|
||||||
@ -493,50 +496,52 @@ XGB_DLL int XGBoosterFree(BoosterHandle handle) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
XGB_DLL int XGBoosterSetParam(BoosterHandle handle,
|
XGB_DLL int XGBoosterSetParam(BoosterHandle handle,
|
||||||
const char *name,
|
const char *name,
|
||||||
const char *value) {
|
const char *value) {
|
||||||
API_BEGIN();
|
API_BEGIN();
|
||||||
static_cast<Booster*>(handle)->SetParam(name, value);
|
static_cast<Booster*>(handle)->SetParam(name, value);
|
||||||
API_END();
|
API_END();
|
||||||
}
|
}
|
||||||
|
|
||||||
XGB_DLL int XGBoosterUpdateOneIter(BoosterHandle handle,
|
XGB_DLL int XGBoosterUpdateOneIter(BoosterHandle handle,
|
||||||
int iter,
|
int iter,
|
||||||
DMatrixHandle dtrain) {
|
DMatrixHandle dtrain) {
|
||||||
API_BEGIN();
|
API_BEGIN();
|
||||||
Booster* bst = static_cast<Booster*>(handle);
|
Booster* bst = static_cast<Booster*>(handle);
|
||||||
DMatrix *dtr = static_cast<DMatrix*>(dtrain);
|
std::shared_ptr<DMatrix> *dtr =
|
||||||
|
static_cast<std::shared_ptr<DMatrix>*>(dtrain);
|
||||||
|
|
||||||
bst->LazyInit();
|
bst->LazyInit();
|
||||||
bst->learner()->UpdateOneIter(iter, dtr);
|
bst->learner()->UpdateOneIter(iter, dtr->get());
|
||||||
API_END();
|
API_END();
|
||||||
}
|
}
|
||||||
|
|
||||||
XGB_DLL int XGBoosterBoostOneIter(BoosterHandle handle,
|
XGB_DLL int XGBoosterBoostOneIter(BoosterHandle handle,
|
||||||
DMatrixHandle dtrain,
|
DMatrixHandle dtrain,
|
||||||
float *grad,
|
float *grad,
|
||||||
float *hess,
|
float *hess,
|
||||||
xgboost::bst_ulong len) {
|
xgboost::bst_ulong len) {
|
||||||
std::vector<bst_gpair>& tmp_gpair = XGBAPIThreadLocalStore::Get()->tmp_gpair;
|
std::vector<bst_gpair>& tmp_gpair = XGBAPIThreadLocalStore::Get()->tmp_gpair;
|
||||||
API_BEGIN();
|
API_BEGIN();
|
||||||
Booster* bst = static_cast<Booster*>(handle);
|
Booster* bst = static_cast<Booster*>(handle);
|
||||||
DMatrix* dtr = static_cast<DMatrix*>(dtrain);
|
std::shared_ptr<DMatrix>* dtr =
|
||||||
|
static_cast<std::shared_ptr<DMatrix>*>(dtrain);
|
||||||
tmp_gpair.resize(len);
|
tmp_gpair.resize(len);
|
||||||
for (xgboost::bst_ulong i = 0; i < len; ++i) {
|
for (xgboost::bst_ulong i = 0; i < len; ++i) {
|
||||||
tmp_gpair[i] = bst_gpair(grad[i], hess[i]);
|
tmp_gpair[i] = bst_gpair(grad[i], hess[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
bst->LazyInit();
|
bst->LazyInit();
|
||||||
bst->learner()->BoostOneIter(0, dtr, &tmp_gpair);
|
bst->learner()->BoostOneIter(0, dtr->get(), &tmp_gpair);
|
||||||
API_END();
|
API_END();
|
||||||
}
|
}
|
||||||
|
|
||||||
XGB_DLL int XGBoosterEvalOneIter(BoosterHandle handle,
|
XGB_DLL int XGBoosterEvalOneIter(BoosterHandle handle,
|
||||||
int iter,
|
int iter,
|
||||||
DMatrixHandle dmats[],
|
DMatrixHandle dmats[],
|
||||||
const char* evnames[],
|
const char* evnames[],
|
||||||
xgboost::bst_ulong len,
|
xgboost::bst_ulong len,
|
||||||
const char** out_str) {
|
const char** out_str) {
|
||||||
std::string& eval_str = XGBAPIThreadLocalStore::Get()->ret_str;
|
std::string& eval_str = XGBAPIThreadLocalStore::Get()->ret_str;
|
||||||
API_BEGIN();
|
API_BEGIN();
|
||||||
Booster* bst = static_cast<Booster*>(handle);
|
Booster* bst = static_cast<Booster*>(handle);
|
||||||
@ -544,7 +549,7 @@ XGB_DLL int XGBoosterEvalOneIter(BoosterHandle handle,
|
|||||||
std::vector<std::string> data_names;
|
std::vector<std::string> data_names;
|
||||||
|
|
||||||
for (xgboost::bst_ulong i = 0; i < len; ++i) {
|
for (xgboost::bst_ulong i = 0; i < len; ++i) {
|
||||||
data_sets.push_back(static_cast<DMatrix*>(dmats[i]));
|
data_sets.push_back(static_cast<std::shared_ptr<DMatrix>*>(dmats[i])->get());
|
||||||
data_names.push_back(std::string(evnames[i]));
|
data_names.push_back(std::string(evnames[i]));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -555,17 +560,17 @@ XGB_DLL int XGBoosterEvalOneIter(BoosterHandle handle,
|
|||||||
}
|
}
|
||||||
|
|
||||||
XGB_DLL int XGBoosterPredict(BoosterHandle handle,
|
XGB_DLL int XGBoosterPredict(BoosterHandle handle,
|
||||||
DMatrixHandle dmat,
|
DMatrixHandle dmat,
|
||||||
int option_mask,
|
int option_mask,
|
||||||
unsigned ntree_limit,
|
unsigned ntree_limit,
|
||||||
xgboost::bst_ulong *len,
|
xgboost::bst_ulong *len,
|
||||||
const float **out_result) {
|
const float **out_result) {
|
||||||
std::vector<float>& preds = XGBAPIThreadLocalStore::Get()->ret_vec_float;
|
std::vector<float>& preds = XGBAPIThreadLocalStore::Get()->ret_vec_float;
|
||||||
API_BEGIN();
|
API_BEGIN();
|
||||||
Booster *bst = static_cast<Booster*>(handle);
|
Booster *bst = static_cast<Booster*>(handle);
|
||||||
bst->LazyInit();
|
bst->LazyInit();
|
||||||
bst->learner()->Predict(
|
bst->learner()->Predict(
|
||||||
static_cast<DMatrix*>(dmat),
|
static_cast<std::shared_ptr<DMatrix>*>(dmat)->get(),
|
||||||
(option_mask & 1) != 0,
|
(option_mask & 1) != 0,
|
||||||
&preds, ntree_limit,
|
&preds, ntree_limit,
|
||||||
(option_mask & 2) != 0);
|
(option_mask & 2) != 0);
|
||||||
|
|||||||
@ -156,16 +156,18 @@ void CLITrain(const CLIParam& param) {
|
|||||||
LOG(CONSOLE) << "start " << pname << ":" << rabit::GetRank();
|
LOG(CONSOLE) << "start " << pname << ":" << rabit::GetRank();
|
||||||
}
|
}
|
||||||
// load in data.
|
// load in data.
|
||||||
std::unique_ptr<DMatrix> dtrain(
|
std::shared_ptr<DMatrix> dtrain(
|
||||||
DMatrix::Load(param.train_path, param.silent != 0, param.dsplit == 2));
|
DMatrix::Load(param.train_path, param.silent != 0, param.dsplit == 2));
|
||||||
std::vector<std::unique_ptr<DMatrix> > deval;
|
std::vector<std::shared_ptr<DMatrix> > deval;
|
||||||
std::vector<DMatrix*> cache_mats, eval_datasets;
|
std::vector<std::shared_ptr<DMatrix> > cache_mats;
|
||||||
cache_mats.push_back(dtrain.get());
|
std::vector<DMatrix*> eval_datasets;
|
||||||
|
cache_mats.push_back(dtrain);
|
||||||
for (size_t i = 0; i < param.eval_data_names.size(); ++i) {
|
for (size_t i = 0; i < param.eval_data_names.size(); ++i) {
|
||||||
deval.emplace_back(
|
deval.emplace_back(
|
||||||
DMatrix::Load(param.eval_data_paths[i], param.silent != 0, param.dsplit == 2));
|
std::shared_ptr<DMatrix>(DMatrix::Load(param.eval_data_paths[i],
|
||||||
|
param.silent != 0, param.dsplit == 2)));
|
||||||
eval_datasets.push_back(deval.back().get());
|
eval_datasets.push_back(deval.back().get());
|
||||||
cache_mats.push_back(deval.back().get());
|
cache_mats.push_back(deval.back());
|
||||||
}
|
}
|
||||||
std::vector<std::string> eval_data_names = param.eval_data_names;
|
std::vector<std::string> eval_data_names = param.eval_data_names;
|
||||||
if (param.eval_train) {
|
if (param.eval_train) {
|
||||||
|
|||||||
@ -87,6 +87,9 @@ struct GBLinearTrainParam : public dmlc::Parameter<GBLinearTrainParam> {
|
|||||||
*/
|
*/
|
||||||
class GBLinear : public GradientBooster {
|
class GBLinear : public GradientBooster {
|
||||||
public:
|
public:
|
||||||
|
explicit GBLinear(float base_margin)
|
||||||
|
: base_margin_(base_margin) {
|
||||||
|
}
|
||||||
void Configure(const std::vector<std::pair<std::string, std::string> >& cfg) override {
|
void Configure(const std::vector<std::pair<std::string, std::string> >& cfg) override {
|
||||||
if (model.weight.size() == 0) {
|
if (model.weight.size() == 0) {
|
||||||
model.param.InitAllowUnknown(cfg);
|
model.param.InitAllowUnknown(cfg);
|
||||||
@ -99,9 +102,9 @@ class GBLinear : public GradientBooster {
|
|||||||
void Save(dmlc::Stream* fo) const override {
|
void Save(dmlc::Stream* fo) const override {
|
||||||
model.Save(fo);
|
model.Save(fo);
|
||||||
}
|
}
|
||||||
virtual void DoBoost(DMatrix *p_fmat,
|
void DoBoost(DMatrix *p_fmat,
|
||||||
int64_t buffer_offset,
|
std::vector<bst_gpair> *in_gpair,
|
||||||
std::vector<bst_gpair> *in_gpair) {
|
ObjFunction* obj) override {
|
||||||
// lazily initialize the model when not ready.
|
// lazily initialize the model when not ready.
|
||||||
if (model.weight.size() == 0) {
|
if (model.weight.size() == 0) {
|
||||||
model.InitModel();
|
model.InitModel();
|
||||||
@ -168,7 +171,6 @@ class GBLinear : public GradientBooster {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Predict(DMatrix *p_fmat,
|
void Predict(DMatrix *p_fmat,
|
||||||
int64_t buffer_offset,
|
|
||||||
std::vector<float> *out_preds,
|
std::vector<float> *out_preds,
|
||||||
unsigned ntree_limit) override {
|
unsigned ntree_limit) override {
|
||||||
if (model.weight.size() == 0) {
|
if (model.weight.size() == 0) {
|
||||||
@ -177,6 +179,11 @@ class GBLinear : public GradientBooster {
|
|||||||
CHECK_EQ(ntree_limit, 0)
|
CHECK_EQ(ntree_limit, 0)
|
||||||
<< "GBLinear::Predict ntrees is only valid for gbtree predictor";
|
<< "GBLinear::Predict ntrees is only valid for gbtree predictor";
|
||||||
std::vector<float> &preds = *out_preds;
|
std::vector<float> &preds = *out_preds;
|
||||||
|
const std::vector<bst_float>& base_margin = p_fmat->info().base_margin;
|
||||||
|
if (base_margin.size() != 0) {
|
||||||
|
CHECK_EQ(preds.size(), base_margin.size())
|
||||||
|
<< "base_margin.size does not match with prediction size";
|
||||||
|
}
|
||||||
preds.resize(0);
|
preds.resize(0);
|
||||||
// start collecting the prediction
|
// start collecting the prediction
|
||||||
dmlc::DataIter<RowBatch> *iter = p_fmat->RowIterator();
|
dmlc::DataIter<RowBatch> *iter = p_fmat->RowIterator();
|
||||||
@ -188,24 +195,27 @@ class GBLinear : public GradientBooster {
|
|||||||
// k is number of group
|
// k is number of group
|
||||||
preds.resize(preds.size() + batch.size * ngroup);
|
preds.resize(preds.size() + batch.size * ngroup);
|
||||||
// parallel over local batch
|
// parallel over local batch
|
||||||
const bst_omp_uint nsize = static_cast<bst_omp_uint>(batch.size);
|
const omp_ulong nsize = static_cast<omp_ulong>(batch.size);
|
||||||
#pragma omp parallel for schedule(static)
|
#pragma omp parallel for schedule(static)
|
||||||
for (bst_omp_uint i = 0; i < nsize; ++i) {
|
for (omp_ulong i = 0; i < nsize; ++i) {
|
||||||
const size_t ridx = batch.base_rowid + i;
|
const size_t ridx = batch.base_rowid + i;
|
||||||
// loop over output groups
|
// loop over output groups
|
||||||
for (int gid = 0; gid < ngroup; ++gid) {
|
for (int gid = 0; gid < ngroup; ++gid) {
|
||||||
this->Pred(batch[i], &preds[ridx * ngroup], gid);
|
float margin = (base_margin.size() != 0) ?
|
||||||
|
base_margin[ridx * ngroup + gid] : base_margin_;
|
||||||
|
this->Pred(batch[i], &preds[ridx * ngroup], gid, margin);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// add base margin
|
||||||
void Predict(const SparseBatch::Inst &inst,
|
void Predict(const SparseBatch::Inst &inst,
|
||||||
std::vector<float> *out_preds,
|
std::vector<float> *out_preds,
|
||||||
unsigned ntree_limit,
|
unsigned ntree_limit,
|
||||||
unsigned root_index) override {
|
unsigned root_index) override {
|
||||||
const int ngroup = model.param.num_output_group;
|
const int ngroup = model.param.num_output_group;
|
||||||
for (int gid = 0; gid < ngroup; ++gid) {
|
for (int gid = 0; gid < ngroup; ++gid) {
|
||||||
this->Pred(inst, dmlc::BeginPtr(*out_preds), gid);
|
this->Pred(inst, dmlc::BeginPtr(*out_preds), gid, base_margin_);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void PredictLeaf(DMatrix *p_fmat,
|
void PredictLeaf(DMatrix *p_fmat,
|
||||||
@ -232,8 +242,8 @@ class GBLinear : public GradientBooster {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
inline void Pred(const RowBatch::Inst &inst, float *preds, int gid) {
|
inline void Pred(const RowBatch::Inst &inst, float *preds, int gid, float base) {
|
||||||
float psum = model.bias()[gid];
|
float psum = model.bias()[gid] + base;
|
||||||
for (bst_uint i = 0; i < inst.length; ++i) {
|
for (bst_uint i = 0; i < inst.length; ++i) {
|
||||||
if (inst[i].index >= model.param.num_feature) continue;
|
if (inst[i].index >= model.param.num_feature) continue;
|
||||||
psum += inst[i].fvalue * model[inst[i].index][gid];
|
psum += inst[i].fvalue * model[inst[i].index][gid];
|
||||||
@ -278,6 +288,8 @@ class GBLinear : public GradientBooster {
|
|||||||
return &weight[i * param.num_output_group];
|
return &weight[i * param.num_output_group];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
// biase margin score
|
||||||
|
float base_margin_;
|
||||||
// model field
|
// model field
|
||||||
Model model;
|
Model model;
|
||||||
// training parameter
|
// training parameter
|
||||||
@ -292,8 +304,8 @@ DMLC_REGISTER_PARAMETER(GBLinearTrainParam);
|
|||||||
|
|
||||||
XGBOOST_REGISTER_GBM(GBLinear, "gblinear")
|
XGBOOST_REGISTER_GBM(GBLinear, "gblinear")
|
||||||
.describe("Linear booster, implement generalized linear model.")
|
.describe("Linear booster, implement generalized linear model.")
|
||||||
.set_body([]() {
|
.set_body([](const std::vector<std::shared_ptr<DMatrix> >&cache, float base_margin) {
|
||||||
return new GBLinear();
|
return new GBLinear(base_margin);
|
||||||
});
|
});
|
||||||
} // namespace gbm
|
} // namespace gbm
|
||||||
} // namespace xgboost
|
} // namespace xgboost
|
||||||
|
|||||||
@ -11,12 +11,15 @@ DMLC_REGISTRY_ENABLE(::xgboost::GradientBoosterReg);
|
|||||||
} // namespace dmlc
|
} // namespace dmlc
|
||||||
|
|
||||||
namespace xgboost {
|
namespace xgboost {
|
||||||
GradientBooster* GradientBooster::Create(const std::string& name) {
|
GradientBooster* GradientBooster::Create(
|
||||||
|
const std::string& name,
|
||||||
|
const std::vector<std::shared_ptr<DMatrix> >& cache_mats,
|
||||||
|
float base_margin) {
|
||||||
auto *e = ::dmlc::Registry< ::xgboost::GradientBoosterReg>::Get()->Find(name);
|
auto *e = ::dmlc::Registry< ::xgboost::GradientBoosterReg>::Get()->Find(name);
|
||||||
if (e == nullptr) {
|
if (e == nullptr) {
|
||||||
LOG(FATAL) << "Unknown gbm type " << name;
|
LOG(FATAL) << "Unknown gbm type " << name;
|
||||||
}
|
}
|
||||||
return (e->body)();
|
return (e->body)(cache_mats, base_margin);
|
||||||
}
|
}
|
||||||
} // namespace xgboost
|
} // namespace xgboost
|
||||||
|
|
||||||
|
|||||||
@ -15,6 +15,7 @@
|
|||||||
#include <utility>
|
#include <utility>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
|
#include <algorithm>
|
||||||
#include "../common/common.h"
|
#include "../common/common.h"
|
||||||
|
|
||||||
#include "../common/random.h"
|
#include "../common/random.h"
|
||||||
@ -123,10 +124,24 @@ struct GBTreeModelParam : public dmlc::Parameter<GBTreeModelParam> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// cache entry
|
||||||
|
struct CacheEntry {
|
||||||
|
std::shared_ptr<DMatrix> data;
|
||||||
|
std::vector<float> predictions;
|
||||||
|
};
|
||||||
|
|
||||||
// gradient boosted trees
|
// gradient boosted trees
|
||||||
class GBTree : public GradientBooster {
|
class GBTree : public GradientBooster {
|
||||||
public:
|
public:
|
||||||
GBTree() : num_pbuffer(0) {}
|
explicit GBTree(float base_margin) : base_margin_(base_margin) {}
|
||||||
|
|
||||||
|
void InitCache(const std::vector<std::shared_ptr<DMatrix> > &cache) {
|
||||||
|
for (const std::shared_ptr<DMatrix>& d : cache) {
|
||||||
|
CacheEntry e;
|
||||||
|
e.data = d;
|
||||||
|
cache_[d.get()] = std::move(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Configure(const std::vector<std::pair<std::string, std::string> >& cfg) override {
|
void Configure(const std::vector<std::pair<std::string, std::string> >& cfg) override {
|
||||||
this->cfg = cfg;
|
this->cfg = cfg;
|
||||||
@ -160,8 +175,6 @@ class GBTree : public GradientBooster {
|
|||||||
this->cfg.clear();
|
this->cfg.clear();
|
||||||
this->cfg.push_back(std::make_pair(std::string("num_feature"),
|
this->cfg.push_back(std::make_pair(std::string("num_feature"),
|
||||||
common::ToString(mparam.num_feature)));
|
common::ToString(mparam.num_feature)));
|
||||||
// clear the predict buffer.
|
|
||||||
this->ResetPredBuffer(num_pbuffer);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Save(dmlc::Stream* fo) const override {
|
void Save(dmlc::Stream* fo) const override {
|
||||||
@ -175,27 +188,19 @@ class GBTree : public GradientBooster {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ResetPredBuffer(size_t num_pbuffer) override {
|
|
||||||
this->num_pbuffer = num_pbuffer;
|
|
||||||
pred_buffer.clear();
|
|
||||||
pred_counter.clear();
|
|
||||||
pred_buffer.resize(this->PredBufferSize(), 0.0f);
|
|
||||||
pred_counter.resize(this->PredBufferSize(), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AllowLazyCheckPoint() const override {
|
bool AllowLazyCheckPoint() const override {
|
||||||
return mparam.num_output_group == 1 ||
|
return mparam.num_output_group == 1 ||
|
||||||
tparam.updater_seq.find("distcol") != std::string::npos;
|
tparam.updater_seq.find("distcol") != std::string::npos;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DoBoost(DMatrix* p_fmat,
|
void DoBoost(DMatrix* p_fmat,
|
||||||
int64_t buffer_offset,
|
std::vector<bst_gpair>* in_gpair,
|
||||||
std::vector<bst_gpair>* in_gpair) override {
|
ObjFunction* obj) override {
|
||||||
const std::vector<bst_gpair>& gpair = *in_gpair;
|
const std::vector<bst_gpair>& gpair = *in_gpair;
|
||||||
std::vector<std::vector<std::unique_ptr<RegTree> > > new_trees;
|
std::vector<std::vector<std::unique_ptr<RegTree> > > new_trees;
|
||||||
if (mparam.num_output_group == 1) {
|
if (mparam.num_output_group == 1) {
|
||||||
std::vector<std::unique_ptr<RegTree> > ret;
|
std::vector<std::unique_ptr<RegTree> > ret;
|
||||||
BoostNewTrees(gpair, p_fmat, buffer_offset, 0, &ret);
|
BoostNewTrees(gpair, p_fmat, 0, &ret);
|
||||||
new_trees.push_back(std::move(ret));
|
new_trees.push_back(std::move(ret));
|
||||||
} else {
|
} else {
|
||||||
const int ngroup = mparam.num_output_group;
|
const int ngroup = mparam.num_output_group;
|
||||||
@ -209,7 +214,7 @@ class GBTree : public GradientBooster {
|
|||||||
tmp[i] = gpair[i * ngroup + gid];
|
tmp[i] = gpair[i * ngroup + gid];
|
||||||
}
|
}
|
||||||
std::vector<std::unique_ptr<RegTree> > ret;
|
std::vector<std::unique_ptr<RegTree> > ret;
|
||||||
BoostNewTrees(tmp, p_fmat, buffer_offset, gid, &ret);
|
BoostNewTrees(tmp, p_fmat, gid, &ret);
|
||||||
new_trees.push_back(std::move(ret));
|
new_trees.push_back(std::move(ret));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -219,48 +224,21 @@ class GBTree : public GradientBooster {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Predict(DMatrix* p_fmat,
|
void Predict(DMatrix* p_fmat,
|
||||||
int64_t buffer_offset,
|
|
||||||
std::vector<float>* out_preds,
|
std::vector<float>* out_preds,
|
||||||
unsigned ntree_limit) override {
|
unsigned ntree_limit) override {
|
||||||
const MetaInfo& info = p_fmat->info();
|
if (ntree_limit == 0 ||
|
||||||
int nthread;
|
ntree_limit * mparam.num_output_group >= trees.size()) {
|
||||||
#pragma omp parallel
|
auto it = cache_.find(p_fmat);
|
||||||
{
|
if (it != cache_.end()) {
|
||||||
nthread = omp_get_num_threads();
|
std::vector<float>& y = it->second.predictions;
|
||||||
}
|
if (y.size() != 0) {
|
||||||
InitThreadTemp(nthread);
|
out_preds->resize(y.size());
|
||||||
std::vector<float> &preds = *out_preds;
|
std::copy(y.begin(), y.end(), out_preds->begin());
|
||||||
const size_t stride = p_fmat->info().num_row * mparam.num_output_group;
|
return;
|
||||||
preds.resize(stride * (mparam.size_leaf_vector+1));
|
|
||||||
// start collecting the prediction
|
|
||||||
dmlc::DataIter<RowBatch>* iter = p_fmat->RowIterator();
|
|
||||||
|
|
||||||
iter->BeforeFirst();
|
|
||||||
while (iter->Next()) {
|
|
||||||
const RowBatch &batch = iter->Value();
|
|
||||||
// parallel over local batch
|
|
||||||
const bst_omp_uint nsize = static_cast<bst_omp_uint>(batch.size);
|
|
||||||
int ridx_error = 0;
|
|
||||||
#pragma omp parallel for schedule(static)
|
|
||||||
for (bst_omp_uint i = 0; i < nsize; ++i) {
|
|
||||||
const int tid = omp_get_thread_num();
|
|
||||||
RegTree::FVec &feats = thread_temp[tid];
|
|
||||||
int64_t ridx = static_cast<int64_t>(batch.base_rowid + i);
|
|
||||||
if (static_cast<size_t>(ridx) >= info.num_row) {
|
|
||||||
ridx_error = 1;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// loop over output groups
|
|
||||||
for (int gid = 0; gid < mparam.num_output_group; ++gid) {
|
|
||||||
this->Pred(batch[i],
|
|
||||||
buffer_offset < 0 ? -1 : buffer_offset + ridx,
|
|
||||||
gid, info.GetRoot(ridx), &feats,
|
|
||||||
&preds[ridx * mparam.num_output_group + gid], stride,
|
|
||||||
ntree_limit);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
CHECK(!ridx_error) << "ridx out of bounds";
|
|
||||||
}
|
}
|
||||||
|
PredLoopInternal<GBTree>(p_fmat, out_preds, 0, ntree_limit, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Predict(const SparseBatch::Inst& inst,
|
void Predict(const SparseBatch::Inst& inst,
|
||||||
@ -271,12 +249,16 @@ class GBTree : public GradientBooster {
|
|||||||
thread_temp.resize(1, RegTree::FVec());
|
thread_temp.resize(1, RegTree::FVec());
|
||||||
thread_temp[0].Init(mparam.num_feature);
|
thread_temp[0].Init(mparam.num_feature);
|
||||||
}
|
}
|
||||||
|
ntree_limit *= mparam.num_output_group;
|
||||||
|
if (ntree_limit == 0 || ntree_limit > trees.size()) {
|
||||||
|
ntree_limit = static_cast<unsigned>(trees.size());
|
||||||
|
}
|
||||||
out_preds->resize(mparam.num_output_group * (mparam.size_leaf_vector+1));
|
out_preds->resize(mparam.num_output_group * (mparam.size_leaf_vector+1));
|
||||||
// loop over output groups
|
// loop over output groups
|
||||||
for (int gid = 0; gid < mparam.num_output_group; ++gid) {
|
for (int gid = 0; gid < mparam.num_output_group; ++gid) {
|
||||||
this->Pred(inst, -1, gid, root_index, &thread_temp[0],
|
(*out_preds)[gid] =
|
||||||
&(*out_preds)[gid], mparam.num_output_group,
|
PredValue(inst, gid, root_index,
|
||||||
ntree_limit);
|
&thread_temp[0], 0, ntree_limit) + base_margin_;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -301,6 +283,84 @@ class GBTree : public GradientBooster {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
// internal prediction loop
|
||||||
|
// add predictions to out_preds
|
||||||
|
template<typename Derived>
|
||||||
|
inline void PredLoopInternal(
|
||||||
|
DMatrix* p_fmat,
|
||||||
|
std::vector<float>* out_preds,
|
||||||
|
unsigned tree_begin,
|
||||||
|
unsigned ntree_limit,
|
||||||
|
bool init_out_preds) {
|
||||||
|
int num_group = mparam.num_output_group;
|
||||||
|
ntree_limit *= num_group;
|
||||||
|
if (ntree_limit == 0 || ntree_limit > trees.size()) {
|
||||||
|
ntree_limit = static_cast<unsigned>(trees.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (init_out_preds) {
|
||||||
|
size_t n = num_group * p_fmat->info().num_row;
|
||||||
|
const std::vector<float>& base_margin = p_fmat->info().base_margin;
|
||||||
|
out_preds->resize(n);
|
||||||
|
if (base_margin.size() != 0) {
|
||||||
|
CHECK_EQ(out_preds->size(), n);
|
||||||
|
std::copy(base_margin.begin(), base_margin.end(), out_preds->begin());
|
||||||
|
} else {
|
||||||
|
std::fill(out_preds->begin(), out_preds->end(), base_margin_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (num_group == 1) {
|
||||||
|
PredLoopSpecalize<Derived>(p_fmat, out_preds, 1,
|
||||||
|
tree_begin, ntree_limit);
|
||||||
|
} else {
|
||||||
|
PredLoopSpecalize<Derived>(p_fmat, out_preds, num_group,
|
||||||
|
tree_begin, ntree_limit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Derived>
|
||||||
|
inline void PredLoopSpecalize(
|
||||||
|
DMatrix* p_fmat,
|
||||||
|
std::vector<float>* out_preds,
|
||||||
|
int num_group,
|
||||||
|
unsigned tree_begin,
|
||||||
|
unsigned tree_end) {
|
||||||
|
const MetaInfo& info = p_fmat->info();
|
||||||
|
int nthread;
|
||||||
|
#pragma omp parallel
|
||||||
|
{
|
||||||
|
nthread = omp_get_num_threads();
|
||||||
|
}
|
||||||
|
CHECK_EQ(num_group, mparam.num_output_group);
|
||||||
|
InitThreadTemp(nthread);
|
||||||
|
std::vector<float> &preds = *out_preds;
|
||||||
|
CHECK_EQ(mparam.size_leaf_vector, 0)
|
||||||
|
<< "size_leaf_vector is enforced to 0 so far";
|
||||||
|
CHECK_EQ(preds.size(), p_fmat->info().num_row * num_group);
|
||||||
|
// start collecting the prediction
|
||||||
|
dmlc::DataIter<RowBatch>* iter = p_fmat->RowIterator();
|
||||||
|
Derived* self = static_cast<Derived*>(this);
|
||||||
|
iter->BeforeFirst();
|
||||||
|
while (iter->Next()) {
|
||||||
|
const RowBatch &batch = iter->Value();
|
||||||
|
// parallel over local batch
|
||||||
|
const bst_omp_uint nsize = static_cast<bst_omp_uint>(batch.size);
|
||||||
|
#pragma omp parallel for schedule(static)
|
||||||
|
for (bst_omp_uint i = 0; i < nsize; ++i) {
|
||||||
|
const int tid = omp_get_thread_num();
|
||||||
|
RegTree::FVec &feats = thread_temp[tid];
|
||||||
|
int64_t ridx = static_cast<int64_t>(batch.base_rowid + i);
|
||||||
|
CHECK_LT(static_cast<size_t>(ridx), info.num_row);
|
||||||
|
for (int gid = 0; gid < num_group; ++gid) {
|
||||||
|
size_t offset = ridx * num_group + gid;
|
||||||
|
preds[offset] +=
|
||||||
|
self->PredValue(batch[i], gid, info.GetRoot(ridx),
|
||||||
|
&feats, tree_begin, tree_end);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
// initialize updater before using them
|
// initialize updater before using them
|
||||||
inline void InitUpdater() {
|
inline void InitUpdater() {
|
||||||
if (updaters.size() != 0) return;
|
if (updaters.size() != 0) return;
|
||||||
@ -316,7 +376,6 @@ class GBTree : public GradientBooster {
|
|||||||
inline void
|
inline void
|
||||||
BoostNewTrees(const std::vector<bst_gpair> &gpair,
|
BoostNewTrees(const std::vector<bst_gpair> &gpair,
|
||||||
DMatrix *p_fmat,
|
DMatrix *p_fmat,
|
||||||
int64_t buffer_offset,
|
|
||||||
int bst_group,
|
int bst_group,
|
||||||
std::vector<std::unique_ptr<RegTree> >* ret) {
|
std::vector<std::unique_ptr<RegTree> >* ret) {
|
||||||
this->InitUpdater();
|
this->InitUpdater();
|
||||||
@ -334,111 +393,50 @@ class GBTree : public GradientBooster {
|
|||||||
for (auto& up : updaters) {
|
for (auto& up : updaters) {
|
||||||
up->Update(gpair, p_fmat, new_trees);
|
up->Update(gpair, p_fmat, new_trees);
|
||||||
}
|
}
|
||||||
// optimization, update buffer, if possible
|
|
||||||
// this is only under distributed column mode
|
|
||||||
// for safety check of lazy checkpoint
|
|
||||||
if (buffer_offset >= 0 &&
|
|
||||||
new_trees.size() == 1 && updaters.size() > 0 &&
|
|
||||||
updaters.back()->GetLeafPosition() != nullptr) {
|
|
||||||
CHECK_EQ(p_fmat->info().num_row, p_fmat->buffered_rowset().size());
|
|
||||||
this->UpdateBufferByPosition(p_fmat,
|
|
||||||
buffer_offset,
|
|
||||||
bst_group,
|
|
||||||
*new_trees[0],
|
|
||||||
updaters.back()->GetLeafPosition());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// commit new trees all at once
|
// commit new trees all at once
|
||||||
virtual void
|
virtual void
|
||||||
CommitModel(std::vector<std::unique_ptr<RegTree> >&& new_trees,
|
CommitModel(std::vector<std::unique_ptr<RegTree> >&& new_trees,
|
||||||
int bst_group) {
|
int bst_group) {
|
||||||
|
size_t old_ntree = trees.size();
|
||||||
for (size_t i = 0; i < new_trees.size(); ++i) {
|
for (size_t i = 0; i < new_trees.size(); ++i) {
|
||||||
trees.push_back(std::move(new_trees[i]));
|
trees.push_back(std::move(new_trees[i]));
|
||||||
tree_info.push_back(bst_group);
|
tree_info.push_back(bst_group);
|
||||||
}
|
}
|
||||||
mparam.num_trees += static_cast<int>(new_trees.size());
|
mparam.num_trees += static_cast<int>(new_trees.size());
|
||||||
}
|
|
||||||
// update buffer by pre-cached position
|
// update cache entry
|
||||||
inline void UpdateBufferByPosition(DMatrix *p_fmat,
|
for (auto &kv : cache_) {
|
||||||
int64_t buffer_offset,
|
CacheEntry& e = kv.second;
|
||||||
int bst_group,
|
if (e.predictions.size() == 0) {
|
||||||
const RegTree &new_tree,
|
PredLoopInternal<GBTree>(
|
||||||
const int* leaf_position) {
|
e.data.get(), &(e.predictions),
|
||||||
const RowSet& rowset = p_fmat->buffered_rowset();
|
0, trees.size(), true);
|
||||||
const bst_omp_uint ndata = static_cast<bst_omp_uint>(rowset.size());
|
} else {
|
||||||
int pred_counter_error = 0, tid_error = 0;
|
PredLoopInternal<GBTree>(
|
||||||
#pragma omp parallel for schedule(static)
|
e.data.get(), &(e.predictions),
|
||||||
for (bst_omp_uint i = 0; i < ndata; ++i) {
|
old_ntree, trees.size(), false);
|
||||||
const bst_uint ridx = rowset[i];
|
|
||||||
const int64_t bid = this->BufferOffset(buffer_offset + ridx, bst_group);
|
|
||||||
const int tid = leaf_position[ridx];
|
|
||||||
if (pred_counter[bid] != trees.size()) {
|
|
||||||
pred_counter_error = 1;
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
if (tid < 0) {
|
|
||||||
tid_error = 1;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
pred_buffer[bid] += new_tree[tid].leaf_value();
|
|
||||||
for (int i = 0; i < mparam.size_leaf_vector; ++i) {
|
|
||||||
pred_buffer[bid + i + 1] += new_tree.leafvec(tid)[i];
|
|
||||||
}
|
|
||||||
pred_counter[bid] += tparam.num_parallel_tree;
|
|
||||||
}
|
}
|
||||||
CHECK(!pred_counter_error) << "incorrect pred_counter[bid]";
|
|
||||||
CHECK(!tid_error) << "tid cannot be negative";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// make a prediction for a single instance
|
// make a prediction for a single instance
|
||||||
inline void Pred(const RowBatch::Inst &inst,
|
inline float PredValue(const RowBatch::Inst &inst,
|
||||||
int64_t buffer_index,
|
int bst_group,
|
||||||
int bst_group,
|
unsigned root_index,
|
||||||
unsigned root_index,
|
RegTree::FVec *p_feats,
|
||||||
RegTree::FVec *p_feats,
|
unsigned tree_begin,
|
||||||
float *out_pred,
|
unsigned tree_end) {
|
||||||
size_t stride,
|
float psum = 0.0f;
|
||||||
unsigned ntree_limit) {
|
p_feats->Fill(inst);
|
||||||
size_t itop = 0;
|
for (size_t i = tree_begin; i < tree_end; ++i) {
|
||||||
float psum = 0.0f;
|
if (tree_info[i] == bst_group) {
|
||||||
// sum of leaf vector
|
int tid = trees[i]->GetLeafIndex(*p_feats, root_index);
|
||||||
std::vector<float> vec_psum(mparam.size_leaf_vector, 0.0f);
|
psum += (*trees[i])[tid].leaf_value();
|
||||||
const int64_t bid = this->BufferOffset(buffer_index, bst_group);
|
|
||||||
// number of valid trees
|
|
||||||
unsigned treeleft = ntree_limit == 0 ? std::numeric_limits<unsigned>::max() : ntree_limit;
|
|
||||||
// load buffered results if any
|
|
||||||
if (bid >= 0 && ntree_limit == 0) {
|
|
||||||
itop = pred_counter[bid];
|
|
||||||
psum = pred_buffer[bid];
|
|
||||||
for (int i = 0; i < mparam.size_leaf_vector; ++i) {
|
|
||||||
vec_psum[i] = pred_buffer[bid + i + 1];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (itop != trees.size()) {
|
p_feats->Drop(inst);
|
||||||
p_feats->Fill(inst);
|
return psum;
|
||||||
for (size_t i = itop; i < trees.size(); ++i) {
|
|
||||||
if (tree_info[i] == bst_group) {
|
|
||||||
int tid = trees[i]->GetLeafIndex(*p_feats, root_index);
|
|
||||||
psum += (*trees[i])[tid].leaf_value();
|
|
||||||
for (int j = 0; j < mparam.size_leaf_vector; ++j) {
|
|
||||||
vec_psum[j] += trees[i]->leafvec(tid)[j];
|
|
||||||
}
|
|
||||||
if (--treeleft == 0) break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
p_feats->Drop(inst);
|
|
||||||
}
|
|
||||||
// updated the buffered results
|
|
||||||
if (bid >= 0 && ntree_limit == 0) {
|
|
||||||
pred_counter[bid] = static_cast<unsigned>(trees.size());
|
|
||||||
pred_buffer[bid] = psum;
|
|
||||||
for (int i = 0; i < mparam.size_leaf_vector; ++i) {
|
|
||||||
pred_buffer[bid + i + 1] = vec_psum[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
out_pred[0] = psum;
|
|
||||||
for (int i = 0; i < mparam.size_leaf_vector; ++i) {
|
|
||||||
out_pred[stride * (i + 1)] = vec_psum[i];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// predict independent leaf index
|
// predict independent leaf index
|
||||||
inline void PredPath(DMatrix *p_fmat,
|
inline void PredPath(DMatrix *p_fmat,
|
||||||
@ -446,6 +444,7 @@ class GBTree : public GradientBooster {
|
|||||||
unsigned ntree_limit) {
|
unsigned ntree_limit) {
|
||||||
const MetaInfo& info = p_fmat->info();
|
const MetaInfo& info = p_fmat->info();
|
||||||
// number of valid trees
|
// number of valid trees
|
||||||
|
ntree_limit *= mparam.num_output_group;
|
||||||
if (ntree_limit == 0 || ntree_limit > trees.size()) {
|
if (ntree_limit == 0 || ntree_limit > trees.size()) {
|
||||||
ntree_limit = static_cast<unsigned>(trees.size());
|
ntree_limit = static_cast<unsigned>(trees.size());
|
||||||
}
|
}
|
||||||
@ -482,22 +481,9 @@ class GBTree : public GradientBooster {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/*! \return size of prediction buffer actually needed */
|
|
||||||
inline size_t PredBufferSize() const {
|
|
||||||
return mparam.num_output_group * num_pbuffer * (mparam.size_leaf_vector + 1);
|
|
||||||
}
|
|
||||||
/*!
|
|
||||||
* \brief get the buffer offset given a buffer index and group id
|
|
||||||
* \return calculated buffer offset
|
|
||||||
*/
|
|
||||||
inline int64_t BufferOffset(int64_t buffer_index, int bst_group) const {
|
|
||||||
if (buffer_index < 0) return -1;
|
|
||||||
size_t bidx = static_cast<size_t>(buffer_index);
|
|
||||||
CHECK_LT(bidx, num_pbuffer);
|
|
||||||
return (bidx + num_pbuffer * bst_group) * (mparam.size_leaf_vector + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- data structure ---
|
// --- data structure ---
|
||||||
|
// base margin
|
||||||
|
float base_margin_;
|
||||||
// training parameter
|
// training parameter
|
||||||
GBTreeTrainParam tparam;
|
GBTreeTrainParam tparam;
|
||||||
// model parameter
|
// model parameter
|
||||||
@ -506,13 +492,8 @@ class GBTree : public GradientBooster {
|
|||||||
std::vector<std::unique_ptr<RegTree> > trees;
|
std::vector<std::unique_ptr<RegTree> > trees;
|
||||||
/*! \brief some information indicator of the tree, reserved */
|
/*! \brief some information indicator of the tree, reserved */
|
||||||
std::vector<int> tree_info;
|
std::vector<int> tree_info;
|
||||||
/*! \brief predict buffer size */
|
|
||||||
size_t num_pbuffer;
|
|
||||||
/*! \brief prediction buffer */
|
|
||||||
std::vector<float> pred_buffer;
|
|
||||||
/*! \brief prediction buffer counter, remember the prediction */
|
|
||||||
std::vector<unsigned> pred_counter;
|
|
||||||
// ----training fields----
|
// ----training fields----
|
||||||
|
std::unordered_map<DMatrix*, CacheEntry> cache_;
|
||||||
// configurations for tree
|
// configurations for tree
|
||||||
std::vector<std::pair<std::string, std::string> > cfg;
|
std::vector<std::pair<std::string, std::string> > cfg;
|
||||||
// temporal storage for per thread
|
// temporal storage for per thread
|
||||||
@ -524,7 +505,7 @@ class GBTree : public GradientBooster {
|
|||||||
// dart
|
// dart
|
||||||
class Dart : public GBTree {
|
class Dart : public GBTree {
|
||||||
public:
|
public:
|
||||||
Dart() {}
|
explicit Dart(float base_margin) : GBTree(base_margin) {}
|
||||||
|
|
||||||
void Configure(const std::vector<std::pair<std::string, std::string> >& cfg) override {
|
void Configure(const std::vector<std::pair<std::string, std::string> >& cfg) override {
|
||||||
GBTree::Configure(cfg);
|
GBTree::Configure(cfg);
|
||||||
@ -550,44 +531,10 @@ class Dart : public GBTree {
|
|||||||
|
|
||||||
// predict the leaf scores with dropout if ntree_limit = 0
|
// predict the leaf scores with dropout if ntree_limit = 0
|
||||||
void Predict(DMatrix* p_fmat,
|
void Predict(DMatrix* p_fmat,
|
||||||
int64_t buffer_offset,
|
|
||||||
std::vector<float>* out_preds,
|
std::vector<float>* out_preds,
|
||||||
unsigned ntree_limit) override {
|
unsigned ntree_limit) override {
|
||||||
DropTrees(ntree_limit);
|
DropTrees(ntree_limit);
|
||||||
const MetaInfo& info = p_fmat->info();
|
PredLoopInternal<Dart>(p_fmat, out_preds, 0, ntree_limit, true);
|
||||||
int nthread;
|
|
||||||
#pragma omp parallel
|
|
||||||
{
|
|
||||||
nthread = omp_get_num_threads();
|
|
||||||
}
|
|
||||||
InitThreadTemp(nthread);
|
|
||||||
std::vector<float> &preds = *out_preds;
|
|
||||||
const size_t stride = p_fmat->info().num_row * mparam.num_output_group;
|
|
||||||
preds.resize(stride * (mparam.size_leaf_vector+1));
|
|
||||||
// start collecting the prediction
|
|
||||||
dmlc::DataIter<RowBatch>* iter = p_fmat->RowIterator();
|
|
||||||
|
|
||||||
iter->BeforeFirst();
|
|
||||||
while (iter->Next()) {
|
|
||||||
const RowBatch &batch = iter->Value();
|
|
||||||
// parallel over local batch
|
|
||||||
const bst_omp_uint nsize = static_cast<bst_omp_uint>(batch.size);
|
|
||||||
#pragma omp parallel for schedule(static)
|
|
||||||
for (bst_omp_uint i = 0; i < nsize; ++i) {
|
|
||||||
const int tid = omp_get_thread_num();
|
|
||||||
RegTree::FVec &feats = thread_temp[tid];
|
|
||||||
int64_t ridx = static_cast<int64_t>(batch.base_rowid + i);
|
|
||||||
CHECK_LT(static_cast<size_t>(ridx), info.num_row);
|
|
||||||
// loop over output groups
|
|
||||||
for (int gid = 0; gid < mparam.num_output_group; ++gid) {
|
|
||||||
this->Pred(batch[i],
|
|
||||||
buffer_offset < 0 ? -1 : buffer_offset + ridx,
|
|
||||||
gid, info.GetRoot(ridx), &feats,
|
|
||||||
&preds[ridx * mparam.num_output_group + gid], stride,
|
|
||||||
ntree_limit);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Predict(const SparseBatch::Inst& inst,
|
void Predict(const SparseBatch::Inst& inst,
|
||||||
@ -599,20 +546,24 @@ class Dart : public GBTree {
|
|||||||
thread_temp.resize(1, RegTree::FVec());
|
thread_temp.resize(1, RegTree::FVec());
|
||||||
thread_temp[0].Init(mparam.num_feature);
|
thread_temp[0].Init(mparam.num_feature);
|
||||||
}
|
}
|
||||||
out_preds->resize(mparam.num_output_group * (mparam.size_leaf_vector+1));
|
out_preds->resize(mparam.num_output_group);
|
||||||
|
ntree_limit *= mparam.num_output_group;
|
||||||
|
if (ntree_limit == 0 || ntree_limit > trees.size()) {
|
||||||
|
ntree_limit = static_cast<unsigned>(trees.size());
|
||||||
|
}
|
||||||
// loop over output groups
|
// loop over output groups
|
||||||
for (int gid = 0; gid < mparam.num_output_group; ++gid) {
|
for (int gid = 0; gid < mparam.num_output_group; ++gid) {
|
||||||
this->Pred(inst, -1, gid, root_index, &thread_temp[0],
|
(*out_preds)[gid]
|
||||||
&(*out_preds)[gid], mparam.num_output_group,
|
= PredValue(inst, gid, root_index,
|
||||||
ntree_limit);
|
&thread_temp[0], 0, ntree_limit) + base_margin_;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
friend class GBTree;
|
||||||
// commit new trees all at once
|
// commit new trees all at once
|
||||||
virtual void
|
void CommitModel(std::vector<std::unique_ptr<RegTree> >&& new_trees,
|
||||||
CommitModel(std::vector<std::unique_ptr<RegTree> >&& new_trees,
|
int bst_group) override {
|
||||||
int bst_group) {
|
|
||||||
for (size_t i = 0; i < new_trees.size(); ++i) {
|
for (size_t i = 0; i < new_trees.size(); ++i) {
|
||||||
trees.push_back(std::move(new_trees[i]));
|
trees.push_back(std::move(new_trees[i]));
|
||||||
tree_info.push_back(bst_group);
|
tree_info.push_back(bst_group);
|
||||||
@ -625,44 +576,25 @@ class Dart : public GBTree {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// predict the leaf scores without dropped trees
|
// predict the leaf scores without dropped trees
|
||||||
inline void Pred(const RowBatch::Inst &inst,
|
inline float PredValue(const RowBatch::Inst &inst,
|
||||||
int64_t buffer_index,
|
int bst_group,
|
||||||
int bst_group,
|
unsigned root_index,
|
||||||
unsigned root_index,
|
RegTree::FVec *p_feats,
|
||||||
RegTree::FVec *p_feats,
|
unsigned tree_begin,
|
||||||
float *out_pred,
|
unsigned tree_end) {
|
||||||
size_t stride,
|
float psum = 0.0f;
|
||||||
unsigned ntree_limit) {
|
|
||||||
float psum = 0.0f;
|
|
||||||
// sum of leaf vector
|
|
||||||
std::vector<float> vec_psum(mparam.size_leaf_vector, 0.0f);
|
|
||||||
const int64_t bid = this->BufferOffset(buffer_index, bst_group);
|
|
||||||
p_feats->Fill(inst);
|
p_feats->Fill(inst);
|
||||||
for (size_t i = 0; i < trees.size(); ++i) {
|
for (size_t i = tree_begin; i < tree_end; ++i) {
|
||||||
if (tree_info[i] == bst_group) {
|
if (tree_info[i] == bst_group) {
|
||||||
bool drop = (std::find(idx_drop.begin(), idx_drop.end(), i) != idx_drop.end());
|
bool drop = (std::binary_search(idx_drop.begin(), idx_drop.end(), i));
|
||||||
if (!drop) {
|
if (!drop) {
|
||||||
int tid = trees[i]->GetLeafIndex(*p_feats, root_index);
|
int tid = trees[i]->GetLeafIndex(*p_feats, root_index);
|
||||||
psum += weight_drop[i] * (*trees[i])[tid].leaf_value();
|
psum += weight_drop[i] * (*trees[i])[tid].leaf_value();
|
||||||
for (int j = 0; j < mparam.size_leaf_vector; ++j) {
|
|
||||||
vec_psum[j] += weight_drop[i] * trees[i]->leafvec(tid)[j];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
p_feats->Drop(inst);
|
p_feats->Drop(inst);
|
||||||
// updated the buffered results
|
return psum;
|
||||||
if (bid >= 0 && ntree_limit == 0) {
|
|
||||||
pred_counter[bid] = static_cast<unsigned>(trees.size());
|
|
||||||
pred_buffer[bid] = psum;
|
|
||||||
for (int i = 0; i < mparam.size_leaf_vector; ++i) {
|
|
||||||
pred_buffer[bid + i + 1] = vec_psum[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
out_pred[0] = psum;
|
|
||||||
for (int i = 0; i < mparam.size_leaf_vector; ++i) {
|
|
||||||
out_pred[stride * (i + 1)] = vec_psum[i];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// select dropped trees
|
// select dropped trees
|
||||||
@ -744,13 +676,16 @@ DMLC_REGISTER_PARAMETER(DartTrainParam);
|
|||||||
|
|
||||||
XGBOOST_REGISTER_GBM(GBTree, "gbtree")
|
XGBOOST_REGISTER_GBM(GBTree, "gbtree")
|
||||||
.describe("Tree booster, gradient boosted trees.")
|
.describe("Tree booster, gradient boosted trees.")
|
||||||
.set_body([]() {
|
.set_body([](const std::vector<std::shared_ptr<DMatrix> >& cached_mats, float base_margin) {
|
||||||
return new GBTree();
|
GBTree* p = new GBTree(base_margin);
|
||||||
|
p->InitCache(cached_mats);
|
||||||
|
return p;
|
||||||
});
|
});
|
||||||
XGBOOST_REGISTER_GBM(Dart, "dart")
|
XGBOOST_REGISTER_GBM(Dart, "dart")
|
||||||
.describe("Tree booster, dart.")
|
.describe("Tree booster, dart.")
|
||||||
.set_body([]() {
|
.set_body([](const std::vector<std::shared_ptr<DMatrix> >& cached_mats, float base_margin) {
|
||||||
return new Dart();
|
GBTree* p = new Dart(base_margin);
|
||||||
|
return p;
|
||||||
});
|
});
|
||||||
} // namespace gbm
|
} // namespace gbm
|
||||||
} // namespace xgboost
|
} // namespace xgboost
|
||||||
|
|||||||
@ -118,20 +118,8 @@ DMLC_REGISTER_PARAMETER(LearnerTrainParam);
|
|||||||
*/
|
*/
|
||||||
class LearnerImpl : public Learner {
|
class LearnerImpl : public Learner {
|
||||||
public:
|
public:
|
||||||
explicit LearnerImpl(const std::vector<DMatrix*>& cache_mats)
|
explicit LearnerImpl(const std::vector<std::shared_ptr<DMatrix> >& cache)
|
||||||
noexcept(false) {
|
: cache_(cache) {
|
||||||
// setup the cache setting in constructor.
|
|
||||||
CHECK_EQ(cache_.size(), 0);
|
|
||||||
size_t buffer_size = 0;
|
|
||||||
for (auto it = cache_mats.begin(); it != cache_mats.end(); ++it) {
|
|
||||||
// avoid duplication.
|
|
||||||
if (std::find(cache_mats.begin(), it, *it) != it) continue;
|
|
||||||
DMatrix* pmat = *it;
|
|
||||||
pmat->cache_learner_ptr_ = this;
|
|
||||||
cache_.push_back(CacheEntry(pmat, buffer_size, pmat->info().num_row));
|
|
||||||
buffer_size += pmat->info().num_row;
|
|
||||||
}
|
|
||||||
pred_buffer_size_ = buffer_size;
|
|
||||||
// boosted tree
|
// boosted tree
|
||||||
name_obj_ = "reg:linear";
|
name_obj_ = "reg:linear";
|
||||||
name_gbm_ = "gbtree";
|
name_gbm_ = "gbtree";
|
||||||
@ -257,7 +245,7 @@ class LearnerImpl : public Learner {
|
|||||||
<< "BoostLearner: wrong model format";
|
<< "BoostLearner: wrong model format";
|
||||||
// duplicated code with LazyInitModel
|
// duplicated code with LazyInitModel
|
||||||
obj_.reset(ObjFunction::Create(name_obj_));
|
obj_.reset(ObjFunction::Create(name_obj_));
|
||||||
gbm_.reset(GradientBooster::Create(name_gbm_));
|
gbm_.reset(GradientBooster::Create(name_gbm_, cache_, mparam.base_score));
|
||||||
gbm_->Load(fi);
|
gbm_->Load(fi);
|
||||||
if (mparam.contain_extra_attrs != 0) {
|
if (mparam.contain_extra_attrs != 0) {
|
||||||
std::vector<std::pair<std::string, std::string> > attr;
|
std::vector<std::pair<std::string, std::string> > attr;
|
||||||
@ -265,8 +253,6 @@ class LearnerImpl : public Learner {
|
|||||||
attributes_ = std::map<std::string, std::string>(
|
attributes_ = std::map<std::string, std::string>(
|
||||||
attr.begin(), attr.end());
|
attr.begin(), attr.end());
|
||||||
}
|
}
|
||||||
this->base_score_ = mparam.base_score;
|
|
||||||
gbm_->ResetPredBuffer(pred_buffer_size_);
|
|
||||||
cfg_["num_class"] = common::ToString(mparam.num_class);
|
cfg_["num_class"] = common::ToString(mparam.num_class);
|
||||||
cfg_["num_feature"] = common::ToString(mparam.num_feature);
|
cfg_["num_feature"] = common::ToString(mparam.num_feature);
|
||||||
obj_->Configure(cfg_.begin(), cfg_.end());
|
obj_->Configure(cfg_.begin(), cfg_.end());
|
||||||
@ -294,7 +280,7 @@ class LearnerImpl : public Learner {
|
|||||||
this->LazyInitDMatrix(train);
|
this->LazyInitDMatrix(train);
|
||||||
this->PredictRaw(train, &preds_);
|
this->PredictRaw(train, &preds_);
|
||||||
obj_->GetGradient(preds_, train->info(), iter, &gpair_);
|
obj_->GetGradient(preds_, train->info(), iter, &gpair_);
|
||||||
gbm_->DoBoost(train, this->FindBufferOffset(train), &gpair_);
|
gbm_->DoBoost(train, &gpair_, obj_.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
void BoostOneIter(int iter,
|
void BoostOneIter(int iter,
|
||||||
@ -304,7 +290,7 @@ class LearnerImpl : public Learner {
|
|||||||
common::GlobalRandom().seed(tparam.seed * kRandSeedMagic + iter);
|
common::GlobalRandom().seed(tparam.seed * kRandSeedMagic + iter);
|
||||||
}
|
}
|
||||||
this->LazyInitDMatrix(train);
|
this->LazyInitDMatrix(train);
|
||||||
gbm_->DoBoost(train, this->FindBufferOffset(train), in_gpair);
|
gbm_->DoBoost(train, in_gpair);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string EvalOneIter(int iter,
|
std::string EvalOneIter(int iter,
|
||||||
@ -435,28 +421,24 @@ class LearnerImpl : public Learner {
|
|||||||
// estimate feature bound
|
// estimate feature bound
|
||||||
unsigned num_feature = 0;
|
unsigned num_feature = 0;
|
||||||
for (size_t i = 0; i < cache_.size(); ++i) {
|
for (size_t i = 0; i < cache_.size(); ++i) {
|
||||||
|
CHECK(cache_[i] != nullptr);
|
||||||
num_feature = std::max(num_feature,
|
num_feature = std::max(num_feature,
|
||||||
static_cast<unsigned>(cache_[i].mat_->info().num_col));
|
static_cast<unsigned>(cache_[i]->info().num_col));
|
||||||
}
|
}
|
||||||
// run allreduce on num_feature to find the maximum value
|
// run allreduce on num_feature to find the maximum value
|
||||||
rabit::Allreduce<rabit::op::Max>(&num_feature, 1);
|
rabit::Allreduce<rabit::op::Max>(&num_feature, 1);
|
||||||
if (num_feature > mparam.num_feature) {
|
if (num_feature > mparam.num_feature) {
|
||||||
mparam.num_feature = num_feature;
|
mparam.num_feature = num_feature;
|
||||||
}
|
}
|
||||||
|
|
||||||
// setup
|
// setup
|
||||||
cfg_["num_feature"] = common::ToString(mparam.num_feature);
|
cfg_["num_feature"] = common::ToString(mparam.num_feature);
|
||||||
CHECK(obj_.get() == nullptr && gbm_.get() == nullptr);
|
CHECK(obj_.get() == nullptr && gbm_.get() == nullptr);
|
||||||
obj_.reset(ObjFunction::Create(name_obj_));
|
obj_.reset(ObjFunction::Create(name_obj_));
|
||||||
gbm_.reset(GradientBooster::Create(name_gbm_));
|
|
||||||
gbm_->Configure(cfg_.begin(), cfg_.end());
|
|
||||||
obj_->Configure(cfg_.begin(), cfg_.end());
|
obj_->Configure(cfg_.begin(), cfg_.end());
|
||||||
|
|
||||||
// reset the base score
|
// reset the base score
|
||||||
mparam.base_score = obj_->ProbToMargin(mparam.base_score);
|
mparam.base_score = obj_->ProbToMargin(mparam.base_score);
|
||||||
|
gbm_.reset(GradientBooster::Create(name_gbm_, cache_, mparam.base_score));
|
||||||
this->base_score_ = mparam.base_score;
|
gbm_->Configure(cfg_.begin(), cfg_.end());
|
||||||
gbm_->ResetPredBuffer(pred_buffer_size_);
|
|
||||||
}
|
}
|
||||||
/*!
|
/*!
|
||||||
* \brief get un-transformed prediction
|
* \brief get un-transformed prediction
|
||||||
@ -471,29 +453,9 @@ class LearnerImpl : public Learner {
|
|||||||
CHECK(gbm_.get() != nullptr)
|
CHECK(gbm_.get() != nullptr)
|
||||||
<< "Predict must happen after Load or InitModel";
|
<< "Predict must happen after Load or InitModel";
|
||||||
gbm_->Predict(data,
|
gbm_->Predict(data,
|
||||||
this->FindBufferOffset(data),
|
|
||||||
out_preds,
|
out_preds,
|
||||||
ntree_limit);
|
ntree_limit);
|
||||||
// add base margin
|
|
||||||
std::vector<float>& preds = *out_preds;
|
|
||||||
const bst_omp_uint ndata = static_cast<bst_omp_uint>(preds.size());
|
|
||||||
const std::vector<bst_float>& base_margin = data->info().base_margin;
|
|
||||||
if (base_margin.size() != 0) {
|
|
||||||
CHECK_EQ(preds.size(), base_margin.size())
|
|
||||||
<< "base_margin.size does not match with prediction size";
|
|
||||||
#pragma omp parallel for schedule(static)
|
|
||||||
for (bst_omp_uint j = 0; j < ndata; ++j) {
|
|
||||||
preds[j] += base_margin[j];
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
#pragma omp parallel for schedule(static)
|
|
||||||
for (bst_omp_uint j = 0; j < ndata; ++j) {
|
|
||||||
preds[j] += this->base_score_;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// cached size of predict buffer
|
|
||||||
size_t pred_buffer_size_;
|
|
||||||
// model parameter
|
// model parameter
|
||||||
LearnerModelParam mparam;
|
LearnerModelParam mparam;
|
||||||
// training parameter
|
// training parameter
|
||||||
@ -514,31 +476,11 @@ class LearnerImpl : public Learner {
|
|||||||
private:
|
private:
|
||||||
/*! \brief random number transformation seed. */
|
/*! \brief random number transformation seed. */
|
||||||
static const int kRandSeedMagic = 127;
|
static const int kRandSeedMagic = 127;
|
||||||
// cache entry object that helps handle feature caching
|
// internal cached dmatrix
|
||||||
struct CacheEntry {
|
std::vector<std::shared_ptr<DMatrix> > cache_;
|
||||||
const DMatrix* mat_;
|
|
||||||
size_t buffer_offset_;
|
|
||||||
size_t num_row_;
|
|
||||||
CacheEntry(const DMatrix* mat, size_t buffer_offset, size_t num_row)
|
|
||||||
:mat_(mat), buffer_offset_(buffer_offset), num_row_(num_row) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
// find internal buffer offset for certain matrix, if not exist, return -1
|
|
||||||
inline int64_t FindBufferOffset(const DMatrix* mat) const {
|
|
||||||
for (size_t i = 0; i < cache_.size(); ++i) {
|
|
||||||
if (cache_[i].mat_ == mat && mat->cache_learner_ptr_ == this) {
|
|
||||||
if (cache_[i].num_row_ == mat->info().num_row) {
|
|
||||||
return static_cast<int64_t>(cache_[i].buffer_offset_);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
/*! \brief the entries indicates that we have internal prediction cache */
|
|
||||||
std::vector<CacheEntry> cache_;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Learner* Learner::Create(const std::vector<DMatrix*>& cache_data) {
|
Learner* Learner::Create(const std::vector<std::shared_ptr<DMatrix> >& cache_data) {
|
||||||
return new LearnerImpl(cache_data);
|
return new LearnerImpl(cache_data);
|
||||||
}
|
}
|
||||||
} // namespace xgboost
|
} // namespace xgboost
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user