add boost group support to xgboost. now have beta multi-class classification

This commit is contained in:
tqchen 2014-05-04 12:10:03 -07:00
parent 21f93ffd6a
commit 8c0c10463e
6 changed files with 210 additions and 75 deletions

View File

@ -88,8 +88,8 @@ namespace xgboost{
} }
} }
if (mparam.num_pbuffer != 0){ if (mparam.num_pbuffer != 0){
pred_buffer.resize(mparam.num_pbuffer); pred_buffer.resize(mparam.PredBufferSize());
pred_counter.resize(mparam.num_pbuffer); pred_counter.resize(mparam.PredBufferSize());
utils::Assert(fi.Read(&pred_buffer[0], pred_buffer.size()*sizeof(float)) != 0); utils::Assert(fi.Read(&pred_buffer[0], pred_buffer.size()*sizeof(float)) != 0);
utils::Assert(fi.Read(&pred_counter[0], pred_counter.size()*sizeof(unsigned)) != 0); utils::Assert(fi.Read(&pred_counter[0], pred_counter.size()*sizeof(unsigned)) != 0);
} }
@ -117,8 +117,8 @@ namespace xgboost{
*/ */
inline void InitModel(void){ inline void InitModel(void){
pred_buffer.clear(); pred_counter.clear(); pred_buffer.clear(); pred_counter.clear();
pred_buffer.resize(mparam.num_pbuffer, 0.0); pred_buffer.resize(mparam.PredBufferSize(), 0.0);
pred_counter.resize(mparam.num_pbuffer, 0); pred_counter.resize(mparam.PredBufferSize(), 0);
utils::Assert(mparam.num_boosters == 0); utils::Assert(mparam.num_boosters == 0);
utils::Assert(boosters.size() == 0); utils::Assert(boosters.size() == 0);
} }
@ -130,6 +130,7 @@ namespace xgboost{
if (tparam.nthread != 0){ if (tparam.nthread != 0){
omp_set_num_threads(tparam.nthread); omp_set_num_threads(tparam.nthread);
} }
if (mparam.num_booster_group == 0) mparam.num_booster_group = 1;
// make sure all the boosters get the latest parameters // make sure all the boosters get the latest parameters
for (size_t i = 0; i < this->boosters.size(); i++){ for (size_t i = 0; i < this->boosters.size(); i++){
this->ConfigBooster(this->boosters[i]); this->ConfigBooster(this->boosters[i]);
@ -175,12 +176,14 @@ namespace xgboost{
* \param feats features of each instance * \param feats features of each instance
* \param root_index pre-partitioned root index of each instance, * \param root_index pre-partitioned root index of each instance,
* root_index.size() can be 0 which indicates that no pre-partition involved * root_index.size() can be 0 which indicates that no pre-partition involved
* \param bst_group which booster group it belongs to, by default, we only have 1 booster group, and leave this parameter as default
*/ */
inline void DoBoost(std::vector<float> &grad, inline void DoBoost(std::vector<float> &grad,
std::vector<float> &hess, std::vector<float> &hess,
const booster::FMatrixS &feats, const booster::FMatrixS &feats,
const std::vector<unsigned> &root_index) { const std::vector<unsigned> &root_index,
booster::IBooster *bst = this->GetUpdateBooster(); int bst_group = 0 ) {
booster::IBooster *bst = this->GetUpdateBooster( bst_group );
bst->DoBoost(grad, hess, feats, root_index); bst->DoBoost(grad, hess, feats, root_index);
} }
/*! /*!
@ -190,26 +193,30 @@ namespace xgboost{
* \param row_index row index in the feature matrix * \param row_index row index in the feature matrix
* \param buffer_index the buffer index of the current feature line, default -1 means no buffer assigned * \param buffer_index the buffer index of the current feature line, default -1 means no buffer assigned
* \param root_index root id of current instance, default = 0 * \param root_index root id of current instance, default = 0
* \param bst_group booster group index
* \return prediction * \return prediction
*/ */
inline float Predict(const FMatrixS &feats, bst_uint row_index, int buffer_index = -1, unsigned root_index = 0){ inline float Predict(const FMatrixS &feats, bst_uint row_index,
size_t istart = 0; int buffer_index = -1, unsigned root_index = 0, int bst_group = 0 ){
size_t itop = 0;
float psum = 0.0f; float psum = 0.0f;
const int bid = mparam.BufferOffset(buffer_index, bst_group);
// load buffered results if any // load buffered results if any
if (mparam.do_reboost == 0 && buffer_index >= 0){ if (mparam.do_reboost == 0 && bid >= 0){
utils::Assert(buffer_index < mparam.num_pbuffer, "buffer index exceed num_pbuffer"); itop = this->pred_counter[bid];
istart = this->pred_counter[buffer_index]; psum = this->pred_buffer[bid];
psum = this->pred_buffer[buffer_index];
} }
for (size_t i = istart; i < this->boosters.size(); i++){ for (size_t i = itop; i < this->boosters.size(); ++i ){
psum += this->boosters[i]->Predict(feats, row_index, root_index); if( booster_info[i] == bst_group ){
psum += this->boosters[i]->Predict(feats, row_index, root_index);
}
} }
// updated the buffered results // updated the buffered results
if (mparam.do_reboost == 0 && buffer_index >= 0){ if (mparam.do_reboost == 0 && bid >= 0){
this->pred_counter[buffer_index] = static_cast<unsigned>(boosters.size()); this->pred_counter[bid] = static_cast<unsigned>(boosters.size());
this->pred_buffer[buffer_index] = psum; this->pred_buffer[bid] = psum;
} }
return psum; return psum;
} }
@ -217,6 +224,11 @@ namespace xgboost{
inline int NumBoosters(void) const{ inline int NumBoosters(void) const{
return mparam.num_boosters; return mparam.num_boosters;
} }
/*! \return number of booster groups */
inline int NumBoosterGroup(void) const{
if( mparam.num_booster_group == 0 ) return 1;
return mparam.num_booster_group;
}
public: public:
//--------trial code for interactive update an existing booster------ //--------trial code for interactive update an existing booster------
//-------- usually not needed, ignore this region --------- //-------- usually not needed, ignore this region ---------
@ -224,14 +236,17 @@ namespace xgboost{
* \brief same as Predict, but removes the prediction of booster to be updated * \brief same as Predict, but removes the prediction of booster to be updated
* this function must be called once and only once for every data with pbuffer * this function must be called once and only once for every data with pbuffer
*/ */
inline float InteractPredict(const FMatrixS &feats, bst_uint row_index, int buffer_index = -1, unsigned root_index = 0){ inline float InteractPredict(const FMatrixS &feats, bst_uint row_index,
int buffer_index = -1, unsigned root_index = 0, int bst_group = 0){
float psum = this->Predict(feats, row_index, buffer_index, root_index); float psum = this->Predict(feats, row_index, buffer_index, root_index);
if (tparam.reupdate_booster != -1){ if (tparam.reupdate_booster != -1){
const int bid = tparam.reupdate_booster; const int bid = tparam.reupdate_booster;
utils::Assert(bid >= 0 && bid < (int)boosters.size(), "interact:booster_index exceed existing bound"); utils::Assert(bid >= 0 && bid < (int)boosters.size(), "interact:booster_index exceed existing bound");
psum -= boosters[bid]->Predict(feats, row_index, root_index); if( bst_group == booster_info[bid] ){
psum -= boosters[bid]->Predict(feats, row_index, root_index);
}
if (mparam.do_reboost == 0 && buffer_index >= 0){ if (mparam.do_reboost == 0 && buffer_index >= 0){
this->pred_buffer[buffer_index] = psum; this->pred_buffer[mparam.BufferOffset(buffer_index,bst_group)] = psum;
} }
} }
return psum; return psum;
@ -246,15 +261,21 @@ namespace xgboost{
booster_info[i - 1] = booster_info[i]; booster_info[i - 1] = booster_info[i];
} }
boosters.resize(mparam.num_boosters -= 1); boosters.resize(mparam.num_boosters -= 1);
booster_info.resize(boosters.size()); booster_info.resize(boosters.size());
// update pred counter
for( size_t i = 0; i < pred_counter.size(); ++ i ){
if( pred_counter[i] > (unsigned)bid ) pred_counter[i] -= 1;
}
} }
/*! \brief update the prediction buffer, after booster have been updated */ /*! \brief update the prediction buffer, after booster have been updated */
inline void InteractRePredict(const FMatrixS &feats, bst_uint row_index, int buffer_index = -1, unsigned root_index = 0){ inline void InteractRePredict(const FMatrixS &feats, bst_uint row_index,
int buffer_index = -1, unsigned root_index = 0, int bst_group = 0 ){
if (tparam.reupdate_booster != -1){ if (tparam.reupdate_booster != -1){
const int bid = tparam.reupdate_booster; const int bid = tparam.reupdate_booster;
if( booster_info[bid] != bst_group ) return;
utils::Assert(bid >= 0 && bid < (int)boosters.size(), "interact:booster_index exceed existing bound"); utils::Assert(bid >= 0 && bid < (int)boosters.size(), "interact:booster_index exceed existing bound");
if (mparam.do_reboost == 0 && buffer_index >= 0){ if (mparam.do_reboost == 0 && buffer_index >= 0){
this->pred_buffer[buffer_index] += boosters[bid]->Predict(feats, row_index, root_index); this->pred_buffer[mparam.BufferOffset(buffer_index,bst_group)] += boosters[bid]->Predict(feats, row_index, root_index);
} }
} }
} }
@ -278,18 +299,19 @@ namespace xgboost{
* \brief get a booster to update * \brief get a booster to update
* \return the booster created * \return the booster created
*/ */
inline booster::IBooster *GetUpdateBooster(void){ inline booster::IBooster *GetUpdateBooster(int bst_group){
if (tparam.reupdate_booster != -1){ if (tparam.reupdate_booster != -1){
const int bid = tparam.reupdate_booster; const int bid = tparam.reupdate_booster;
utils::Assert(bid >= 0 && bid < (int)boosters.size(), "interact:booster_index exceed existing bound"); utils::Assert(bid >= 0 && bid < (int)boosters.size(), "interact:booster_index exceed existing bound");
this->ConfigBooster(boosters[bid]); this->ConfigBooster(boosters[bid]);
utils::Assert( bst_group == booster_info[bid], "booster group must match existing reupdate booster");
return boosters[bid]; return boosters[bid];
} }
if (mparam.do_reboost == 0 || boosters.size() == 0){ if (mparam.do_reboost == 0 || boosters.size() == 0){
mparam.num_boosters += 1; mparam.num_boosters += 1;
boosters.push_back(booster::CreateBooster<FMatrixS>(mparam.booster_type)); boosters.push_back(booster::CreateBooster<FMatrixS>(mparam.booster_type));
booster_info.push_back(0); booster_info.push_back(bst_group);
this->ConfigBooster(boosters.back()); this->ConfigBooster(boosters.back());
boosters.back()->InitModel(); boosters.back()->InitModel();
} }
@ -316,8 +338,13 @@ namespace xgboost{
* set to 1 for linear booster, so that regularization term can be considered * set to 1 for linear booster, so that regularization term can be considered
*/ */
int do_reboost; int do_reboost;
/*!
* \brief number of booster group, how many predictions a single
* input instance could corresponds to
*/
int num_booster_group;
/*! \brief reserved parameters */ /*! \brief reserved parameters */
int reserved[32]; int reserved[31];
/*! \brief constructor */ /*! \brief constructor */
ModelParam(void){ ModelParam(void){
num_boosters = 0; num_boosters = 0;
@ -325,6 +352,7 @@ namespace xgboost{
num_roots = num_feature = 0; num_roots = num_feature = 0;
do_reboost = 0; do_reboost = 0;
num_pbuffer = 0; num_pbuffer = 0;
num_booster_group = 1;
memset(reserved, 0, sizeof(reserved)); memset(reserved, 0, sizeof(reserved));
} }
/*! /*!
@ -338,10 +366,21 @@ namespace xgboost{
// linear boost automatically set do reboost // linear boost automatically set do reboost
if (booster_type == 1) do_reboost = 1; if (booster_type == 1) do_reboost = 1;
} }
if (!strcmp("num_pbuffer", name)) num_pbuffer = atoi(val); if (!strcmp("num_pbuffer", name)) num_pbuffer = atoi(val);
if (!strcmp("do_reboost", name)) do_reboost = atoi(val); if (!strcmp("do_reboost", name)) do_reboost = atoi(val);
if (!strcmp("bst:num_roots", name)) num_roots = atoi(val); if (!strcmp("num_booster_group", name)) num_booster_group = atoi(val);
if (!strcmp("bst:num_feature", name)) num_feature = atoi(val); if (!strcmp("bst:num_roots", name)) num_roots = atoi(val);
if (!strcmp("bst:num_feature", name)) num_feature = atoi(val);
}
inline int PredBufferSize(void) const{
if (num_booster_group == 0) return num_pbuffer;
else return num_booster_group * num_pbuffer;
}
inline int BufferOffset( int buffer_index, int bst_group ) const{
if( buffer_index < 0 ) return -1;
utils::Assert( buffer_index < num_pbuffer, "buffer_indexexceed num_pbuffer" );
return buffer_index + num_pbuffer * bst_group;
} }
}; };
/*! \brief training parameters */ /*! \brief training parameters */

View File

@ -86,6 +86,7 @@ namespace xgboost{
if (!strcmp(name, "silent")) silent = atoi(val); if (!strcmp(name, "silent")) silent = atoi(val);
if (!strcmp(name, "eval_metric")) evaluator_.AddEval(val); if (!strcmp(name, "eval_metric")) evaluator_.AddEval(val);
if (!strcmp(name, "objective") ) name_obj_ = val; if (!strcmp(name, "objective") ) name_obj_ = val;
if (!strcmp(name, "num_class") ) base_gbm.SetParam("num_booster_group", val );
mparam.SetParam(name, val); mparam.SetParam(name, val);
base_gbm.SetParam(name, val); base_gbm.SetParam(name, val);
cfg_.push_back( std::make_pair( std::string(name), std::string(val) ) ); cfg_.push_back( std::make_pair( std::string(name), std::string(val) ) );
@ -95,7 +96,13 @@ namespace xgboost{
* this function is reserved for solver to allocate necessary space and do other preparation * this function is reserved for solver to allocate necessary space and do other preparation
*/ */
inline void InitTrainer(void){ inline void InitTrainer(void){
base_gbm.InitTrainer(); if( mparam.num_class != 0 ){
if( name_obj_ != "softmax" ){
name_obj_ = "softmax";
printf("auto select objective=softmax to support multi-class classification\n" );
}
}
base_gbm.InitTrainer();
obj_ = CreateObjFunction( name_obj_.c_str() ); obj_ = CreateObjFunction( name_obj_.c_str() );
for( size_t i = 0; i < cfg_.size(); ++ i ){ for( size_t i = 0; i < cfg_.size(); ++ i ){
obj_->SetParam( cfg_[i].first.c_str(), cfg_[i].second.c_str() ); obj_->SetParam( cfg_[i].first.c_str(), cfg_[i].second.c_str() );
@ -166,9 +173,18 @@ namespace xgboost{
inline void UpdateOneIter(const DMatrix &train){ inline void UpdateOneIter(const DMatrix &train){
this->PredictRaw(preds_, train); this->PredictRaw(preds_, train);
obj_->GetGradient(preds_, train.info, base_gbm.NumBoosters(), grad_, hess_); obj_->GetGradient(preds_, train.info, base_gbm.NumBoosters(), grad_, hess_);
// do boost if( grad_.size() == train.Size() ){
std::vector<unsigned> root_index; base_gbm.DoBoost(grad_, hess_, train.data, train.info.root_index);
base_gbm.DoBoost(grad_, hess_, train.data, root_index); }else{
int ngroup = base_gbm.NumBoosterGroup();
utils::Assert( grad_.size() == train.Size() * (size_t)ngroup, "BUG: UpdateOneIter: mclass" );
std::vector<float> tgrad( train.Size() ), thess( train.Size() );
for( int g = 0; g < ngroup; ++ g ){
memcpy( &tgrad[0], &grad_[g*tgrad.size()], sizeof(float)*tgrad.size() );
memcpy( &thess[0], &hess_[g*tgrad.size()], sizeof(float)*tgrad.size() );
base_gbm.DoBoost(tgrad, thess, train.data, train.info.root_index, g );
}
}
} }
/*! /*!
* \brief evaluate the model for specific iteration * \brief evaluate the model for specific iteration
@ -190,9 +206,14 @@ namespace xgboost{
fprintf(fo, "\n"); fprintf(fo, "\n");
fflush(fo); fflush(fo);
} }
/*! \brief get prediction, without buffering */ /*!
inline void Predict(std::vector<float> &preds, const DMatrix &data){ * \brief get prediction
this->PredictRaw(preds,data); * \param storage to store prediction
* \param data input data
* \param bst_group booster group we are in
*/
inline void Predict(std::vector<float> &preds, const DMatrix &data, int bst_group = -1){
this->PredictRaw( preds, data, bst_group );
obj_->PredTransform( preds ); obj_->PredTransform( preds );
} }
public: public:
@ -243,22 +264,31 @@ namespace xgboost{
} }
private: private:
/*! \brief get un-transformed prediction*/ /*! \brief get un-transformed prediction*/
inline void PredictRaw(std::vector<float> &preds, const DMatrix &data){ inline void PredictRaw(std::vector<float> &preds, const DMatrix &data, int bst_group = -1 ){
this->PredictBuffer(preds, data, this->FindBufferOffset(data) ); int buffer_offset = this->FindBufferOffset(data);
if( bst_group < 0 ){
int ngroup = base_gbm.NumBoosterGroup();
preds.resize( data.Size() * ngroup );
for( int g = 0; g < ngroup; ++ g ){
this->PredictBuffer(&preds[ data.Size() * g ], data, buffer_offset, g );
}
}else{
preds.resize( data.Size() );
this->PredictBuffer(&preds[0], data, buffer_offset, bst_group );
}
} }
/*! \brief get the un-transformed predictions, given data */ /*! \brief get the un-transformed predictions, given data */
inline void PredictBuffer(std::vector<float> &preds, const DMatrix &data, int buffer_offset){ inline void PredictBuffer(float *preds, const DMatrix &data, int buffer_offset, int bst_group ){
preds.resize(data.Size());
const unsigned ndata = static_cast<unsigned>(data.Size()); const unsigned ndata = static_cast<unsigned>(data.Size());
if( buffer_offset >= 0 ){ if( buffer_offset >= 0 ){
#pragma omp parallel for schedule( static ) #pragma omp parallel for schedule( static )
for (unsigned j = 0; j < ndata; ++j){ for (unsigned j = 0; j < ndata; ++j){
preds[j] = mparam.base_score + base_gbm.Predict(data.data, j, buffer_offset + j); preds[j] = mparam.base_score + base_gbm.Predict(data.data, j, buffer_offset + j, data.info.GetRoot(j), bst_group );
} }
}else }else
#pragma omp parallel for schedule( static ) #pragma omp parallel for schedule( static )
for (unsigned j = 0; j < ndata; ++j){ for (unsigned j = 0; j < ndata; ++j){
preds[j] = mparam.base_score + base_gbm.Predict(data.data, j, -1); preds[j] = mparam.base_score + base_gbm.Predict(data.data, j, -1, data.info.GetRoot(j), bst_group );
}{ }{
} }
} }
@ -270,14 +300,17 @@ namespace xgboost{
/* \brief type of loss function */ /* \brief type of loss function */
int loss_type; int loss_type;
/* \brief number of features */ /* \brief number of features */
int num_feature; int num_feature;
/* \brief number of class, if it is multi-class classification */
int num_class;
/*! \brief reserved field */ /*! \brief reserved field */
int reserved[16]; int reserved[15];
/*! \brief constructor */ /*! \brief constructor */
ModelParam(void){ ModelParam(void){
base_score = 0.5f; base_score = 0.5f;
loss_type = 0; loss_type = 0;
num_feature = 0; num_feature = 0;
num_class = 0;
memset(reserved, 0, sizeof(reserved)); memset(reserved, 0, sizeof(reserved));
} }
/*! /*!
@ -288,6 +321,7 @@ namespace xgboost{
inline void SetParam(const char *name, const char *val){ inline void SetParam(const char *name, const char *val){
if (!strcmp("base_score", name)) base_score = (float)atof(val); if (!strcmp("base_score", name)) base_score = (float)atof(val);
if (!strcmp("loss_type", name)) loss_type = atoi(val); if (!strcmp("loss_type", name)) loss_type = atoi(val);
if (!strcmp("num_class", name)) num_class = atoi(val);
if (!strcmp("bst:num_feature", name)) num_feature = atoi(val); if (!strcmp("bst:num_feature", name)) num_feature = atoi(val);
} }
/*! /*!

View File

@ -35,11 +35,17 @@ namespace xgboost{
std::vector<unsigned> group_ptr; std::vector<unsigned> group_ptr;
/*! \brief weights of each instance, optional */ /*! \brief weights of each instance, optional */
std::vector<float> weights; std::vector<float> weights;
/*! \brief specified root index of each instance, can be used for multi task setting*/
std::vector<unsigned> root_index;
/*! \brief get weight of each instances */ /*! \brief get weight of each instances */
inline float GetWeight( size_t i ) const{ inline float GetWeight( size_t i ) const{
if( weights.size() != 0 ) return weights[i]; if( weights.size() != 0 ) return weights[i];
else return 1.0f; else return 1.0f;
} }
inline float GetRoot( size_t i ) const{
if( root_index.size() != 0 ) return root_index[i];
else return 0;
}
}; };
public: public:
/*! \brief feature data content */ /*! \brief feature data content */

View File

@ -13,6 +13,7 @@
#include "../utils/xgboost_omp.h" #include "../utils/xgboost_omp.h"
#include "../utils/xgboost_random.h" #include "../utils/xgboost_random.h"
#include "xgboost_regrank_data.h" #include "xgboost_regrank_data.h"
#include "xgboost_regrank_utils.h"
namespace xgboost{ namespace xgboost{
namespace regrank{ namespace regrank{
@ -31,17 +32,11 @@ namespace xgboost{
virtual ~IEvaluator(void){} virtual ~IEvaluator(void){}
}; };
inline static bool CmpFirst(const std::pair<float, unsigned> &a, const std::pair<float, unsigned> &b){
return a.first > b.first;
}
inline static bool CmpSecond(const std::pair<float, unsigned> &a, const std::pair<float, unsigned> &b){
return a.second > b.second;
}
/*! \brief RMSE */ /*! \brief RMSE */
struct EvalRMSE : public IEvaluator{ struct EvalRMSE : public IEvaluator{
virtual float Eval(const std::vector<float> &preds, virtual float Eval(const std::vector<float> &preds,
const DMatrix::Info &info) const { const DMatrix::Info &info) const {
utils::Assert( preds.size() == info.labels.size(), "label size predict size not match" );
const unsigned ndata = static_cast<unsigned>(preds.size()); const unsigned ndata = static_cast<unsigned>(preds.size());
float sum = 0.0, wsum = 0.0; float sum = 0.0, wsum = 0.0;
#pragma omp parallel for reduction(+:sum,wsum) schedule( static ) #pragma omp parallel for reduction(+:sum,wsum) schedule( static )
@ -62,6 +57,7 @@ namespace xgboost{
struct EvalLogLoss : public IEvaluator{ struct EvalLogLoss : public IEvaluator{
virtual float Eval(const std::vector<float> &preds, virtual float Eval(const std::vector<float> &preds,
const DMatrix::Info &info) const { const DMatrix::Info &info) const {
utils::Assert( preds.size() == info.labels.size(), "label size predict size not match" );
const unsigned ndata = static_cast<unsigned>(preds.size()); const unsigned ndata = static_cast<unsigned>(preds.size());
float sum = 0.0f, wsum = 0.0f; float sum = 0.0f, wsum = 0.0f;
#pragma omp parallel for reduction(+:sum,wsum) schedule( static ) #pragma omp parallel for reduction(+:sum,wsum) schedule( static )
@ -106,7 +102,8 @@ namespace xgboost{
/*! \brief Area under curve, for both classification and rank */ /*! \brief Area under curve, for both classification and rank */
struct EvalAuc : public IEvaluator{ struct EvalAuc : public IEvaluator{
virtual float Eval(const std::vector<float> &preds, virtual float Eval(const std::vector<float> &preds,
const DMatrix::Info &info) const { const DMatrix::Info &info) const {
utils::Assert( preds.size() == info.labels.size(), "label size predict size not match" );
std::vector<unsigned> tgptr(2, 0); tgptr[1] = preds.size(); std::vector<unsigned> tgptr(2, 0); tgptr[1] = preds.size();
const std::vector<unsigned> &gptr = info.group_ptr.size() == 0 ? tgptr : info.group_ptr; const std::vector<unsigned> &gptr = info.group_ptr.size() == 0 ? tgptr : info.group_ptr;
utils::Assert(gptr.back() == preds.size(), "EvalAuc: group structure must match number of prediction"); utils::Assert(gptr.back() == preds.size(), "EvalAuc: group structure must match number of prediction");
@ -159,6 +156,7 @@ namespace xgboost{
public: public:
virtual float Eval(const std::vector<float> &preds, virtual float Eval(const std::vector<float> &preds,
const DMatrix::Info &info) const { const DMatrix::Info &info) const {
utils::Assert( preds.size() == info.labels.size(), "label size predict size not match" );
const std::vector<unsigned> &gptr = info.group_ptr; const std::vector<unsigned> &gptr = info.group_ptr;
utils::Assert(gptr.size() != 0 && gptr.back() == preds.size(), "EvalAuc: group structure must match number of prediction"); utils::Assert(gptr.size() != 0 && gptr.back() == preds.size(), "EvalAuc: group structure must match number of prediction");
const unsigned ngroup = static_cast<unsigned>(gptr.size() - 1); const unsigned ngroup = static_cast<unsigned>(gptr.size() - 1);

View File

@ -106,8 +106,9 @@ namespace xgboost{
namespace regrank{ namespace regrank{
IObjFunction* CreateObjFunction( const char *name ){ IObjFunction* CreateObjFunction( const char *name ){
if( !strcmp("reg", name ) ) return new RegressionObj(); if( !strcmp("reg", name ) ) return new RegressionObj();
if( !strcmp("rank", name ) ) return new PairwiseRankObj(); if( !strcmp("rank:pairwise", name ) ) return new PairwiseRankObj();
if( !strcmp("softmax", name ) ) return new SoftmaxObj(); if( !strcmp("rank:softmax", name ) ) return new SoftmaxRankObj();
if( !strcmp("softmax", name ) ) return new SoftmaxMultiClassObj();
utils::Error("unknown objective function type"); utils::Error("unknown objective function type");
return NULL; return NULL;
} }

View File

@ -1,12 +1,13 @@
#ifndef XGBOOST_REGRANK_OBJ_HPP #ifndef XGBOOST_REGRANK_OBJ_HPP
#define XGBOOST_REGRANK_OBJ_HPP #define XGBOOST_REGRANK_OBJ_HPP
/*! /*!
* \file xgboost_regrank_obj.h * \file xgboost_regrank_obj.hpp
* \brief implementation of objective functions * \brief implementation of objective functions
* \author Tianqi Chen, Kailong Chen * \author Tianqi Chen, Kailong Chen
*/ */
//#include "xgboost_regrank_sample.h" //#include "xgboost_regrank_sample.h"
#include <vector> #include <vector>
#include "xgboost_regrank_utils.h"
namespace xgboost{ namespace xgboost{
namespace regrank{ namespace regrank{
@ -24,6 +25,7 @@ namespace xgboost{
int iter, int iter,
std::vector<float> &grad, std::vector<float> &grad,
std::vector<float> &hess ) { std::vector<float> &hess ) {
utils::Assert( preds.size() == info.labels.size(), "label size predict size not match" );
grad.resize(preds.size()); hess.resize(preds.size()); grad.resize(preds.size()); hess.resize(preds.size());
const unsigned ndata = static_cast<unsigned>(preds.size()); const unsigned ndata = static_cast<unsigned>(preds.size());
@ -52,11 +54,11 @@ namespace xgboost{
namespace regrank{ namespace regrank{
// simple softmax rak // simple softmax rak
class SoftmaxObj : public IObjFunction{ class SoftmaxRankObj : public IObjFunction{
public: public:
SoftmaxObj(void){ SoftmaxRankObj(void){
} }
virtual ~SoftmaxObj(){} virtual ~SoftmaxRankObj(){}
virtual void SetParam(const char *name, const char *val){ virtual void SetParam(const char *name, const char *val){
} }
virtual void GetGradient(const std::vector<float>& preds, virtual void GetGradient(const std::vector<float>& preds,
@ -64,6 +66,7 @@ namespace xgboost{
int iter, int iter,
std::vector<float> &grad, std::vector<float> &grad,
std::vector<float> &hess ) { std::vector<float> &hess ) {
utils::Assert( preds.size() == info.labels.size(), "label size predict size not match" );
grad.resize(preds.size()); hess.resize(preds.size()); grad.resize(preds.size()); hess.resize(preds.size());
const std::vector<unsigned> &gptr = info.group_ptr; const std::vector<unsigned> &gptr = info.group_ptr;
utils::Assert( gptr.size() != 0 && gptr.back() == preds.size(), "rank loss must have group file" ); utils::Assert( gptr.size() != 0 && gptr.back() == preds.size(), "rank loss must have group file" );
@ -96,23 +99,76 @@ namespace xgboost{
} }
virtual const char* DefaultEvalMetric(void) { virtual const char* DefaultEvalMetric(void) {
return "pre@1"; return "pre@1";
}
private:
inline static void Softmax( std::vector<float>& rec ){
float wmax = rec[0];
for( size_t i = 1; i < rec.size(); ++ i ){
wmax = std::max( rec[i], wmax );
}
double wsum = 0.0f;
for( size_t i = 0; i < rec.size(); ++ i ){
rec[i] = expf(rec[i]-wmax);
wsum += rec[i];
}
for( size_t i = 0; i < rec.size(); ++ i ){
rec[i] /= wsum;
}
} }
}; };
// simple softmax multi-class classification
class SoftmaxMultiClassObj : public IObjFunction{
public:
SoftmaxMultiClassObj(void){
nclass = 0;
}
virtual ~SoftmaxMultiClassObj(){}
virtual void SetParam(const char *name, const char *val){
if( !strcmp( "num_class", name ) ) nclass = atoi(val);
}
virtual void GetGradient(const std::vector<float>& preds,
const DMatrix::Info &info,
int iter,
std::vector<float> &grad,
std::vector<float> &hess ) {
utils::Assert( nclass != 0, "must set num_class to use softmax" );
utils::Assert( preds.size() == (size_t)nclass * info.labels.size(), "SoftmaxMultiClassObj: label size and pred size does not match" );
grad.resize(preds.size()); hess.resize(preds.size());
const unsigned ndata = static_cast<unsigned>(info.labels.size());
#pragma omp parallel
{
std::vector<float> rec(nclass);
#pragma for schedule(static)
for (unsigned j = 0; j < ndata; ++j){
for( int k = 0; k < nclass; ++ k ){
rec[k] = preds[j + k * ndata];
}
Softmax( rec );
int label = static_cast<int>(info.labels[j]);
utils::Assert( label < nclass, "SoftmaxMultiClassObj: label exceed num_class" );
for( int k = 0; k < nclass; ++ k ){
float p = rec[ k ];
if( label == k ){
grad[j+k*ndata] = p - 1.0f;
}else{
grad[j+k*ndata] = p;
}
hess[j+k*ndata] = 2.0f * p * ( 1.0f - p );
}
}
}
}
virtual void PredTransform(std::vector<float> &preds){
utils::Assert( nclass != 0, "must set num_class to use softmax" );
utils::Assert( preds.size() % nclass == 0, "SoftmaxMultiClassObj: label size and pred size does not match" );
const unsigned ndata = static_cast<unsigned>(preds.size()/nclass);
#pragma omp parallel
{
std::vector<float> rec(nclass);
#pragma for schedule(static)
for (unsigned j = 0; j < ndata; ++j){
for( int k = 0; k < nclass; ++ k ){
rec[k] = preds[j + k * ndata];
}
Softmax( rec );
preds[j] = FindMaxIndex( rec );
}
}
preds.resize( ndata );
}
virtual const char* DefaultEvalMetric(void) {
return "error";
}
private:
int nclass;
};
}; };
namespace regrank{ namespace regrank{
@ -133,6 +189,7 @@ namespace xgboost{
int iter, int iter,
std::vector<float> &grad, std::vector<float> &grad,
std::vector<float> &hess ) { std::vector<float> &hess ) {
utils::Assert( preds.size() == info.labels.size(), "label size predict size not match" );
grad.resize(preds.size()); hess.resize(preds.size()); grad.resize(preds.size()); hess.resize(preds.size());
const std::vector<unsigned> &gptr = info.group_ptr; const std::vector<unsigned> &gptr = info.group_ptr;
utils::Assert( gptr.size() != 0 && gptr.back() == preds.size(), "rank loss must have group file" ); utils::Assert( gptr.size() != 0 && gptr.back() == preds.size(), "rank loss must have group file" );