From 312546b99dc8e8e17c7aaae9a1fc2664844f0bf8 Mon Sep 17 00:00:00 2001 From: tqchen Date: Mon, 19 Jan 2015 10:00:28 -0800 Subject: [PATCH 01/94] quick fix --- R-package/R/predict.xgb.Booster.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R-package/R/predict.xgb.Booster.R b/R-package/R/predict.xgb.Booster.R index b41f2428c..62a64f0b5 100644 --- a/R-package/R/predict.xgb.Booster.R +++ b/R-package/R/predict.xgb.Booster.R @@ -48,7 +48,7 @@ setMethod("predict", signature = "xgb.Booster", if (predleaf) { option <- option + 2 } - ret <- .Call("XGBoosterPredict_R", object, newdata, as.integer(predleaf), as.integer(ntreelimit), PACKAGE = "xgboost") + ret <- .Call("XGBoosterPredict_R", object, newdata, as.integer(option), as.integer(ntreelimit), PACKAGE = "xgboost") return(ret) }) From 43c13d82badc3cdd0ccca92a7c2f0d8b0ccdf8c7 Mon Sep 17 00:00:00 2001 From: hetong Date: Mon, 19 Jan 2015 10:34:14 -0800 Subject: [PATCH 02/94] add leaf example in R --- R-package/R/predict.xgb.Booster.R | 16 +++++++++++++--- R-package/demo/predict_leaf_indices.R | 22 ++++++++++++++++++++++ 2 files changed, 35 insertions(+), 3 deletions(-) create mode 100644 R-package/demo/predict_leaf_indices.R diff --git a/R-package/R/predict.xgb.Booster.R b/R-package/R/predict.xgb.Booster.R index 62a64f0b5..8e1982049 100644 --- a/R-package/R/predict.xgb.Booster.R +++ b/R-package/R/predict.xgb.Booster.R @@ -11,7 +11,7 @@ setClass("xgb.Booster") #' value of sum of functions, when outputmargin=TRUE, the prediction is #' untransformed margin value. In logistic regression, outputmargin=T will #' output value before logistic transformation. -#' @param predleaf whether predict leaf index instead +#' @param predleaf whether predict leaf index instead. If set to TRUE, the output will be a matrix object. #' @param ntreelimit limit number of trees used in prediction, this parameter is #' only valid for gbtree, but not for gblinear. set it to be value bigger #' than 0. It will use all trees by default. @@ -26,7 +26,8 @@ setClass("xgb.Booster") #' @export #' setMethod("predict", signature = "xgb.Booster", - definition = function(object, newdata, missing = NULL, outputmargin = FALSE, ntreelimit = NULL, predleaf = FALSE) { + definition = function(object, newdata, missing = NULL, + outputmargin = FALSE, ntreelimit = NULL, predleaf = FALSE) { if (class(newdata) != "xgb.DMatrix") { if (is.null(missing)) { newdata <- xgb.DMatrix(newdata) @@ -48,7 +49,16 @@ setMethod("predict", signature = "xgb.Booster", if (predleaf) { option <- option + 2 } - ret <- .Call("XGBoosterPredict_R", object, newdata, as.integer(option), as.integer(ntreelimit), PACKAGE = "xgboost") + ret <- .Call("XGBoosterPredict_R", object, newdata, as.integer(option), + as.integer(ntreelimit), PACKAGE = "xgboost") + if (predleaf){ + if (length(ret) == nrow(newdata)){ + ret <- matrix(ret,ncol = 1) + } else { + ret <- matrix(ret, ncol = nrow(newdata)) + ret <- t(ret) + } + } return(ret) }) diff --git a/R-package/demo/predict_leaf_indices.R b/R-package/demo/predict_leaf_indices.R new file mode 100644 index 000000000..1fc64ba4a --- /dev/null +++ b/R-package/demo/predict_leaf_indices.R @@ -0,0 +1,22 @@ +require(xgboost) +# load in the agaricus dataset +data(agaricus.train, package='xgboost') +data(agaricus.test, package='xgboost') +dtrain <- xgb.DMatrix(agaricus.train$data, label = agaricus.train$label) +dtest <- xgb.DMatrix(agaricus.test$data, label = agaricus.test$label) + +param <- list(max.depth=2,eta=1,silent=1,objective='binary:logistic') +watchlist <- list(eval = dtest, train = dtrain) +nround = 5 + +# training the model for two rounds +bst = xgb.train(param, dtrain, nround, watchlist) +cat('start testing prediction from first n trees\n') +labels <- getinfo(dtest,'label') + +### predict using first 2 tree +pred_with_leaf = predict(bst, dtest, ntreelimit = 2, predleaf = TRUE) +head(pred_with_leaf) +# by default, we predict using all the trees +pred_with_leaf = predict(bst, dtest, predleaf = TRUE) +head(pred_with_leaf) From a1e188aa7503d1a4ccdc09ef1e7e5663f02fb4b7 Mon Sep 17 00:00:00 2001 From: hetong Date: Mon, 19 Jan 2015 13:35:11 -0800 Subject: [PATCH 03/94] add nrow to getinfo --- R-package/R/getinfo.xgb.DMatrix.R | 9 +++++++-- R-package/R/predict.xgb.Booster.R | 3 ++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/R-package/R/getinfo.xgb.DMatrix.R b/R-package/R/getinfo.xgb.DMatrix.R index ed61ba654..830bbbffa 100644 --- a/R-package/R/getinfo.xgb.DMatrix.R +++ b/R-package/R/getinfo.xgb.DMatrix.R @@ -32,10 +32,15 @@ setMethod("getinfo", signature = "xgb.DMatrix", if (class(object) != "xgb.DMatrix") { stop("xgb.setinfo: first argument dtrain must be xgb.DMatrix") } - if (name != "label" && name != "weight" && name != "base_margin") { + if (name != "label" && name != "weight" && + name != "base_margin" && name != "nrow") { stop(paste("xgb.getinfo: unknown info name", name)) } - ret <- .Call("XGDMatrixGetInfo_R", object, name, PACKAGE = "xgboost") + if (name != "nrow"){ + ret <- .Call("XGDMatrixGetInfo_R", object, name, PACKAGE = "xgboost") + } else { + ret <- .Call("XGDMatrixNumRow_R", object) + } return(ret) }) diff --git a/R-package/R/predict.xgb.Booster.R b/R-package/R/predict.xgb.Booster.R index 8e1982049..cdb3f3dc1 100644 --- a/R-package/R/predict.xgb.Booster.R +++ b/R-package/R/predict.xgb.Booster.R @@ -52,7 +52,8 @@ setMethod("predict", signature = "xgb.Booster", ret <- .Call("XGBoosterPredict_R", object, newdata, as.integer(option), as.integer(ntreelimit), PACKAGE = "xgboost") if (predleaf){ - if (length(ret) == nrow(newdata)){ + len <- getinfo(newdata, "nrow") + if (length(ret) == len){ ret <- matrix(ret,ncol = 1) } else { ret <- matrix(ret, ncol = nrow(newdata)) From f295177b1d404ddff844a297e057a37ee6bd4d35 Mon Sep 17 00:00:00 2001 From: hetong Date: Mon, 19 Jan 2015 13:36:53 -0800 Subject: [PATCH 04/94] add nrow to getinfo --- R-package/R/getinfo.xgb.DMatrix.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R-package/R/getinfo.xgb.DMatrix.R b/R-package/R/getinfo.xgb.DMatrix.R index 830bbbffa..53ca5748c 100644 --- a/R-package/R/getinfo.xgb.DMatrix.R +++ b/R-package/R/getinfo.xgb.DMatrix.R @@ -39,7 +39,7 @@ setMethod("getinfo", signature = "xgb.DMatrix", if (name != "nrow"){ ret <- .Call("XGDMatrixGetInfo_R", object, name, PACKAGE = "xgboost") } else { - ret <- .Call("XGDMatrixNumRow_R", object) + ret <- .Call("XGDMatrixNumRow_R", object, PACKAGE = "xgboost") } return(ret) }) From c0c6951b7338e677f5d88fe5c85a9dcf7c612318 Mon Sep 17 00:00:00 2001 From: hetong Date: Mon, 19 Jan 2015 19:26:25 -0800 Subject: [PATCH 05/94] fix bug in format of input --- R-package/R/predict.xgb.Booster.R | 2 +- R-package/demo/predict_leaf_indices.R | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/R-package/R/predict.xgb.Booster.R b/R-package/R/predict.xgb.Booster.R index cdb3f3dc1..15af9f34d 100644 --- a/R-package/R/predict.xgb.Booster.R +++ b/R-package/R/predict.xgb.Booster.R @@ -56,7 +56,7 @@ setMethod("predict", signature = "xgb.Booster", if (length(ret) == len){ ret <- matrix(ret,ncol = 1) } else { - ret <- matrix(ret, ncol = nrow(newdata)) + ret <- matrix(ret, ncol = len) ret <- t(ret) } } diff --git a/R-package/demo/predict_leaf_indices.R b/R-package/demo/predict_leaf_indices.R index 1fc64ba4a..480578c1d 100644 --- a/R-package/demo/predict_leaf_indices.R +++ b/R-package/demo/predict_leaf_indices.R @@ -12,7 +12,6 @@ nround = 5 # training the model for two rounds bst = xgb.train(param, dtrain, nround, watchlist) cat('start testing prediction from first n trees\n') -labels <- getinfo(dtest,'label') ### predict using first 2 tree pred_with_leaf = predict(bst, dtest, ntreelimit = 2, predleaf = TRUE) From 3b190123c82bb0a235a271346f5e13254643342b Mon Sep 17 00:00:00 2001 From: hetong Date: Mon, 19 Jan 2015 19:29:24 -0800 Subject: [PATCH 06/94] update demo readme --- demo/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/demo/README.md b/demo/README.md index 908cd0627..e35f3033d 100644 --- a/demo/README.md +++ b/demo/README.md @@ -34,6 +34,7 @@ This is a list of short codes introducing different functionalities of xgboost a [Julia](https://github.com/antinucleon/XGBoost.jl/blob/master/demo/cross_validation.jl) * Predicting leaf indices [python](guide-python/predict_leaf_indices.py) + [R](../R-package/demo/predict_leaf_indices.R) Basic Examples by Tasks ==== From ea50f8e030111f659dd69b89c86eba51abd39eba Mon Sep 17 00:00:00 2001 From: tqchen Date: Mon, 19 Jan 2015 21:26:25 -0800 Subject: [PATCH 07/94] Squashed 'subtree/rabit/' changes from 1db6449..85b7463 85b7463 change def of reducer to take function ptr fe6366e add engine base a98720e more deps git-subtree-dir: subtree/rabit git-subtree-split: 85b746394e0bed36a22ebb12beb8672616b39047 --- Makefile | 8 +++-- include/rabit.h | 12 +++++--- include/rabit/rabit-inl.h | 34 +++++++++++++++------ src/engine.cc | 4 +++ src/engine_base.cc | 15 +++++++++ src/engine_mpi.cc | 17 +++++++++-- windows/.gitignore | 2 +- windows/basic/basic.vcxproj | 2 +- windows/rabit.sln | 6 ++++ windows/rabit_wrapper/rabit_wrapper.vcxproj | 2 +- 10 files changed, 78 insertions(+), 24 deletions(-) create mode 100644 src/engine_base.cc diff --git a/Makefile b/Makefile index 33925d025..20045bbd6 100644 --- a/Makefile +++ b/Makefile @@ -10,13 +10,13 @@ BPATH=. # objectives that makes up rabit library MPIOBJ= $(BPATH)/engine_mpi.o OBJ= $(BPATH)/allreduce_base.o $(BPATH)/allreduce_robust.o $(BPATH)/engine.o $(BPATH)/engine_empty.o $(BPATH)/engine_mock.o\ - $(BPATH)/rabit_wrapper.o + $(BPATH)/rabit_wrapper.o $(BPATH)/engine_base.o SLIB= wrapper/librabit_wrapper.so wrapper/librabit_wrapper_mock.so wrapper/librabit_wrapper_mpi.so -ALIB= lib/librabit.a lib/librabit_mpi.a lib/librabit_empty.a lib/librabit_mock.a +ALIB= lib/librabit.a lib/librabit_mpi.a lib/librabit_empty.a lib/librabit_mock.a lib/librabit_base.a HEADERS=src/*.h include/*.h include/rabit/*.h .PHONY: clean all install mpi python -all: lib/librabit.a lib/librabit_mock.a wrapper/librabit_wrapper.so wrapper/librabit_wrapper_mock.so +all: lib/librabit.a lib/librabit_mock.a wrapper/librabit_wrapper.so wrapper/librabit_wrapper_mock.so lib/librabit_base.a mpi: lib/librabit_mpi.a wrapper/librabit_wrapper_mpi.so python: wrapper/librabit_wrapper.so wrapper/librabit_wrapper_mock.so @@ -26,8 +26,10 @@ $(BPATH)/allreduce_robust.o: src/allreduce_robust.cc $(HEADERS) $(BPATH)/engine_mpi.o: src/engine_mpi.cc $(HEADERS) $(BPATH)/engine_empty.o: src/engine_empty.cc $(HEADERS) $(BPATH)/engine_mock.o: src/engine_mock.cc $(HEADERS) +$(BPATH)/engine_base.o: src/engine_base.cc $(HEADERS) lib/librabit.a: $(BPATH)/allreduce_base.o $(BPATH)/allreduce_robust.o $(BPATH)/engine.o +lib/librabit_base.a: $(BPATH)/allreduce_base.o $(BPATH)/engine_base.o lib/librabit_mock.a: $(BPATH)/allreduce_base.o $(BPATH)/allreduce_robust.o $(BPATH)/engine_mock.o lib/librabit_empty.a: $(BPATH)/engine_empty.o lib/librabit_mpi.a: $(MPIOBJ) diff --git a/include/rabit.h b/include/rabit.h index 1c5c70e5f..eb2834b30 100644 --- a/include/rabit.h +++ b/include/rabit.h @@ -135,7 +135,6 @@ template inline void Allreduce(DType *sendrecvbuf, size_t count, void (*prepare_fun)(void *arg) = NULL, void *prepare_arg = NULL); - // C++11 support for lambda prepare function #if __cplusplus >= 201103L /*! @@ -238,11 +237,13 @@ class ReduceHandle; } // namespace engine /*! * \brief template class to make customized reduce and all reduce easy - * Do not use reducer directly in the function you call Finalize, because the destructor can execute after Finalize + * Do not use reducer directly in the function you call Finalize, + * because the destructor can execute after Finalize * \tparam DType data type that to be reduced - * DType must be a struct, with no pointer, and contain a function Reduce(const DType &d); + * \tparam freduce the customized reduction function + * DType must be a struct, with no pointer */ -template +template class Reducer { public: Reducer(void); @@ -280,7 +281,8 @@ class Reducer { * Do not use reducer directly in the function you call Finalize, because the destructor can execute after Finalize * * \tparam DType data type that to be reduced, DType must contain the following functions: - * (1) Save(IStream &fs) (2) Load(IStream &fs) (3) Reduce(const DType &d); + * \tparam freduce the customized reduction function + * (1) Save(IStream &fs) (2) Load(IStream &fs) (3) Reduce(const DType &src, size_t max_nbyte) */ template class SerializeReducer { diff --git a/include/rabit/rabit-inl.h b/include/rabit/rabit-inl.h index 4ffd812e7..d2a20eecb 100644 --- a/include/rabit/rabit-inl.h +++ b/include/rabit/rabit-inl.h @@ -195,8 +195,8 @@ inline int VersionNumber(void) { // Code to handle customized Reduce // --------------------------------- // function to perform reduction for Reducer -template -inline void ReducerFunc_(const void *src_, void *dst_, int len_, const MPI::Datatype &dtype) { +template +inline void ReducerSafe_(const void *src_, void *dst_, int len_, const MPI::Datatype &dtype) { const size_t kUnit = sizeof(DType); const char *psrc = reinterpret_cast(src_); char *pdst = reinterpret_cast(dst_); @@ -205,18 +205,32 @@ inline void ReducerFunc_(const void *src_, void *dst_, int len_, const MPI::Data // use memcpy to avoid alignment issue std::memcpy(&tdst, pdst + i * kUnit, sizeof(tdst)); std::memcpy(&tsrc, psrc + i * kUnit, sizeof(tsrc)); - tdst.Reduce(tsrc); + freduce(tdst, tsrc); std::memcpy(pdst + i * kUnit, &tdst, sizeof(tdst)); } } -template -inline Reducer::Reducer(void) { - this->handle_.Init(ReducerFunc_, sizeof(DType)); +// function to perform reduction for Reducer +template +inline void ReducerAlign_(const void *src_, void *dst_, int len_, const MPI::Datatype &dtype) { + const DType *psrc = reinterpret_cast(src_); + DType *pdst = reinterpret_cast(dst_); + for (int i = 0; i < len_; ++i) { + freduce(pdst[i], psrc[i]); + } } -template -inline void Reducer::Allreduce(DType *sendrecvbuf, size_t count, - void (*prepare_fun)(void *arg), - void *prepare_arg) { +template +inline Reducer::Reducer(void) { + // it is safe to directly use handle for aligned data types + if (sizeof(DType) == 8 || sizeof(DType) == 4 || sizeof(DType) == 1) { + this->handle_.Init(ReducerAlign_, sizeof(DType)); + } else { + this->handle_.Init(ReducerSafe_, sizeof(DType)); + } +} +template +inline void Reducer::Allreduce(DType *sendrecvbuf, size_t count, + void (*prepare_fun)(void *arg), + void *prepare_arg) { handle_.Allreduce(sendrecvbuf, sizeof(DType), count, prepare_fun, prepare_arg); } // function to perform reduction for SerializeReducer diff --git a/src/engine.cc b/src/engine.cc index b58f738d3..c5041642e 100644 --- a/src/engine.cc +++ b/src/engine.cc @@ -17,11 +17,15 @@ namespace rabit { namespace engine { // singleton sync manager +#ifndef RABIT_USE_BASE #ifndef RABIT_USE_MOCK AllreduceRobust manager; #else AllreduceMock manager; #endif +#else +AllreduceBase manager; +#endif /*! \brief intiialize the synchronization module */ void Init(int argc, char *argv[]) { diff --git a/src/engine_base.cc b/src/engine_base.cc new file mode 100644 index 000000000..62739536f --- /dev/null +++ b/src/engine_base.cc @@ -0,0 +1,15 @@ +/*! + * Copyright (c) 2014 by Contributors + * \file engine_mock.cc + * \brief this is an engine implementation that will + * insert failures in certain call point, to test if the engine is robust to failure + * \author Tianqi Chen + */ +// define use MOCK, os we will use mock Manager +#define _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_DEPRECATE +#define NOMINMAX +// switch engine to AllreduceMock +#define RABIT_USE_BASE +#include "./engine.cc" + diff --git a/src/engine_mpi.cc b/src/engine_mpi.cc index 9962745a2..829051231 100644 --- a/src/engine_mpi.cc +++ b/src/engine_mpi.cc @@ -159,12 +159,17 @@ void ReduceHandle::Init(IEngine::ReduceFunction redfunc, size_t type_nbytes) { utils::Assert(handle_ == NULL, "cannot initialize reduce handle twice"); if (type_nbytes != 0) { MPI::Datatype *dtype = new MPI::Datatype(); - *dtype = MPI::CHAR.Create_contiguous(type_nbytes); + if (type_nbytes % 8 == 0) { + *dtype = MPI::LONG.Create_contiguous(type_nbytes / sizeof(long)); + } else if (type_nbytes % 4 == 0) { + *dtype = MPI::INT.Create_contiguous(type_nbytes / sizeof(int)); + } else { + *dtype = MPI::CHAR.Create_contiguous(type_nbytes); + } dtype->Commit(); created_type_nbytes_ = type_nbytes; htype_ = dtype; } - MPI::Op *op = new MPI::Op(); MPI::User_function *pf = redfunc; op->Init(pf, true); @@ -183,7 +188,13 @@ void ReduceHandle::Allreduce(void *sendrecvbuf, } else { dtype->Free(); } - *dtype = MPI::CHAR.Create_contiguous(type_nbytes); + if (type_nbytes % 8 == 0) { + *dtype = MPI::LONG.Create_contiguous(type_nbytes / sizeof(long)); + } else if (type_nbytes % 4 == 0) { + *dtype = MPI::INT.Create_contiguous(type_nbytes / sizeof(int)); + } else { + *dtype = MPI::CHAR.Create_contiguous(type_nbytes); + } dtype->Commit(); created_type_nbytes_ = type_nbytes; } diff --git a/windows/.gitignore b/windows/.gitignore index 38d71ecc9..3bc83e45f 100644 --- a/windows/.gitignore +++ b/windows/.gitignore @@ -1,6 +1,6 @@ *.suo *.exp -*.sdf +*sdf *.exe ipch x64 diff --git a/windows/basic/basic.vcxproj b/windows/basic/basic.vcxproj index 5c7c9603a..4e686584c 100644 --- a/windows/basic/basic.vcxproj +++ b/windows/basic/basic.vcxproj @@ -105,7 +105,7 @@ true true true - ..\x64\Release\rabit.lib;%(AdditionalDependencies) + $(OutDir)\rabit.lib;%(AdditionalDependencies) diff --git a/windows/rabit.sln b/windows/rabit.sln index 064dfda81..bf61256d6 100644 --- a/windows/rabit.sln +++ b/windows/rabit.sln @@ -4,8 +4,14 @@ Microsoft Visual Studio Solution File, Format Version 11.00 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "rabit", "rabit\rabit.vcxproj", "{D7B77D06-4F5F-4BD7-B81E-7CC8EBBE684F}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "basic", "basic\basic.vcxproj", "{A6A95246-EB0A-46BA-9471-5939CB6B0006}" + ProjectSection(ProjectDependencies) = postProject + {D7B77D06-4F5F-4BD7-B81E-7CC8EBBE684F} = {D7B77D06-4F5F-4BD7-B81E-7CC8EBBE684F} + EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "rabit_wrapper", "rabit_wrapper\rabit_wrapper.vcxproj", "{2F89A7C5-CA4F-4D77-A728-6702D9F33F9F}" + ProjectSection(ProjectDependencies) = postProject + {D7B77D06-4F5F-4BD7-B81E-7CC8EBBE684F} = {D7B77D06-4F5F-4BD7-B81E-7CC8EBBE684F} + EndProjectSection EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/windows/rabit_wrapper/rabit_wrapper.vcxproj b/windows/rabit_wrapper/rabit_wrapper.vcxproj index 6f2cf9f7e..73eb5abb4 100644 --- a/windows/rabit_wrapper/rabit_wrapper.vcxproj +++ b/windows/rabit_wrapper/rabit_wrapper.vcxproj @@ -106,7 +106,7 @@ true true true - ..\x64\Release\rabit.lib;%(AdditionalDependencies) + $(OutDir)\rabit.lib;%(AdditionalDependencies) From cd2bce47190532a9e4c14af1e3d92b65fae2c10c Mon Sep 17 00:00:00 2001 From: tqchen Date: Mon, 19 Jan 2015 21:32:25 -0800 Subject: [PATCH 08/94] update with new rabit api --- multi-node/row-split/machine-row-rabit.sh | 6 +++--- src/tree/param.h | 12 ++++++------ src/tree/updater_distcol-inl.hpp | 2 +- src/tree/updater_histmaker-inl.hpp | 2 +- src/tree/updater_refresh-inl.hpp | 2 +- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/multi-node/row-split/machine-row-rabit.sh b/multi-node/row-split/machine-row-rabit.sh index 69f94b9d1..fb3e3ba60 100755 --- a/multi-node/row-split/machine-row-rabit.sh +++ b/multi-node/row-split/machine-row-rabit.sh @@ -17,8 +17,8 @@ cd - python splitrows.py ../../demo/regression/machine.txt.train train-machine $k # run xgboost mpi -../../rabit/tracker/rabit_mpi.py -n $k ../../xgboost machine-row.conf dsplit=row num_round=3 eval_train=1 +../../subtree/rabit/tracker/rabit_demo.py -n $k ../../xgboost machine-row.conf dsplit=row num_round=3 eval_train=1 # run xgboost-mpi save model 0001, continue to run from existing model -../../rabit/tracker/rabit_mpi.py -n $k ../../xgboost machine-row.conf dsplit=row num_round=1 -../../rabit/tracker/rabit_mpi.py -n $k ../../xgboost machine-row.conf dsplit=row num_round=2 model_in=0001.model +../../subtree/rabit/tracker/rabit_demo.py -n $k ../../xgboost machine-row.conf dsplit=row num_round=1 +../../subtree/rabit/tracker/rabit_demo.py -n $k ../../xgboost machine-row.conf dsplit=row num_round=2 model_in=0001.model diff --git a/src/tree/param.h b/src/tree/param.h index 701721c17..2c2362095 100644 --- a/src/tree/param.h +++ b/src/tree/param.h @@ -205,8 +205,8 @@ struct GradStats { this->Add(b.sum_grad, b.sum_hess); } /*! \brief same as add, reduce is used in All Reduce */ - inline void Reduce(const GradStats &b) { - this->Add(b); + inline static void Reduce(GradStats &a, const GradStats &b) { + a.Add(b); } /*! \brief set current value to a - b */ inline void SetSubstract(const GradStats &a, const GradStats &b) { @@ -285,8 +285,8 @@ struct CVGradStats : public GradStats { } } /*! \brief same as add, reduce is used in All Reduce */ - inline void Reduce(const CVGradStats &b) { - this->Add(b); + inline static void Reduce(CVGradStats &a, const CVGradStats &b) { + a.Add(b); } /*! \brief set current value to a - b */ inline void SetSubstract(const CVGradStats &a, const CVGradStats &b) { @@ -368,8 +368,8 @@ struct SplitEntry{ } } /*! \brief same as update, used by AllReduce*/ - inline void Reduce(const SplitEntry &e) { - this->Update(e); + inline static void Reduce(SplitEntry &dst, const SplitEntry &src) { + dst.Update(src); } /*!\return feature index to split on */ inline unsigned split_index(void) const { diff --git a/src/tree/updater_distcol-inl.hpp b/src/tree/updater_distcol-inl.hpp index fe872971b..c989f4e47 100644 --- a/src/tree/updater_distcol-inl.hpp +++ b/src/tree/updater_distcol-inl.hpp @@ -155,7 +155,7 @@ class DistColMaker : public ColMaker { private: utils::BitMap bitmap; std::vector boolmap; - rabit::Reducer reducer; + rabit::Reducer reducer; }; // we directly introduce pruner here TreePruner pruner; diff --git a/src/tree/updater_histmaker-inl.hpp b/src/tree/updater_histmaker-inl.hpp index b519f3480..8c617450b 100644 --- a/src/tree/updater_histmaker-inl.hpp +++ b/src/tree/updater_histmaker-inl.hpp @@ -117,7 +117,7 @@ class HistMaker: public BaseMaker { // workspace of thread ThreadWSpace wspace; // reducer for histogram - rabit::Reducer histred; + rabit::Reducer histred; // set of working features std::vector fwork_set; // update function implementation diff --git a/src/tree/updater_refresh-inl.hpp b/src/tree/updater_refresh-inl.hpp index 75ee3aadf..8613c8ea6 100644 --- a/src/tree/updater_refresh-inl.hpp +++ b/src/tree/updater_refresh-inl.hpp @@ -147,7 +147,7 @@ class TreeRefresher: public IUpdater { // training parameter TrainParam param; // reducer - rabit::Reducer reducer; + rabit::Reducer reducer; }; } // namespace tree From 6937384e625dd44b181d0216fde6234be1b7c874 Mon Sep 17 00:00:00 2001 From: tqchen Date: Mon, 19 Jan 2015 21:37:23 -0800 Subject: [PATCH 09/94] Squashed 'subtree/rabit/' changes from 85b7463..4ebe657 4ebe657 fix in cxx11 git-subtree-dir: subtree/rabit git-subtree-split: 4ebe657dd75999c1556c5f77625bdef2cd29917e --- include/rabit/rabit-inl.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/rabit/rabit-inl.h b/include/rabit/rabit-inl.h index d2a20eecb..e0a14f4ad 100644 --- a/include/rabit/rabit-inl.h +++ b/include/rabit/rabit-inl.h @@ -294,9 +294,9 @@ inline void SerializeReducer::Allreduce(DType *sendrecvobj, } #if __cplusplus >= 201103L -template -inline void Reducer::Allreduce(DType *sendrecvbuf, size_t count, - std::function prepare_fun) { +template +inline void Reducer::Allreduce(DType *sendrecvbuf, size_t count, + std::function prepare_fun) { this->Allreduce(sendrecvbuf, count, InvokeLambda_, &prepare_fun); } template From 947f0a926ddf12c66a31b412e8c9261cd16888b8 Mon Sep 17 00:00:00 2001 From: hetong007 Date: Tue, 20 Jan 2015 14:12:45 -0800 Subject: [PATCH 10/94] enable returning prediction in cv --- R-package/R/getinfo.xgb.DMatrix.R | 2 +- R-package/R/utils.R | 25 +++++++++++++++++-------- R-package/R/xgb.cv.R | 23 +++++++++++++++++++---- 3 files changed, 37 insertions(+), 13 deletions(-) diff --git a/R-package/R/getinfo.xgb.DMatrix.R b/R-package/R/getinfo.xgb.DMatrix.R index 53ca5748c..6e291fe62 100644 --- a/R-package/R/getinfo.xgb.DMatrix.R +++ b/R-package/R/getinfo.xgb.DMatrix.R @@ -39,7 +39,7 @@ setMethod("getinfo", signature = "xgb.DMatrix", if (name != "nrow"){ ret <- .Call("XGDMatrixGetInfo_R", object, name, PACKAGE = "xgboost") } else { - ret <- .Call("XGDMatrixNumRow_R", object, PACKAGE = "xgboost") + ret <- xgb.numrow(object) } return(ret) }) diff --git a/R-package/R/utils.R b/R-package/R/utils.R index 34ce003db..412132891 100644 --- a/R-package/R/utils.R +++ b/R-package/R/utils.R @@ -131,7 +131,7 @@ xgb.iter.update <- function(booster, dtrain, iter, obj = NULL) { } # iteratively evaluate one iteration -xgb.iter.eval <- function(booster, watchlist, iter, feval = NULL) { +xgb.iter.eval <- function(booster, watchlist, iter, feval = NULL, prediction = FALSE) { if (class(booster) != "xgb.Booster") { stop("xgb.eval: first argument must be type xgb.Booster") } @@ -169,18 +169,27 @@ xgb.iter.eval <- function(booster, watchlist, iter, feval = NULL) { } else { msg <- "" } + if (prediction){ + preds <- predict(booster,watchlist[[2]]) + return(list(msg,preds)) + } return(msg) -} +} #------------------------------------------ # helper functions for cross validation # xgb.cv.mknfold <- function(dall, nfold, param) { - randidx <- sample(1 : xgb.numrow(dall)) - kstep <- length(randidx) / nfold - idset <- list() - for (i in 1:nfold) { - idset[[i]] <- randidx[ ((i-1) * kstep + 1) : min(i * kstep, length(randidx)) ] + if (nfold <= 1) { + stop("nfold must be bigger than 1") } + randidx <- sample(1 : xgb.numrow(dall)) + kstep <- length(randidx) %/% nfold + idset <- list() + for (i in 1:(nfold-1)) { + idset[[i]] = randidx[1:kstep] + randidx = setdiff(randidx,idset[[i]]) + } + idset[[nfold]] = randidx ret <- list() for (k in 1:nfold) { dtest <- slice(dall, idset[[k]]) @@ -193,7 +202,7 @@ xgb.cv.mknfold <- function(dall, nfold, param) { dtrain <- slice(dall, didx) bst <- xgb.Booster(param, list(dtrain, dtest)) watchlist = list(train=dtrain, test=dtest) - ret[[k]] <- list(dtrain=dtrain, booster=bst, watchlist=watchlist) + ret[[k]] <- list(dtrain=dtrain, booster=bst, watchlist=watchlist, index=idset[[k]]) } return (ret) } diff --git a/R-package/R/xgb.cv.R b/R-package/R/xgb.cv.R index b071f08a7..e562610f1 100644 --- a/R-package/R/xgb.cv.R +++ b/R-package/R/xgb.cv.R @@ -31,6 +31,9 @@ #' @param nrounds the max number of iterations #' @param nfold number of folds used #' @param label option field, when data is Matrix +#' @param missing Missing is only used when input is dense matrix, pick a float +# value that represents missing value. Sometime a data use 0 or other extreme value to represents missing values. +#' @param prediction A logical value indicating whether to return the prediction vector. #' @param showsd \code{boolean}, whether show standard deviation of cross validation #' @param metrics, list of evaluation metrics to be used in corss validation, #' when it is not specified, the evaluation metric is chosen according to objective function. @@ -71,7 +74,8 @@ #' @export #' xgb.cv <- function(params=list(), data, nrounds, nfold, label = NULL, missing = NULL, - showsd = TRUE, metrics=list(), obj = NULL, feval = NULL, verbose = T,...) { + prediction = FALSE, showsd = TRUE, metrics=list(), + obj = NULL, feval = NULL, verbose = T,...) { if (typeof(params) != "list") { stop("xgb.cv: first argument params must be list") } @@ -90,13 +94,20 @@ xgb.cv <- function(params=list(), data, nrounds, nfold, label = NULL, missing = } folds <- xgb.cv.mknfold(dtrain, nfold, params) + predictValues <- rep(0,xgb.numrow(dtrain)) history <- c() for (i in 1:nrounds) { msg <- list() for (k in 1:nfold) { fd <- folds[[k]] - succ <- xgb.iter.update(fd$booster, fd$dtrain, i - 1, obj) - msg[[k]] <- xgb.iter.eval(fd$booster, fd$watchlist, i - 1, feval) %>% str_split("\t") %>% .[[1]] + succ <- xgb.iter.update(fd$booster, fd$dtrain, i - 1, obj) + if (!prediction){ + msg[[k]] <- xgb.iter.eval(fd$booster, fd$watchlist, i - 1, feval) %>% str_split("\t") %>% .[[1]] + } else { + res <- xgb.iter.eval(fd$booster, fd$watchlist, i - 1, feval, prediction) + predictValues[fd$index] <- res[[2]] + msg[[k]] <- res[[1]] %>% str_split("\t") %>% .[[1]] + } } ret <- xgb.cv.aggcv(msg, showsd) history <- c(history, ret) @@ -115,5 +126,9 @@ xgb.cv <- function(params=list(), data, nrounds, nfold, label = NULL, missing = split <- str_split(string = history, pattern = "\t") for(line in split) dt <- line[2:length(line)] %>% str_extract_all(pattern = "\\d*\\.+\\d*") %>% unlist %>% as.list %>% {vec <- .; rbindlist(list(dt, vec), use.names = F, fill = F)} - dt + + if (prediction) { + return(list(dt,predictValues)) + } + return(dt) } \ No newline at end of file From eb01acfad84bf4264e19ca39f7d447df05ded9bf Mon Sep 17 00:00:00 2001 From: hetong007 Date: Tue, 20 Jan 2015 14:35:44 -0800 Subject: [PATCH 11/94] improve demo of cv in R --- R-package/R/xgb.cv.R | 2 +- R-package/demo/cross_validation.R | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/R-package/R/xgb.cv.R b/R-package/R/xgb.cv.R index e562610f1..988f67470 100644 --- a/R-package/R/xgb.cv.R +++ b/R-package/R/xgb.cv.R @@ -128,7 +128,7 @@ xgb.cv <- function(params=list(), data, nrounds, nfold, label = NULL, missing = for(line in split) dt <- line[2:length(line)] %>% str_extract_all(pattern = "\\d*\\.+\\d*") %>% unlist %>% as.list %>% {vec <- .; rbindlist(list(dt, vec), use.names = F, fill = F)} if (prediction) { - return(list(dt,predictValues)) + return(list(dt = dt,pred = predictValues)) } return(dt) } \ No newline at end of file diff --git a/R-package/demo/cross_validation.R b/R-package/demo/cross_validation.R index c7e7ba537..47a0adea0 100644 --- a/R-package/demo/cross_validation.R +++ b/R-package/demo/cross_validation.R @@ -45,3 +45,7 @@ param <- list(max.depth=2,eta=1,silent=1) xgb.cv(param, dtrain, nround, nfold = 5, obj = logregobj, feval=evalerror) +# do cross validation with prediction values for each fold +res <- xgb.cv(param, dtrain, nround, nfold=5, prediction = TRUE) +res$dt +length(res$pred) From 6901e9073036be6ab4f216b643f1f9c1f554f097 Mon Sep 17 00:00:00 2001 From: hetong007 Date: Tue, 20 Jan 2015 15:51:42 -0800 Subject: [PATCH 12/94] resolving not-CRAN issues --- R-package/DESCRIPTION | 3 +-- R-package/R/predict.xgb.Booster.R | 2 +- R-package/R/xgb.cv.R | 2 +- R-package/R/xgb.dump.R | 4 ++-- R-package/R/xgb.importance.R | 3 ++- R-package/R/xgb.model.dt.tree.R | 3 ++- R-package/R/xgb.plot.tree.R | 3 ++- R-package/data/agaricus.test.rda | Bin 37713 -> 15452 bytes R-package/data/agaricus.train.rda | Bin 216514 -> 57282 bytes R-package/demo/00Index | 3 ++- R-package/demo/create_sparse_matrix.R | 4 ++-- 11 files changed, 15 insertions(+), 12 deletions(-) diff --git a/R-package/DESCRIPTION b/R-package/DESCRIPTION index 7fec935f6..0384fc599 100644 --- a/R-package/DESCRIPTION +++ b/R-package/DESCRIPTION @@ -25,5 +25,4 @@ Imports: data.table (>= 1.9), magrittr (>= 1.5), stringr, - DiagrammeR, - vcd + DiagrammeR diff --git a/R-package/R/predict.xgb.Booster.R b/R-package/R/predict.xgb.Booster.R index 15af9f34d..122e116c7 100644 --- a/R-package/R/predict.xgb.Booster.R +++ b/R-package/R/predict.xgb.Booster.R @@ -11,10 +11,10 @@ setClass("xgb.Booster") #' value of sum of functions, when outputmargin=TRUE, the prediction is #' untransformed margin value. In logistic regression, outputmargin=T will #' output value before logistic transformation. -#' @param predleaf whether predict leaf index instead. If set to TRUE, the output will be a matrix object. #' @param ntreelimit limit number of trees used in prediction, this parameter is #' only valid for gbtree, but not for gblinear. set it to be value bigger #' than 0. It will use all trees by default. +#' @param predleaf whether predict leaf index instead. If set to TRUE, the output will be a matrix object. #' @examples #' data(agaricus.train, package='xgboost') #' data(agaricus.test, package='xgboost') diff --git a/R-package/R/xgb.cv.R b/R-package/R/xgb.cv.R index 988f67470..0aae574fb 100644 --- a/R-package/R/xgb.cv.R +++ b/R-package/R/xgb.cv.R @@ -32,7 +32,7 @@ #' @param nfold number of folds used #' @param label option field, when data is Matrix #' @param missing Missing is only used when input is dense matrix, pick a float -# value that represents missing value. Sometime a data use 0 or other extreme value to represents missing values. +#' value that represents missing value. Sometime a data use 0 or other extreme value to represents missing values. #' @param prediction A logical value indicating whether to return the prediction vector. #' @param showsd \code{boolean}, whether show standard deviation of cross validation #' @param metrics, list of evaluation metrics to be used in corss validation, diff --git a/R-package/R/xgb.dump.R b/R-package/R/xgb.dump.R index b6c829663..7658557dd 100644 --- a/R-package/R/xgb.dump.R +++ b/R-package/R/xgb.dump.R @@ -29,7 +29,7 @@ #' bst <- xgboost(data = train$data, label = train$label, max.depth = 2, #' eta = 1, nround = 2,objective = "binary:logistic") #' # save the model in file 'xgb.model.dump' -#' xgb.dump(bst, 'xgb.model.dump', with.stats = T) +#' xgb.dump(bst, 'xgb.model.dump', with.stats = TRUE) #' #' # print the model without saving it to a file #' print(xgb.dump(bst)) @@ -54,4 +54,4 @@ xgb.dump <- function(model = NULL, fname = NULL, fmap = "", with.stats=FALSE) { result %>% str_split("\n") %>% unlist %>% Filter(function(x) x != "", .) %>% writeLines(fname) return(TRUE) } -} \ No newline at end of file +} diff --git a/R-package/R/xgb.importance.R b/R-package/R/xgb.importance.R index 69715d3cb..c2688848b 100644 --- a/R-package/R/xgb.importance.R +++ b/R-package/R/xgb.importance.R @@ -32,7 +32,8 @@ #' data(agaricus.train, package='xgboost') #' data(agaricus.test, package='xgboost') #' -#' #Both dataset are list with two items, a sparse matrix and labels (labels = outcome column which will be learned). +#' #Both dataset are list with two items, a sparse matrix and labels +#' (labels = outcome column which will be learned). #' #Each column of the sparse Matrix is a feature in one hot encoding format. #' train <- agaricus.train #' test <- agaricus.test diff --git a/R-package/R/xgb.model.dt.tree.R b/R-package/R/xgb.model.dt.tree.R index 87b9f3a99..b67597126 100644 --- a/R-package/R/xgb.model.dt.tree.R +++ b/R-package/R/xgb.model.dt.tree.R @@ -42,7 +42,8 @@ #' @examples #' data(agaricus.train, package='xgboost') #' -#' #Both dataset are list with two items, a sparse matrix and labels (labels = outcome column which will be learned). +#' #Both dataset are list with two items, a sparse matrix and labels +#' (labels = outcome column which will be learned). #' #Each column of the sparse Matrix is a feature in one hot encoding format. #' train <- agaricus.train #' diff --git a/R-package/R/xgb.plot.tree.R b/R-package/R/xgb.plot.tree.R index 01261fab3..443446916 100644 --- a/R-package/R/xgb.plot.tree.R +++ b/R-package/R/xgb.plot.tree.R @@ -42,7 +42,8 @@ #' @examples #' data(agaricus.train, package='xgboost') #' -#' #Both dataset are list with two items, a sparse matrix and labels (labels = outcome column which will be learned). +#' #Both dataset are list with two items, a sparse matrix and labels +#' (labels = outcome column which will be learned). #' #Each column of the sparse Matrix is a feature in one hot encoding format. #' train <- agaricus.train #' diff --git a/R-package/data/agaricus.test.rda b/R-package/data/agaricus.test.rda index bffe6de21914ccb338731ecb208a76d7e7d581bb..ad8d50af7a790ea86702efab6be043f61cee4151 100644 GIT binary patch literal 15452 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 SMoVcvFb#_W000000a;qfWC!H{ literal 37713 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 diff --git a/R-package/data/agaricus.train.rda b/R-package/data/agaricus.train.rda index c471d01733ff83f8323147da46a867629c22b938..3f5f241445f313d7ac7a04f3237f7bc19b0de32a 100644 GIT binary patch literal 57282 zcmafZRZtvG@FpJIeQ{VE!lJ8YBT>gulP{=Sybx8Vng>(OiIv2%aBM;t;6`uG2nU2(4^RTbgpiB2NN&@`jqMLkr0 znpLY4(ob4mwrdNUgGC*>MayVlQUmqF9KiS1E>7|WyJ^fgYbk&wVr{K@u+4b|SpR_B zom<05`|7aCv&(LsNOonh^=i0D)3T~{bL3L&bh33`e|fWCSz`0orH2PwJygnQ zg_wXwiT-5=TWqa6`%+EkWJICg;qQPwwV(=%%D^eFdeWsA#n6wECx zZeqVWy4tL5T(`Lz?&u(2Z`m}c0CzDj+NbT=+IFFy*66LRc^N5Gx9CH2t(D_aI_-1X z96hHtZRO7m@<(NqHfy0?MvHJowz18Y%Ytsvncyzh%XC{S_a!RciG&EPEQnE?J%PDe z7v|gm?ROx|BGqDrCOi~qgw#qty`HgKT%5d_535POa#|~e-@2$b#-c>(<=}uj&r7PR z#)!PODFD6L?6j3%jSQ3LZmDTKXOq=hBm&!aK&SES?d`3-96h{TJo7u@qtV)Q?9&|u zNq3>YE_EE!&bzb+na_SrE4WW{*RqvMkq8_{yV-i?G_zHk1+lRr=54|;)1sl=(M%E8U;@ZPp(*rN5zg*g7!o6K@^_{W^Qf{izx*f z_H|LNxCl#?`#z*-QH-L9`vHE56kHDD8*&Q2gi&enfRu}a#dH=_U05ZmSas%I{I?V} zFuS_qudkDqmN+#IatItsG#oa=sgepqWr4>1vJg{?Fhkh`45u(QQVur;QkYET5DZde zLM25JQm_pviLl~=?W@=jN;XB+E4P(mycMXRy1GccIvu~_WNBzT)i3Bxy-Z_SWsy0D z@~reh(l+%g)!cu^fmiw;3)#SMbHk9@_y+%HzM}t8{5Rs#P{nYBq|#S}DhX@cr{bh2 zeX6v9L5iw6g`t4X!>p+=F3yQzz)#rN_~y#(inj=hpTRZ;W6@CLi22F;7BJ4g%Kp<5 zRZ`3;jCpoquFm_{{lT3aj6kQ-@rHLUiuTrL{)ivDCOlY9^gHku?WJGi#to5)#=42f z=ccm7r^5D|ce(8Y*o*$yXqZ?rjsfBYqoq_lxlj%UMw;4uc@|SaTAvdPds;g*bIs5X-hW6A~MfiqPoTiU7=8wse}U% zQ=$LEDdelT*_W&kjr~v)V)ifRXmrmfsVv2BhP2WYiQL{&Ji3_fs2zqPVo+B^_-F#4 z{jC%`b@Vwk?fiADMfAR|jcx3UYz`d}h@N2?)r9H`a9?j8s|JP(h4_>wQ1=sNQr)xY z(XM;XPhxO!Zc}!VUmC&jHZZS%vbG@a(-d544Nq8%$&;8NI3GkRrYgLGufiwuDXcB* zcF6Q%Iq~y4oy=#TEchjq)ha+Q)4%5ZO#WtD>%6c{0;)KsvVo!{LmSi~VTCLw(vPYo zSa%c07 zaVD*ZC)BaicQVxY8@+HLSPuRJRa)N-FLm)DS`)nRM`fPM=X<$?s6K)=CN0TESz#R_ zwm=fv$pSO|c#uTMT6XiY>A%*{{z&Ga8RSHck-nHWYOOR-uX>EutBdoT0m`FU{xTi4 z%(0Q5&_7kik-Ch#{_W*|0%4XuGfQLvoLcFyC{c^inWz{>iInx?)}CLKeyo%;wfd}f zr3Nii%lP3_&PC@hNt>m<($zwR)NHG+r7$q#!5M8!MK#i%@!3vmRMhF0AL@?I*=b~Y z>mO#P=;6%G83B-%^KCh|hnlQ4>b1T)nt|*Ay;-RBEqTgn z)(<60%kw|#WF5mYJ?-CI*fEG^&*$v1^Q=Gl_0JyvY`+ccOi@TPHbF#c)66hhypS|a z7?$>4d|j3m`Ths3+zGXjN;{Y z!Fq#p5X+U*SNXA~I>PWR;lA(%m$UI? znbPw0px*C*mO0z_J4_X@?+TYz+}IAXwbb-+A4B3XRQjM9|yUH)abUNLBfCNbe_!t3IZq}I`4Z)#M6(<`n(Y2G4@lFQq?iAjHp z^+T1>f_sF5IurMAPPV@bak4m8unIvsWzW z9|AgGg$|MzCEzl+VbA01HN!IvfW7i6Tjp7ez|{D4s04ZI)mUdW!1_;k{V)A*LE*t0 zV?hoP*n6ZE>(TzdvO!ZxZ!o0*%WK!Ppg~-ZQ~}jDX9J;5)4msETE$k^w0|bB4xiuC zn9WAtH3=Qvmg}cKei8zj#n!t1a^fGN4K)7!5@YUUXlC;-8D>T0R-N&g69E30fNLll zy$xnRYTCF`FXcg1Nhq)b8=N4-4Go9nbWj6BNk>wSSHBBnDsq zwj+?NFUuBQEV-H+-rT*zx zPurAN5-8k*QMa1Bk<>J6`Jpl*8}-?v3c>QDpMcBT(lq@!*b2L9;GphElmHMPe*7G~ z!CH;A6{bQQT=j8J*o@z-2I(UQVyFlQ{Y*(7Xv?2{XCfI*s@F>k{CI}j%;L(I-DP<~ z(U8F~Zwm?q>LqBrQ)@If<*mh+Ziu?#BsiX!HW_!ybA#3hDPL5p9CH_J`lce#>0F-0 znoR{aD31}rWM*EZI=9%m=B(p&pch6hFZ(voi-dB_FVMd;0(l;UKwSb?X^YH}nmcU6 zj&rLc7wMoPR7=#0;)2wxk6YCmB;2ElqtH?V0U0j=T&0F`kU#;)N;+13iX$|CT{X*r)+urk=OS>xc(6LbwY)z*bH{OO?l$Eu@*t=z`A+!9i=>`*1ExFE zE3$?xVDr-ew$jc&**G(d$2f_jXR}hN2}#Va8u1>%m3^&M}@R z3-Q)}{{EgtYwd5yX-V@%*7PFjoIeRcC%;sp6@L$}<80tpTyGUMo?{znqo&=e?kQnb zpdZw^LJYCIEw~{ZVTp>4b>_<3ayg(;S6vU{&>kMlM-ZxBBjg&bjFC5^J@Zp<_?7Ua)>zQQ1H66sd&g9*+`K8;B78!O8eBG)BFT~`N$kenkU6{vpswV4&fk@fufz6k`9|TA zVW1^udNuyZBr>wyaK;W#`c$y@nV(j+|)20xGkPR|Kx_-W-OtlYB0WI+*G1{IyzLx{&i4J-+Yz=cfDyGNJ+%} zFC$qkiJF-)P7pcLXu+fmfSjrIBTDe@)IUNzjC+=UR-@x2&hx)>6UU#o%zj$oC2HOMW3rU-G?OW zj5og)Zv1`DV&{_9+|Hu}J^vY_g1*fM(X>Rwk?z#MwavfksSPhFbsxaet?2Y^rJkkjIaq1FEg2*+XcYv*^ z;xAn(={TEY57mWyM4nS=$YT=8t$YTV8A_ftZs4p1vy;AMn)%;k9h-ewfqsj#wbqjd zaXuNF_8c4si|kUKd77NC>e}uhzNpSo3aGc=Plpl5XNUdxzf*V|Yk6*!^n+dwbIfLg z!j){1&Wc~-1D%O2EY@(#&cc$nZHvpRy#sO#wZ?Z8~XXlL~|&H7LorNeGhUOiBNxVP3Io`Tj^pY+cFH zz;8V@bgrTW?3rI)G*ye$*pF*DCE%`e_G#P-J~-Gp@^=g}*1pfAYYIF}GVh1vdmzka0D5pS9{L&-`~zDp9qxRM=Gc;k zt*43r0;->jK48vqD=Ph(eQ1|^hAITQjgupMs*-ZT0vXubNRzm|OA>aYwpn7`pd=0W zGNV&07qP?u2BM}q%e6?T-Wm8>Y>|kG)_O{yZD<+<0h=%b4#jP3fpRXU=hfsD<|5DE zIUFYNMUJVcGc}d214#)#V=^9GN(F%sIyW2HBVWa%oeFNj`m%|u)Wg~lV2nY#=eFI& z1zJ458JD(Y8c@8DOr!D;&0tt=?krBJ?74ianoAy@^*jNC`RY9Cu|PQ~ndP;m4XT-y zK(4QP>y^+epUjclf&ofTg8^rEh@wVPY^p3e@6D1-JD= z&9b05;3}w*sjq+zu-Tj`lKB#yJ6TJ$xz2dxn>l`vfj!myczm)z$SHo8TFK-Jh!2{Q zQ~z_dW{U<8HQ3sHw@3sKS7?4WvrNtr4+(c4dmmPfYc{S8zr}&O5te@!2)<)t4zh2O z7&PqyNg5l!J2Uq$x-k9T1F~m^yO@rCm(nsL$ai{?D6QlH4t$FUKP}B}`Tawp;;Wir z`gw10q~m~qDa%Whnwh+s0MYC&P;<~P-i^~!uDO{+SY?{F;C_D)v&xir%^Ye=%OBC@ zcQac;M8@M9cwI@ng-f`9pd(4_`3sZovn;@zcjfZK=fwzS7GoTf`EzDvRdG^IYjbEF zQ-g->=0`PpV(Fk(b{T#HF|Sjx0q39P)2}61QlFv>Hfo(DqC|7j#S^dUE?sd5B3Erc zMw1Qjs5=Nnbu%p49To_h7*}V0nwA5215`DmBc_z-v?-kc2?eAICg6Ba&)u8k>icNw zkujam+_VvWSF&ggjcsA`&bK&pIA!?Hp3m0}1+~aO!PBW;#$S-IEG8H5*35EKPsF>@ zwi&%zia2<^S||ZI@+-#vT6YTTl^Mjoh6{Qg<3H;z->w})IkkV!WuhFUM=bLSx26~4 zRGO;Z+(*~ObLL#t!h#J4T^^gJQCA~nX17p9KZV8r5^X(6$#ei1&&Y%LBEM!N|8T{h zJVE4kionbK+$HPqd2P%JGLlvyHA_v#xmHUxs!NWfX0>@#*ZXat{xXK{cqu;5XN>h2 z=%Bal{Z!P7BzQ5$LY(_9W*Kjw%B?xZQHR zd>4SZvi!ykw1nh)Ad)vKzZg5i@nGT8kBuBfIAC(ztaZj=yaFGOwuyyjW0LM*lXD%~ zA}Cx5$mJ5Q0uZWyF8NPgCqkq`W@|f-_;*zu9r=)n54GhyI@fthN^d=W{J3YAaNVD* zJN#|wOFbah4q<4+J4No&ySt77+>2|DaXl^zixYmv$<;mjZKBC!xV3wUh~EFGB@7p@ zHccmuKcgd1R)@)kd@xzbg(Oh+EdU@HC^$#5nQSY%9)k^w|7zowN&SmI-^u+hF(s~y z@r>9tW7zA$?|~4I%w^8+KReAz{XNMHT%_B{g3g_0!zwItquy>tYm0$8@@L5js}Z~h z^l_dE5cQ$yu!^s*rHZeZ#J09AVw_UTrYqb>(Qxt!DH7x&}>Zlb> zMq?lh`9pb9ra+R>@HuyI6;_!-3GT|WeNaMko@JcIXu1=B`aM}XKif>jXdD5?S; zFQ`%y^Vgn$09=A;_<{xkUK>4kCP}zT@tD0tGY|Mi^KpStIxnvat~j1o*cR7` zN^Sp7jC6s;1xi!CH+ofHm z1*sN*=ShH>h72!%SNjbq9^O!2miH?g-5 z+~&nTKp=#aciNwCrm0AKwChiGEeb#5KGzMMVXBYA+I=vT9G9th>EJ&3saO8M-w)E= zzUD77djI1=tT=yTQfBFW8u%Yz;x+*FA2N{PVw^?>Gtr*WtacApkYmNHU@F8q0v!C{ zP8XRruGhG(V{^i`sXNhr*V5E3uc6(&=XZzq-ydk&UjGY@YdgwY&J>nKXQ2=0{nnpk zV1E(e&zFb$@ruupWpu*?DAQn<&>X}gmsBc};1@Yz+@^E9M~rXBv*jvMGkf-ry~TBy zatoRjGfm&T#;x{Y$`@C2tWER8aY*EeUr>tOm~0Frj<>QPo-r;-3v9^k91S^rPC#SJ zPtHo{70H_;G|B5Q@Laz`r1%sn|5z-e%p^c9#7Tc2ejsDz3s>Q-e!crwmBn_^AdpJC zs{fc=@<))7<>NGpw{QdeMw+b#wG4!SzFIlX#ZIB35?(&Zz;g5ljh~xQKt5OJISJD9 zhrlf%331fpJ<^zcoAB?I_u)(ij3pWLlXPC9%@2Z%+xrN`!HT}+r`RTZTWMPo%XU99 zYpmRvh>$Q;zaMcD9I1yGov3S0^UF#4?N0BiKGDnPwH(H5-GwDr6+Fh@kC94lGhvfAZEuJsdZU%JsC+geTrP!&wF2JO$oTZ0?wv!OqI^x z7etubSo726;6lYOUg^$w8AtxXeKq+OaKe5|mMLU-BcE%-mP0_#$v>T9i*SgzI%_8o zn`b^wvU=M&nq1))Z*IP(9C%+4o zZpr602dBf4e}+I`l0@>_lJ`kiSn``Or?WhR$!1Et<^15$%)kh1aZ6iE(FhXFb*j*7 zDeu@Ch-12$*j%ZbC+1A>{VP0)HQC{*?1J$aBGkL~EJ==X z70{PoVC!lD!*(STdcFJq~)B+K@@ z2bjrBP;5{fq6{vAnu+Rs(Ygfc%_CGAq|I2&5n^sTGS*E6!r%_-g)yY|_G{B$uL;=} z5G9JKLD#A5WnIr!hq``DT8u_J4CAEsH{HXH6k=jHbu(OZ$8$v%$!+Hx&$DuGjt$?% zIxKyfHQT+5#A%ja?m?~y<=Ad{5D#gv@V_VIT?d$No%*1zdT7Gu5D19abZtHH_Kn^U zL^tVnQY*%WKLT}r;zt~8s zB!r5to^UL7kbwcu0mvh?c4qSayF#Jsm?w2*!f4`O&CwkCvTFa`i`T!CkNg(8Re)!S zYWa;N;DgF-rJm&LOCX%(czUtAT2}W&F2#T86=*|_ zU}%PsnQ<1*GsxhFO(&XwC z*j%8^0n~=9(S)&|p0E|y|By5N{U)Z_2&JPBzxI8H>R z)NITp+`O|c0>Blz3ClU&jBe~03wP1tAvJMcyG?0w7ULcY#Af4ZPv!L*!J2<6#2h{? z*qsY0a;-Tsw?K1PL4&YTcP8gO_^$Pdb5SGzNjqxyjTpXZ%jk7+1Le4H>Y-~E%rmJ9 z7DT9^L*x0mt_gHNsQ=Yu*P)@671V-N=|rC=o~k7M0CT>5RGTP;#@ zdULsOq3sf#el?r3j*{SJMZ2L*?&cK*b4>ab(eeH1q>iXvdVPACBVTeIoJ4O)k-K03 zayXLp4ww@JeKDJy{oG5H;6dos$=ABFi-Af{oBMp94t*JJ+Vb5N>+T-|G}$|LtA0aY zHfX_eWmV#x{FB#Ai*8Sea`qC`eTxwpDFPhdu|A6Zwp+4=a6Xb3C`OLOkU?ki$`8~Y z))qbv3~z{XmswKaQ7*cNC#8+qAE(ij*3&0k|5+hhWdv)Ma>0@Tq3SYE9a5`&4jDAC z(IsB6<`X$XUP$!x^$$0IZL%QY9BaeC`t&-k*I+J3%fEuE8J-Zl&3Dp{{@j&B1(6z6XPQ8m|H>)%4=(H$^*K_5f zN?(gfcetgsAE;2V8hptcO->i|9Dfe4KJ4n8D{!8&`boeYan(X-n`bgwZbyW_i5AN7 zFFv&3oK4$$c$u{|Q}}591YI!P#GTZX1gJH+ih>&>dDtGozXG?5vP9!ct;lB;q5cUi zlgRpKAgp*>E=|#BG7hu>%p54okjCOGt`yr=JyT@Dy>O2$*4$mQZh@U&qXO14Ar9$^TqU@vCJUG5Oo2zFbRM`y-c}(H`Bzp8J0v3uOg` z8T)~=RuNOPoa5QYsewhXd7m6X%qN3;Oga)xTj?O68zFWc%dP5XJ)O5hi93vUmCC=O zXaB;C7v+*Tc5g@RkBDY(2}b46`_QCQ*7W~ zGDB(hS5&tDYPg&7)93~96l1mFf#LO1s1_=}TlLqX9nl(JX{0PqD+1cT)3EC7p;|+` z>w&LKMI%*({6H-!>(7kUqcbAw-l~(b zKb9OO9j4-X-<&^+8s~lJQygp|+5C0B3MO8f?M9R)37gc_#YvkmM+Z$}1}-Y8Bkr5N zp)7j{nEV0)<>sbSB^4dM=w=*gzQUxk35ch7zKRCRxSp~M0xwle)S2AC*x1oI1<%r^ zN`&i_-w*>$#T@zB6KWT9wRS@bcgzew`u<2x$zK`n!5hQBPl}k8wk4~A5B%J27Eji8P_*x7&2fD=`)R)q=Xx~+=KzCL79_~@5(dISu2L|xy6d{ z?A1^D`2v%^%mw-mog?gaO`Sx@JmjG(KZmMjsy)yqyqb$%6>`gW@x;tIEA{-FL$@Zw z4m3ZHe$3-PcM@JVDx+n14eya7)DUQJEGVtZ3w6H~b)(zyJoF8VOH3lW!*Rz5AlMZ` zLpfGYn1pz6nrY`k=7k^-Gqd_`oC9VsvqiftTS}^n9qA}#GJ7`ktEESE9m(%Gc2gvk z1>DumVQyG;)89J^81yH5qzZWGbvEzjuU`M~b%sKxs8zOtOFVmEb#UE}d?T6hY=2sH zD}`1Shw;QI|KwubnE2xx_OvS@E0#y4gR$J6wQ7fCGUkg^n$?oewZt)+&aV?ergCpD zXSyf0ZnN?(Ht2p3$#GxOpAeTYzDhHpwX1e>|C94drVM`xr1nwI8;c-G_Cm>0uVr?t z0Ok=EM8w0CtJ>u9MYX5M$`B?_e_yf^v#gRm!2#9VHr|!hm5+t?K1a49H}pcFYr=}3 z`-V8Oc>J>sbDw~bbvwouMsRE4pVrl}{H|UQRnMn6vpRo9att^-K39Du)haGsL2pxP zZo9zYUe{gjxd%3 z|9KR(McVqB6Bycz{$f=p;ncR>kuUmKe$}_~&0;R^_68b$&|>>O#{CA-v@Z!Qaim`-s$)=*L~jmYWj)Vp;Ei)nkPv562Zd9Kt~`I|KP;nbwK8fJA9LsoxkW@`y2kgP_c=@K~u0CXZmwEo`qc z`_xD2%K1meWWP_&@}|--YE%BPJ2QLQ?`@_sSzZq*4k#$*LDD^&H!ks37K1dW!a%Vc;I)x7)l(Z3SEBA@5Qa4uTc0!7A+1gmRe7V-QEls{ zp%j_1CtF87$AKTTSF*3Kj!e(^`wG;+(_tm{*^vt0a7bN41X-B+!WrVIlprmzyqIz9 z;1z@$C|EVeD)Dqj$c(D;o56<6R3eM$&XMWyetN9nSz?PfM^~^$8viP;UGr3>l&4;- z;&d&l*2#~C5fks^Y3u+Lza16qp#n_h;6nH^g@58-O*cc@l#DggThD4F6vrXwl^Ifs z`N=r^&wEfLJ+z=9R8lFk${X2hwW!~Z{-ijAgfDSo789H>tJoO4UP`})1$?N*X4d)o zgb@~nL2-hdeY8^?9OjpyNfHV1{1VuI=3bNjD4eCau(oJ-`E7=bbSw#xTsXcmWpAU_UU<>P`NSXz8T7UcfOnaKo9*-aOI_u} zY6=lb-^!dCgg)lSgjqUzP&rIG4 zkB&4o%bg27Vq@pd5HojTn8RQ2-+R~-5p9sx6(r^@ND=FAEsN4D#WTx?Q%^{V+V48bzQ{^aCvFvbnU~&=E=w&KV2=~%)18jcz7NG- zIo^lOl`VZXF=H}WwX*z2&bXM2IeQH2U?T7z@2iNGBd!}GmtPxPlh$H&;Sr+NZ1cqa8L8T-h|`d`OK*+|y288cS6Fa{*kC!HsV}Z*oKs-)Ex4nHe^X(^g&XN|#c?K4 z#UIoxXqT%}$;*QxoT-d{f(B3{Q9Z*gBVOUnE{aE9=eaR`m1uSG6w)~9jeFhN-y5Cq z+nM^sheOgPkre;niptXk*H-=%qw&>jw8#l<3uolp`Pmy{Q)J1|tl)F(Eg@R6>G}4P z*Xr(bnAO_m9u}bv;H9k9WGgrxLgVD;As(M5q}A0sy9AHHDq4L$SdF5nOBPg}o)9t= z1yKu$i|FG(gKQtzvaA^Tx~XM70kcV_ixyBjuvepgBG}O6LlW0z2o$du;6z)GzsZ4T z)M+i9y-Tu8*QL=#LcfQF(*bhCgkN;5AbLLjFV@v;p;Or(c5s?mbG6Y2Gdh>JUC8B+ z^4UKLe|&%J71d`I;t4*sE!>5Z(iWO@J_w!CY9mhYp=!x0b^jb3YD+SqArt~KkJ87U zyW`|4BAhgH+)gHZ3U#>)z)>WaVUa*{zQL9jhNTeOIr#gBaKWLiPc!Z+x>p5E)g9~k zd)z8`P*7j6n&_wullB@%HiA{A^4i7n$gVj#O+W0ub~5XZ1kJ$e-~su&k=LyILUsZ?JGjn2=^sV{a3Bl+(gf8%MNDt zZr6DlH$vw%56mqv!*{T72I1%6?@CFs_t5&-oFv)3eWPsPC5LlPj2B?>01SVmNG6jB)=mu`u%J7b+C;w2#jG;A+w(@AKQNZ!#vG*MF%4L{ zPd*yZWQEAYV9z|+KLQYSADHS-a<^rZNa)CpcXNAs<^ddj%jgfVcqn+kqD|SOFMqwi zi@dY{2Y;E54>w9lYZn!gXQ5tL!QN`Ap|AWJs#^+Fd zY`z4Vx^e&49nq8F8z3M*P2w*ZzYcE%NwzJR;IjM>|HL}8i6Y&&f09`o@hI7PCx4nk zsj~XV_j6)s@O+*$0Y9q7tdRw~*34Kb5*B@R4@`vY!Dcw`4;58iJr&<)?lhC%7;b@e zA(2>~kz4P^yJ9PAf?4;7ph!JeN(oj{)369pDHlAPfPgTZ5WvoM3!h;FUFom=0ExA% zjiTsU)UbJ+t;c~m^(ioVdSJH=(wwib;C>|@DDRV{_QNt!RtbQ0J0l{@yUZr3AT)>A z(Eo4SVL;N#3!3Sz1z@*!dk+oF(bTKu)%A}AiJi5++j@&|zRBO{iO`R0Nt#^U(B%mN zno!y>EsBA1PprrF6(dOF1$6Z-D4^Rq(FMvvX|0^EH;|ZOdihtEFiCY8BZCugqM*t} zyr{NF_^x}0{;zSY_G|#&SHzfB4996ibRnhzwO!XfI=vp6&EG4tsN9Sz7h7rOr`J?d zLopMelb4^+3~5Cls%o-GeJ5GyBJmC9X>W--e^XLrZFI_@9OI2z-ydvV7N@;O6yL3^Y8}p`;J$Gs z*Z)vQGrj%l?LU&}E8kEp4JW9%%i^(Um&{4NxL5-eL&~)K&pmQF+stJu0B%rwl&VX_ znN9OE@uUBC;eFDGGyq_l{oNYNg1}b*k{}?6qs=|SStc#o84;_lczAdhU8A~eUBJ2b z<$m6@b*+)1v;ZJ9_i6!+Y1gx9r8Ck%i8!1=V9vm;zPzQrfTAz1frUqV$w!3nF8Mnz zZ<7V%3c9dscz)?DTK!rGM0@HjO*(SG2=_Jii$>Y9no%gojH$)h=wC2>HidpUzYSZw zxA~C2vTiMkIUZdkM5*^TLo(7b=+9pj$&6?Am}0ib!_PP@dR0!c9CIU6eM{ns%13%- z@n$2lR63XiR_j7x0CVEi!EwJ+V9!3En;h$wo?*`^V>QVfVrOO{h}kAd!oXpo_yd;x zNCWhMn|34@_)3dILQDaZr3HoZxK!bJbTQlenbKHiSRSh2uPm*5$+r+v6{ zm{{%>TdC3}AW3ISmbvAZ4w_=z)i}CrSCZ23AeHGX=CV%pMf*VL~c!&X;Es>D+FFCYo(?7pY zkJ4B?a?!!zn7{Y4gy#Oq)d!8HK-yik0`q?71yGrXBABl_egS!&8EO|;t$@CoIwqN{ z=&?+5=6e6C5j1y#&db^{N>t2hW}0xKKv6I`HcQR=N`myk_z7a2Ogy9zkgQpjS6Yw7 zzh zTz$p_K(6u07a>|!(OLdiXfn>bXg)*c#EV<@b3^4%dIv~P#JNRNo1tY1C8oYTGvD!t zM~V~lj&_y)=l9+DS2?X{H=KHmq@XV=+rOX5)tZP4fd;o%a~acHG6ltqx3Lx1#R%-! z7qAAh%`}nprys8X%^`sWwm-ixAxU=cJEgXF-P^G-PsWcIM9pG5&eIDrJOXnXX#}8r zY>8vHD3L9O)iV+$ba2~wkcx!PA5qNpiek1raQcUCh9EV#H-_2$>UcxJ7S|iH{ltjw z!toAqz0}>OE+1i%i z8`@Ux5nD3CWT`hW2?PsOKj-FZDu7`*qGwK^!EAXy3ZH3|p1t+-7T|=M9uTUkCP;Ci zxG&E!-)^f<-a_>thX2(Cd0T9OMTo7xqmkH_XKgY_twg7A)6V2;=^F8GJA*+fCkk;z zJzXdc-z=x^f%u%Ha=K6H!Z(Zqc$+YPyt{QyV`P~51yB?vD){H**NL~n0h5J7a;+Pw z5(b=+#%fHNfG=|y3s2i@50W!yTP)COhq~k`fxsN3JW!UcjNYnZ@5cQSz*|>mxsc5|p7jE&X z7Qfj2?0vFU$#;*KgOELjyc5%hK&%tgaj0HuTYhh-E@_28u;Rpj5qW!~A;QRB0)RRl zz=AvbxCvT1S4Fp?#Nw^!h;NUHL{6fb0OmsUDYjt%exBZ7J_}HphS-XA6hvOok4qLn zxVkI2m27Z)^I23ssr7@QnGZl=NSdE^yeHdrfNfn@{L@&m+7nkghk>EHHEmMJcZthbV6=0vGb0wYK8Llkp<8i^DPZA)aMVhGS- zFHuU()5moiWuC<>F`9DN{Cfd=5Lcf{v*kLMen}P?)QX!OW`BjuT=sVt*0C*LvZ@0_ zAI#2hwcI}bV@|t#ncyEy%S2A?aE}cLI<%|MY}iJA)L`cA3{t$#a*fP(^d4Tl(+taQAP}SG4qi?2Rq!FvkkkB+ zGJh=sPIuTM>BjBLkrsTiUoCwT?iF`wXlTps^|o<whLGw{@-T>>n?{M)GX%dDiW&hQ$Y~eE+;Mtbu%lOOF5GYrkAp6@fjn@+Ko28 zxeQiUE63WDT0WvCNSGiRch+xuPeLm-=eJ^JxVO^lC4ctu%le=klPqW4}nJPiC(A#zp`1Y{$JKB=N!Jk*S zLKV$$_XskW?VTeIPV3!fC_5cPgO1!y9~Xwyr=KFO-S=ZZ%4wzb*mrg~RtLnk)ZPuY zw`!@4bVdv@AG3?3Pife$FQ|ohDQ`hWo!YTl?uBVqhMP`%Hn+kX+8ceS0D^57RM|3z zF%Cw*S*GcTvgcs45?^7Y)$*y>GFO-6<0rlQoYNeOW6DyQwf#gV;Q85}sOC)mD}{Tu z3`fEnq{hyR5F1yES;!J{J(;GrqVjm|^ z%*B+XynppThq}qjd>9U;iqfJCJ`1P7qEO4pT z>`*VnaZN3y@RTr1z52+*VZQtkk3jG5{iV7(1@*vKt<`N`@r+FS>Fpe(H}j3>h_d=M zJ|89}Aeg0bU4X9Ebo^Srnjbe^xP&Pieo#(b1?i5x8M1fy&BtBNv^H-pRIvxe;ZJqj zk^*r7M8j7z=ochlflND?X?(&rJUa_dj&+Hi@^Md-^M>C8_c+^8u9)Fd@6$ILjfQmX z*K&tXeD42{amudbbL2bJRs|b`0?t-lo5H_nv?KBFbxbdh)1|tgnx6s9_fc(ubl$TS z61qbWBfkY`sx=dfm~@6=o3FUV5p9tHDfmCeGyMqSCtIk-I`X2!@Ueo}*330 zdoaa$C!xKVsgK+8rAx#NcGEp59_Y8URu{M;FJJ993k5oq_rBsbZzf65*i>QA88h>X zrFz(T%;E^kImlsH>}r^>v;9IFrD9h6m3H&jeuGlt@2m-XhTc#=MG*ogJuhx1xsb}1 zhuAk1ff$+QkpB`kZ?kg=weiOFX<+2YG>S~7dRIV^qUntFMPbc;+z_8qf^e4HY-M^E zd44`Z-4Xx9s2cN(i)j(EkhL0fXTrpSTuUl9)jErlZ0dJsCo$ri8-3jD!Ac#7yk>6G z8I0S1sXU57&05vH950}UigSt-W-3i28u>S=oV7g*-vI>b0{oktE~}w0Cu44WH$+zF zbVL(PLJxU@8S*warOV?mb~_NEC>xUfxs)oQbi;H|{i`%w&C9m*Zz@=Hr%(YY9~c&(|3F4xm+d z(LUfKI4!w!{y&GS?eY4cO7CgXa+70D|5_J}ao6c9CtaJ5(C+Q2@Sh`eP<)7SK6xde zXgIHR3GWb|SV5GnVnpA|DlBwo)tJE-qs2&q`N??DxdjDlARCM{bX!${g#W(INV2W6 zwBUyMa5yteLfwdV8fd#a%bk8?Y|jm;qOd&$uG~0z6|;CD>!>lf$mrujaOy%04E+=W z5E_@wX6Z8YLI2ue8|D5>c7{0bfy6R_CV1BF9&jtwCA@GHU}ginv9H8f--g(4?$Hfk zX%?tAPHa#TBSr)Z#CSut?#84qY`F13t$a<6`J}i>SPAs6FwSUt+@>v`H zJ}5G|F>gPClRk54rfvL73SLpn+Iu6}v@QCJz_#C(U>*#EU%R1waTj~3%3%|F`yE%QZJLid>{xb7PV zT7f?ugc?eEU~W>if;qcHsmHIx)3Qf=v?03YL)^fWu6`rQ2C5Nv+^Mw7V_6gzTVUSU z#9W$BS4|I!OiMvd;UQi9FTHtI5CNy}Pf$uhJ+mQf(-P9U`mD*f-xr;c)KI`tX)Rp` zS8QfkhG+VNqb<ay$L<#4Rt}YN@Krk{!$Itrx2OY2K6qcggTFa^#;hI zV`FtMtYW{~60}-YPx-(YtOq2NHp59Qyy7n;mL0w%{veQU%#XwaZLacl5KGNyN->bw~3^v#7ap=iVBmD zAF@xv?|HXg3l3Gr==3Yj1$1sL=8Q}0dJq_P;~jtvmml`Tu+a}>mK~Whu#~d)%^FEu zgTv^O-)weXYe^|(n{Zj+j>ga zhtD&p7ShJ~N!cJ>%N9qA-}C4uneni8hubAF;$H&JK`kWG@O?fMA8%hQjg{U*oJzW8LA z(->LqorI~R$aD4yOLdMok4Q@G6FL%-s@~P@61JXUp)BI7X=a42rb*EyRkUur(o$xi z`k)&m(3LZHD!PDNW_LiUr_L^f3fKH0ZeE(-3)l;TAnt*zZ)IiG$y)(&X0&O;aV+WYuSi7dlBlS9Nj6h=^#G<6!g6(>Exrn9 z`7`Y3E?c4u7|#@myEnV8YeJ?rn&!z4sZ(tS!*fU+HbYD`q^&ljK1wG( zs0wt>fE?J)8wez4>4=7kyz%2@umxLM_6U}fc#VK7l$DQqM%OTIcORAZbR#H!98IqL zgrl}_`!piTfMZD(t`O|Mm*`6!@FF>G^Q2)l%DPgoUvqS*{7Hb+I>%1wmINODx9)5- z6Nu(c$)uJZBtZPh1~QjV8OAx+p#G`L`K9J!>cVht`C(JBcYAx!F5Nhc_5u9&7?>mVA#6J6KZh)XulT2pduNci5~ z#r8{xf-%0Jsf1;Dv<8ZHd%dlMI&w~-3#zY5@KR1@$$G|$rP6Mg2u$g3b9u z%hq-_610oPd%wdR@RSjwO1|#knVX}S%+WUkuLP|2{^eG5GelxX8D{Vd?&khT zBLYm`fWXQ@#)V0Z3}fS{gT3*-fH}xLyij`BjN{A!^WuzY2KkkCy(rMIG{KFJeF2(@ zMz{IU8F;s#Da%uYdcP;At!=#$fyMkJ4YM7cu#-tzPkA_t`y`cCZpoyuz~^Lq zx`K=-uk}EdCp!ZsD$VFF8(XO$uteRoQ^viSD z0`~~@KpP$2$O(wwKhHrWZmQ_;{f0D*oZv)qhY!p_Z?oxXDrp_KokSbRXbWgF&KgHG zh#7sN_&OqkCmY(O>2sE;@IEk|~j7@JEFXM)VfMYK!vF!AWri_H; zw61_Rf|2$a%<3#QmUx>lUVzIHZ0dr6aipnYNWx6dK!zn_ag0i{iflN#eXSFgWp_Ur zbeEe=aB%eOO3ar4IO_yXHbGHqP4nRAmdRl@2JQAl1~t2;cQZ&M6uK_-0UMs*KA;zf zGxJ1E@w&ua@7|4=Z4tYto-_egLza*myWVIPc7Yi*D$0QFIClR()L7Y^pkh4lst6b= z9)KlCbkYdJ8nK>`EM$n=?0rr8Bo@x7-LQMb>1AK1LZSA^rX^A8zj&>z41&jZQCb@r z;SKQmBLfZ8?UC)@k0h16GM03*cMX{t{rbQw?5%oo}u0%{bek}^dHn1DZe~3ePh9J6%(-==f zDes^OzQs>SlOv-x(gHkYkZ!=IPe?G*s*UwP{ZN?6%%zi83P+3S5c_; zxK~nSfd`)>v3o3lVO-^;pz|=cN5%4K2Eb#LQKY8wHX}%lrqI<(^FVqrRkzRqnu+Np z9&TR!1Ye^WTyM}nunx&^(-(8vR)id79<&4*p7;gIGJAl9JO^O3EG95)Wlo7E_=G-$ zmKfnWq@MCN^pba)rhUYg8QRu_TSsi{ec1d$GBG|Q{Q(f{?p4BnTf{_d87Dq8AgNRI zlHZjQf+3zm^X`7=xoQR{BF4HL0rfK610kBVQm?6NNfn(4m2sT8yLCFX%VzYjzv8phfEsc2Kq|r zVH_4?zsyEmv36}e$abM3jHABvBaCG|&f1gi2(T1uttB~63LCu?7S055^Mypv?Yb77 zzmSU$INpxoJDN)_)J(-uS^(MYQuLCIIiJl|VW)L#T77Lj0BIItxJ%t}#G{oDx83df zQz5oWDR53J=s|4e@N-9pL>4ErZci~R)pLc`kY_Mf#`J)MFUcdb5NsKtX9YNo5+5JX zHyMM5oxVf3>d7MrYp)%|#&JGZMB_p%V()i7)P|g+i6>MI!E(0iqAi0q#eys`&5u0#;R9lh!4?t67~zcVvele68!UFdi|o z@^>>rFCg;=#L^5Vl#@amtji-_9w@PcS%i*fqEKQtymbS2mBK!o|1~^Pz#PHxUqAvj z`Sj8^Ww31-Q{hN0%?jFUwWanv(O~l97;jtXMmK~`5%fh27+mx-cchy^3}Dh$o)+lz zzt-qj49E;_O@yn~H%GP4-u&e=!X-~`_Y)E?mpnHd-Be)%MQY21j}`jGWJkPV+B( z|LG+f@MeK@Vcjcd)D7zJz7gm8-$%SG4k)0Hldgcdv`JXdjOQzvbf=OjiL|~Slf*@% zY*(Y756ngwyK}huh_)tFnh|-CJ0Y*=ij!#x2HkJ6QsuyEF0F+?V+vw$-ys!XMB~0H zFuS6)C~2m`xX{G>3w%)q8GTaZ;)sv(d^|i-=JE#q9J$_-%NQB?ud~DsTLIwX&)A_Z zE|?We^&*(Sd6k=W5L@rxup3K_rL*cRCN;#(R%-mju-uPnp(opTu=gOusU@AK(|;%U zXxw1d?Zdh8q=OCn(oNf_^pj~zW;yBtnpWcP_!8dAFz^=~^qGSI(z=j~KT*!F$NA`M zi5FOGg+mB)`T(Xeao6}&_yvX` z8t_X%MM_&a%fG@Pw;qyK!{17l zS95^(-gObRQHl4Q-_Q|Axwd7`Xq7r=d%X!^<>wIUkyREh6*_|;Um7*oTtI~QM}t+E z%CiLBOq!=_!#=}8*j(r+^JgFa3kgAtr8-x zABszl85MexRKqvYQGnUIoE4fS|jz(tW^u^G8yGq(~?Do!rTmK zMg-G){+O0bMT+qfbWFA6A~1~F;ngAF<(yLG_PwItFd$-XbG-pqOyno%^=J)45w2CK zJqwDGF9~!0^)mqYvvJIeU5-{?9`GrpS1sT205JGu(3=(;Ytb{IUI`fp-#h8~K$<6h zr^*G73Kw6QI(!r0QFb0S3Lo<3SsMz(2i=F zJ$dp}3#~~^jI4RHOiB0c&4x+=2Cl1=4U}u%? zWgY)6&qy-FdftVG3{BeighmmkIMNGd4Wm3Xm4h12RXgT{V`)vDyo1D6!KPg7t+@F8 zsJ1+e*&Jhm-$k2Pg$nx2b9colqe~ec3*oXXI=E%f`{>XaYZr8r#plpUm>CR@$OC7y zZ$M#zjiS;5)HEQ?U3-$YkyAZHb4xF`yX5MYK;^e(obmWT?YNb^;L=!1*x}hUlVUHn zeLpm|sQ}MxIwfKn;%P0KO~)kiOAav+=_u_d9_No_7R8Kj$KFB9kxX9X^Jof+?-2yg zq%7R9=3&Lz{(1_n7@K^BAm1q!IPuU!9Ui!SHU7`;rCj2RX*ZkA0U8S#pdJCs6X=sy zAX_Up)d6huO$g!M7RPJ-ZGV0&$PhCVo@rY_IGxYjfVU4{RH39w@1V^*k7T zy?Lgbgk;6ld>6PnM53n3S%*CdvYA;r)6^lU$B(2BB@&Os1La=DZkoO*uZSP8{FFeqg zH!z$_y~L_x?cZB7(xsOJqgpl`!HaY9#Yv&2Uns8H6_ePUr0cveQHCecm7z>BHwsA{*K9q+10V=mlk^wRcD4BJC?>%4DS|_!vgk!pjYrf&CKkDM_4sCOL{2lC$U+s_p!2O zJo?vYUgK{!6ilB5bP;S#hw@N&v=^E5BQ3Q^XAiq>pcKzmC%G;pj10>15whfo=_Zn; zyipL(R4B?|{`jz%Q+uxe387)2ZL+=Ms>)Uox;S>;$zY&0cv;hs40%Fc!Vl0&i99>_ zRIfCa8;{WX09$R%08frtUTG-bZPWK8pUWxJ(3NcptDevfPU{CjF5E@RW8NionryE$ zf}8y0rNpkFXbT?+aQ&Yj;Y$fUq~cC~NmEMM8^(a<(vH~lBYTNq@A?8GGQBWDOfk`gC_E5(aZEi zOC=`$5QszV?&Hl>!pskV$OP^_ut}+!fmcBayx6ONUjood4kfj#B%S zfP+ov!j-v`Es!y~nIQ*{oo&fg2+_mPrOYtqdzx-5IY5C^H+o8#tZ3h=DKza6=RH5l zMYPd-F6%Yqi!>@TVM}987M(NipsPWrJZuI_QFt>=-2Gq~yfyFASF|c$k^&r$&`Q$O ziSEOvb;wA(xMy|9W)DQ1_e_Rm5NdMwkaw9)XEi;psHP1V>H9oZgj-98Z_*vQMlLj5x}!&Sv`q!yEJcRGmKir8pI z9EiMezQQYThD>u$$3(O;8g4bWAre)nXEUFMDi?4K7SBzR!z^jiul2R?C3p3M0#$`N~`{X)8x@sbpGB(w0gbYb2hM zRA7AX3TMXqAiD!yVtWuo#s#^2Iz^0PVV#W$Ztd9bNiG>%L#UQ61(c%N4I1U!If^Oo#`twhvLJh==gqm z<5r04*PU$V?Ru8xvl>GattuXY$s)){acXON;$!BhcBY@C0f_55h0KulGg|>$QH}0z zQTZDR_=N(9hmMr8Xed2i$yg=xGW`Yd`2RXwGlrv#HuF}-+h}~C-v6M2(X!e7S?KXg z4IUmyW6FywEo5YVj7KsFcMN#VayTO!|; z-H_=0VQphV_(dBVgrZ39y4xwKimzKumt5x4UKgBr^RO9{SY&Hno;v|{G@kd-uoP04 za!Bh+8x>qV-MjKj7=V0mhJd#sTY9|9 zZ#S?7A{6X`xjqcedA*%Io7osMg$QiTzfCP!S-^9I=ks(yvRguOeNv8^3OCRc)vg?m zFT_j`i8Bs1U*{``Pj8X11_E{WN`zuQ#Q1)IGWbV#c^di^vAvlP`0lIj&=}4t2J7+M z`~tDGZJH}-UMLG>nn_c}BG4Il#g~_VNKK)%Tc_Ags~5%Yhc|HcG%IbmVi1RCue#Zl z=Y6SMwil#^B-3$jTzzZvtChs2^j+u*kOt}XNsKp3pLUYl2|mrOvq?OvX<*NU2nBx$ zIr~rG0~j9D$6rMdI6;)7J6?O}u-fQ=ta(01(f*HOE-|wWrrXBJQDbWAK6NrB>gPzQ0odP>??WNrQgvsz26AoI{@Ciqn5kBE}1($M?8 z@G8ui;Jz&Gz5a1Guu}<7!n%RQiZcyq6x(BA;etDCDvdr3XTisZ;`)NSY-4LS^wVTv zmTAEMHgs|5ST+r&X9rY9s>u*&D=L^)sLHT z*!@y{30X{Tsytln>XcwsB~AK(T~AA`*AhbFRHNuFvNu%olaFKQgP6ISt+3WQVtvW- z|8=??5_ap48mX0Ai#QPc#H!^!`CLOedY0p0cu0QYP{5mqw{<2LrX5IH;uD7**APrh&6cW)kvsFT zoI-)PQt;!GZxXv*h#7{?Pc$6()RRy=1P zsybQpG^bd(#UCq_n%hXGo4c83AL`-Zqy+!l7f_~hagIj2n)d}eh-{Rv#6+n+o^zGV6+ z3aWERDWR~H^Ps#Fxi0sl6fMS23HSu6cF9BNC0T{HG$lu(${)}rZMEApC0Ge3ib|L| z>H$rb7O{ir1L_JOm|jIkdFnp8H}*w|?$#WH0bf(c{wQf(?S9(4HN zO)&Jx_H6a>^B!jgQLKcbVc91Dlw<8#y{-5*I$Jt9?bk$?Okrar<_LR3FNC1h}#NjWPp=p|!w zi8`dLZSt?{e3D%7vCSpB0-uAqq?Fmh>-HdF=-;${SL!Fpr)~E;>oJ2CuW;P)4zVh_ z@nl!k5{5SStpG<^D?+w`n>v1!r-|&>gFHU#^=#Pt(QXI8)aN@tu@a{IGbr_ZB2}0h zW85V~iPu3XRx*tVGY@O^)`YUnv@;H{{WTU4V(MoSChT=7mOz=cUI-cJBwK>J#6XfS0s`1r40&pN>~_( zt2^mSEyUx$d%SrcN#yL7X=%@;IL>q7NnMpG=t?xm^d*kj#qs$9S03`7-f)yb6Ei*Z z4>Oxc4+rQy=N*xg1{;~2L_f$$chtJGs!H~dI;5$cDnG3~;lIy2h7)py{<^9Vi;=#8ccFXHH&^q0{%LmC*Ts8Ce3sJ z&CT4>N>q%;Pzq&7p(yS}I{-deQ`z_gqi%0TfF%`MXab#O&mktrpKp+rx^d=p65AYR zTz|a)sTKM}t*pAvpcKM&N}5>19+FtmU6NGuIj7{3%|LTo5K73+z*j=ed6`;ZXC{%20PdImR)B@W#ni6cwj%g71u!E!pTY!recSpUNTJyqj9}W~@c7ULJnog`lDxP*zHkb}4FQ&x z9y6TjAhQ|}T!)YYagV4Nevb|x7q&3p^x6zDn1oH^h;8pdTTXNaUo&I80R2#m6P}>qp(32a+^E)M0 z1TcN@DAL5josw||IfnbO_6JAKdaCU)wb8yI&iZ*FMnU7wqNZFYeFT4<`_93;X{MWP z?sgAj#JL_xB~e+P>f!FU-XY5Rz}ZC^6myd}%B$by@>0q?IC7hh7tGUMO785tdQr>h zalSgeK`Tw=yFY{_+G&3aslvPY9-O^u(_sc8G!x2f~d8&m@v4(}vnfcGSZwLQm0o&K88Wc&}Yc`@t$$n2GeRR3M(3nf#5UR34>!hsC!WK=nWn<4GIB8ahaXaG63aLt zZsV*xi2Knij?c{B{HZFA>T#hM!af;>lC}hG)_w{jNQO(;Pa~#Fj!&iC5PuOAS;R7p z^>j_->_m-ZHibH;)Jk^VF!{|1Y&6HADIq)L&<7iHeL~G9vfY)^93%bW+*3K>)CE8= z*#0_F$G>>m;~pIBi#FODzA-uJ$VyAA&YDS9Z6tj}qjf?0y#QY(Thao?UL!(Na;qzT zqEyUu^T-ThjaxL5-deil`%p$RUO-((e+Qs!`yXC)17SUr9MA*oW!F@cxHWO`M4M^D z{g{=QZ^UvE$=SHQHWIB~QaPb2Dw~h8O7@V4obmvcZwt@{OpK#BR#Y)hd0!S0623~Q^=mTs#LqfE~H;?gzU&tR% z#`)ivAJlS22GVTZ8@2u@l*_D!!d7afqhSLxTPwnvzTV0fO~u40j>_Ye1#X2zqb&1% zi6;7;UHZVoNz(VDp*n>}}8D|ZiFC|s?b0-ZGSq3Z7`y$M`!gU|=Fdd~Rj0WR`x{-~(LHFM)7yEfi$ z=O!3&{NS+0mQI?k!cDgLoj!Phi*&19(!Sr_Uu0pj7XUHb|kRs z=8@O~7>Rs@Zr5|aM?E`_ob!>AY-3l`aT@FejWUGC;C!t;*TY5Da6mQ>k0cYmk|Sh| z&P~(KKh=GQAiHB2-W+qi)1TxdqoxgXuk%&wQCB!_0gELYEydPcb=__OI97G9+%yRi~^T{O*3n zyMHfmDVvK?Ir{sHb2B8< z?tu%s%@tqa09&Ph$w^Snr0M}~HPA{vHkR)~QM#z+gsJ1@D~};4)Y?r6Smyb6*(FbI zR}bJIwkD&R5g4Ow^Z=MuItu+^`-k;UuD-|ZkLt9zPl*PUXD>9bvQBO_2F)sM+)=m_ zt-#p>9CHsk7Z(j_y6Ku`di?Uwti9JzHJxp7(Vd08Dx@asQ3^TZK9zF98!Z~e@|3hY z=~XL4Ul3)<0a51uD3rRnc&5lI(HU%dNo>q((w01AXsK7B2MoWKj$x!sk{wiiz4EEvXgBSip zeU4}X+k4Nx0L)7?f*o_-^yp8<`R#)YV@$SjIk-LOv|vorN=)n`MvJ*;BxD$gh~6)5 z_6ct$S}in1|BChrq%&9u9qAJJ2y=n3?`PB4uEC69cNF86!lrk-HK9c;;j+7FjVzx0 z<6t*3jRg7#F1Izg=^gKs>Q@-sFA#@~)37aO5v8N88q8%hjH^dje>bmI$SH_jsaTq8 zu%$Il5Ln&6%J#it;it41++XqfrEPD_kd^Wu2T^3V!en!?lSruB8VnqfAz0uO5!+d8 zg0B`*o9X7+7GT8olY{tx6wK{jl$EvYb3#@*;k>SZWu+L|)LhvrD`<73VDBV?@SXH& z7-HS3YvOtgHa2Y^J>4nte-#;HMYulBaQ@{5w_d#Rwa#9JvTcmSl1Jnv9DTQZX$BbB zHtRhDoM7OwK5xo>Lj!!KAp>B2LA*sIZ+=yGb)>FUB_=u7M6flJX1RJjotj3;vO|YN zvOjh$6B16w%GJtiac?-dXFDsVBbcs$TNF2_%^rLaGU8|aVpP&+Ry~BKV*F&C zkVXaT9|!Brkzv{hOC^R>zCI6N71;>V;pkMGZ8s-t z-{4vTn6Tz&DUT2eQU`5-qkXRniJ8v&KcTz)Ne8cH!Jyh{vq;|-Iskfc8J>bvspaQF zSWTy)DVS|nS`=tX=TvnsU$Db~c3Yls$sr&TYHY75Bp;dw%eC_5!je58*jvRnJ%}LC z$#u`74Ag^R-1qVoj|_?NR5L(Z5hZ^3^8>ex7;5{u{JINgIN=e`x?gWlD?Pm7WmO-S z%S$X8yIbQs*PZef;tDuzcH(+{pI}`vi$_;&(bx^5T2+{Qk#KV3j9=H~t@~c{WrH^a zFk#Jk-JrnUPX1FjoYpG)zaX4ULNxRiG(`bVEMVL1SY~$GjY^09$j3q!?(; z4lby^Lv6tA=kY?l=??_VX`ezU8W?`QPpPC8vO9b3itW&r?3Ov9CQ&1s3nfX~mFGfN zK1&PT43EEcrz*cfwKT<5=@FpE?bh!5Bx4GyZT>k>vD5}kwG`m{`kjAp#nK=~LC867l%SF8;W3okyt3BZwZlZ9PPIMMi z3WUwJAP+_;hcn{S(Ae_C3l%+ znh3%)oOpT>Y)o&}{z^H6LmTwI_vRKnzgMg3?{#pX{Lq_5IHxjIFHe$7e8nxf?1GIB z(H%Fg!ZDt)thEbd6-I4_Yh*d)4*SKVW29_}w-XUXUM}^oKbi0Y49T&oM)li8#(*UzO?q*XSb*;}uW45k&{8iYhkACu3bHgKaX~^-#y?^=SrL z!=2E(dPdhX@$#jB?9a^ng9~*Noujz47%<6(Ml}5Oq^1eFInL|(sK$}Wu3q@89}koR z_h_Jdy`K4yZ`Yu$qZ@6HFKfA^7i1PI6`>CVsXJi*H|P;!%QNYnP+(tG#vN2+8AlC` zmma}8xYn7~!-dDe_8j!&4$zCe&ok-FzsB=Ij|>~}4cwN#Plo}$e)alvjm*f$!;=R@ zM!`mtZH@My{M#iKD;*8@dTq}=zK%&@tlMbA%f1W=m^-}(lMzo!|MTtZm`E3KTRqfxY$VX_fdJ}m3J?Bc#QK3 z8*QSF&g0?6g_xltJF0X0h^C5YKE8t63WP@k`E-eVl||d&d|xClqB0L>QVyNe&`U8< zv8Hoy@czG(=bhOwyW+fQ{*;{I>~+VHddz{A6E4kPH9t2ruFkfEoDJRSbJ zJmq$=jcI$84};TRQ}%jGEu20EaerqR(pJG|>iHxT-`)&1+HZ`?X!oei#zxaH`~M^0 z^9jju!isOQAEtD;|=iN%`>o7m}Fzb-R{|>Oh(Ly#^HJWw^Um; z8cnWx3Ne>}s~TiUZXKcYi(5rCVju_?Cud*jbkZIw2OhoZ0P`7E_oRD<+*RaJO&M#yQU$@4#ot(>_?@Z==( zH|*$JrtP-=s*Z!wR?KW%-#4H!yp2;bJp~P)3xj(#7l=)u`(M213>1JosgYYY1w0Z8lZ5N~ZJ+vG=MI>*D3CpKc zW>pP6$Lu-mU%(S%6eW1_lPzd0w1pNSV}>7dV6EH`Hj8^HoWGPT*-N=;!2G_Qkzuy6 z2yK5Y5?!bV4-RSPq6R7|RkfsA9tPJoXSb>_W&=lUyLuF3VTO)R`ypFb0&fxi7*n{T zV`Dx{&oH-xy~Wzc5un1k2P8Q-{uAd6F|pGjYNzCX4HKKRga#eDpP(wSu@!qH9wRmi zwmTJsWzWY>eU?pKLxLAKcAVa4Sg%Iu2AF%>JboViTOfww2*b6S3%I$*bxh@6uZN$S z%Um}%sMLHv-1P$vqs+~^l4)TzVI)2$$E5RE1`mnF98~dspYae;yTJpFGv#cnF$`k} zh+6RErLE4h5b~i0y3WBzcp+yE_@Bn6DL#5wa>}qjQ?ezr%jFY~vh)~?j!mTBl%`jj zGloLQ&qvMjVDIj|nQ5;ck2am(Hg`jb#x2A6DTI;{&Q8a3^pvv(?A!RL#UT?HQFqbo zKd_53*TbSZpK_D;XuD`{*9eUwZx-?gbTK=08z$Szo4oDZ-i5f#d&(IT(jyD10zV{X zWJ4bc`hIIjs|FaY@Hpw@7X~!OuNmdCO09a@&wL0s$05f{lywYf8Jl$a->-df&8^V)pkP+JU++Q z=&kNZ+L@5;?7Gr3o^%xtt96SzbR%q#?rFKCTHc`@kmlw8?2$!P9A7^-$MQ>XY-7BW zkNSY%yK|K1H}pfQHyaqjHTHf_FyWawWx8fJLW z8Js~ID!Lbq(!394LQ8(9_P+fES3|LG+t!=8BMpRw^gJ`luE=A$WH$Pu8P=#VjLL^p z_SkS7*G??DWRHq3l-3X^YcakWbm}zz$>GTDh#8nORW$0KonQm_w+kHh~%pth; z(hISA_?LVA#1V~!u=k4tk_;`sPCBHmjOR2N*@OzaH+VV*RS;ah52TOsLf&0d8|Na9 zc0_2Lv0W~EX&bECN>Fdl9)m1}(IjfTT)#~NyQN+>U0q{NZu(~Fn7s#nDBHvB`-NkF z1L1RO+5AwAg((A?{ntRJc-m8UB}m`X=mFgD&sS6gewKa#K3uV3IWB9vsxUS%u={(S ztp*0z^9K8z=FK9T$7jc7+AaQ1=>{0n@XRtd@yGfrEMrv12g-+}t=o(#Vts1YGLWlN zHcz|BRiUfgh`E=6;_R@A2oX5X)~&;grh=z)_+VN{^{YQyjgt~a*tz+v=U_9kA%^Tn zoT%70hY63b7$pQgV4VyCSjrn>)wc8s+L>|lI3Rt?S z3#E~;U5wGHG-l6`D$(Tlx($!Kc%G7@V+%69G?Oat*cBLSh_92@f{Y3}M#3}1_!1hw zL7{9k&KNdv?nt&yj&e(NgwmyG%+;Z*!G-cv+{s#5tj^}NmFJG*PqdGbqmZ; z9F4|J3bt_$K2IBaGzGI@rH84}-$7usLeHJk;Q?0Gj1P#3LrgA2Kg~fb35Yg9OO3tl z^G#{zAhSmY4|T8SpsNC>uzsovC9deTBff%K2fjH7FMbo&gC^8G-dNu|1=|}NX}iOF z2CZM6XN*eciTS@oH{{6MID{%g$p+ozwf2|oKW1n#h@)?LRJJccfq`*Dll(xAZ^amD z_}Mei_G`p4G}Cu@YdQTOt0@rO+$H1v@=|EjLWRTF@(Q+Hju9^Z#1uBohGUWN_D_zw ziDYU?q;XQHl;V6&@7sBc8IirApHrLD@e0{E-XY%K$jbMIIT>{MK)ugQnhP11YI8s_?jGaxpcoPNG`eB#d$8Kqcf)bk9ss}k9Zp71zmZrX34Q_GNtCQ6w7hvs;r4dXRc8Q)(w zhAZ^~Y}<+L&QqSJ+x!r`KzmaP^+ll+?V|^(X*P^vE2fpPuKnQGD7&VE8{6EK6;Q@M zpcT$a%%F72{(JKLmRn|8Y3=Y$t@I4FakAWC{6AF_5inoQ2-<0$haO+ST0`0dW1Jhy zSNTC>5aKSLi>F?i@vJ*G6B{%DLF zv^Khh4W5}*6K~`AXF$E8oS_e=d))NDLef{qq7Q{3^qHWhvf~Sr@;@n_VU{%bfd}96 z-OR<{|GE750od~g|**=$0n@l>XwaLr6V|(!oyI~VI$FaO~FV{H18A7AMnC@p~5&U?U95w>UMC2ys-wq(nv=j@9zf&m{3 z^hIH^_3{e7|6h=)g}gMLfFnkh2HR|G$J>G7rEI{$DBp*oR+7SQyW(^5FXjJ+0qexw z;6^e>J>KwanC)q$xR0~b(^X!C%h-@=Gu87~)%pZvDKJK*VBNpn zbVah9;Dc?o;*Q%O*fQU2<?+de#yh{D6?RN6huiY_dE(m;*`>2u z3u(CRj|`pjLNjs^F~7ixLkDPuUH*C~(S6UfdrqxyeEmUc(6ZyOuXM~bQwHRX?AAh2<&j;0IwsFLvvZ0)M{DRsah6?VLc|ATUY+ov%n=)RO1~IXiYwNGp z`GsO^(uSycI543QdVc^3wA6ttMCQpH?ze@a$fhp zh_KAHaN+z({R=NL5yC;Zc0Vx}-wdbq53c-I7Z}LRq*AM=saw8OVYGBZ&EkD#fnHah zdu~HYIGN3&sYm!!VA`i0$z7MEV-OX;tNG~L4oA&{56AR)h3^cE*|xFCnnv4VN+l+? zxzsS9OT(WI3%%>h^whD~gxhNp_4taFAq~{S)b_NUfkJG({bS@RjkGmKoAdz1ac^ri zn#0qNbCRgo!G{9_KR8rn7!9}9i8L+Y(wYZU_Nm#VMssAr%DMBX>+@5@L>guv>p9M- z!H}vdkJqsGD{XU$H}FNJOl?@3@RN-oBafGmL#WfcbX)g72h$6z;O?^-4dm(G?}dyw zT1Y|m`sbQf<3T<;(l@yyCK`*u=@@Kb+|CQQ>FNSFy@Jm;X6TC18eiW0aHCecgwA=( z70>3WP-bh{^p70EsHjJ`J39_SJSAz?$7|oMlJ|r#(b_f3z6Fb>#QYD6Dgg4tPd-)H z5ofTjWW;C3xAGBSZ5ngqDir-R4W*-o{t5~^X_4#Z8S^h>S%bR>$cFw3N{w_qK1hnR zUDDk(IiOWZ|5KU%}z{ z+55{ZZ*$<@nC!Ycyn>!2az>tW;f#IsLg=7wr)fKSqXP_Fb1EM$g9CYF91eboKM-Mi z92I1apFpg;p|PWvI+_T(vq`8k#hMEfR>qI9(t~{K7~5D;#rNlaFTo$w{&*bs;F_iw=9OaxascxXJA*kx@EqnuvPA>alDAEorY^d8!e&x zCqy2%dF16)HTVLXLu_o)yYuJ{A*-#IeLmM5`=3qL7fmobeH2ZH65~p_nlq#FLd}eA zF^?AQ_tMJDhXW|rKFY1M@UBgGT=`$9@(W{ZY}%WwAJ6QJYYq=$Mn8ytnF5TDW=7et z<1~kK48!YhJ%?}97iqMQ27J(<-ES;;4|z`KH>!%Z%qI#BeS~$UGg1U2;r^<7+m1;f z$)2&%=MykEz|#EzE?ZHazf`*VYe9VG^#?yG?Umc0%X>z)ff4>fQr2p*1Jd!Nj1hKiYaS>g78rW87fubl zE^U2>Jl0v3*!;%{y5C=j72gK2am^l7q0asas~fwEjws@g@zGmPJgTxs_|vR?1y~ea z`!^w=w6Gu@60(3uNq2+b0!t~~pp-~A(nvQFOE(CDl$5mOvY<3b3W#*)x7PE3kB{&B z{;%(vy>@2KIdjgr@B4ShoS8W%=%{5!wJQ0;F8XVXU7>s;hvpiPP9#z3^z~H#lcw~F zaqdJHZ0#r;nLScUmz5weaopRX9{20wm1TegH}{v@$T((rmRj1GqU5jfZ85{e=9tD(rQB63(eMC3SKcOWOvW*$f60Vh55xqIS|C~mp+2%BS@JvJB z=UpFxmd_0Aww8|}xA(ECf`wjsfigLKDICJ$gcDB!5T@P-P}0JV%VK2Y>bS4huu8rX z_s8|N&5uq6BvbfU*uoR=r}~y}EZ?T)$+BYhmgkuB*PsMv-lfiO9bva4Hc3*O!_g}w zc~qfnyzbVh$ig4OeB=ZQg6*zKPCTBSzxUiayMH1iM1UGqc*Qj=U15_~TJ^@ln#bE? zAFR62$`Ac1{Z2k_KSK3fO~O8BmcN};?>)MAyWU^I&DP;0r5^r{c9Qj=x!Z1!CZh>_QuY|jUS$?Z)Z3^zpod{~&Ij;L zyU4Hw3=p$*`EXfR>hGKunsmfj-wZtSS&}rslIC@Jv_e7srpw7DhD#U+w>jS*i!9X+ zWr1rRTZhv9h7~7~?-l{yRo!%JM;&vt=3Wf93i7akeJEE=#mhW>F0d^c%+vUIzzvsw zrG_25QGw_|!g}b*P{BEktYx`*4f4#6M}+=ILo!32p3`^<&2|IJ(3~Sb_^NFH&$~|g z$890qW(6VBTf(+>=w)HUR>TyV?WYkMF6~5o9qpnjN$+L_C2rWz2v8QhaT`GOCwq5o zUH0i?GuJi{9ZA2a)hR;hm?dT%4oq@X>h0{KcPT!yz0AXGJ|~UF2iXzbqt~M+2(fbd zhk07kGHPxfBse-A?J$S3SnPgyzerZhGcKeoebiL~pDILsUWpk>bjP{QQn_8o zl`I~4D@cW{b}VDCPNDA8jSJlw!pO&`YOkr9&-ZbV;mO$+m24&h(s3jS9cir+xKtvs z_h#_!gyT%vY7mWEVl95?EFpd3YiWDv$xDRUg1M7?FuVRqlp9Bi+G1Py_LaSqF^(=A zRg`2ALpyL+xFnv@J6p`wx21Vq*&(k}JxY#PShWr{*INl(8bstpN}7*Rq%Jm#v(=V}TxgnI-LaKoC3SR_2c34QaT{P}`#=6miG{Y8}4Y8cy zbEW$^-l(6}^P;f>UG1thQF2yz@YHmWPM3jHS4dQ+xTSAvqUb{sMMg}n_e zGwYLB_bB3oROQ>TS>}h0p6NdSaJz>tyGS~X8G9~)BJ!z&$F^o!4)-aBCfAl7AvsTt z3^(=svjlDF(XDb(^PCT>v>ny=ry1$z^1v?$sC_%IJanCg4}d%XzOg4esI(>zC~R{b z^*9ETz{^05P&usT=H>ir^!AV~|1wO8chj4$l)D&~HOfM-UAR2LpB>%y=Wt1ja@RJ< zu!s_K5x_XA?x>b)C*M!M_-DWmjCKxKulFYR4-0YR0kG+35l;hi>a!Cl(M zS!#{($_hI8@=H6{^t)KXf*4I6Zizg6IzrZXB5yJPK1_D zcK<0ilP=F|3_nr~iCakfl9rr@9)WS%^oG3{%o$?lHv&<@=2MjLagRMPJgwI}K7XO? z8~-9CV>EbA&{e6JM*ji3{+-*9Z1ePtc{2Yf0o@^w(VNZ5qYcqZOl{xOx6it%rXZgOQX6Q2F^dlYT>^kmL87GSt zdl3DFH*trG4Rg3!w)XkmET%}VzC6XAsN3kQEFX21*aBm5nqt|{sE($q&*Hb{&ss#Q z+vBe%xeOLx3wvP``-!&XbD-{Z^aMXHA0uF03Z;-M#0H!w~8n`hZ(KfOkux2WMuml8LtP1n>?;VW~| z*X&LW6b4x#S7RI|We;SOgA>&~Uy6m;i>3o#gXtW^l9Vc{ovBsw)L`|pjHE`;7}hGL z4V1jbtBXLR)a+hZ=e>PLGKEK3Tj4XF5}wh!EckJ2NBvoDZFj~NRzoZy+k!ze|ksH0HHYL#A63?ulR zN_*mm0neteIlsAX9VwH?L$}2;bz7w}!R1(fJsd6%s{J~ap1;kQ?u!c3whVXjVRjLb zzV;sdg<^rR2~%@t=QtXXZm=T0yB$lwEC<;zT9%$yViOk}pOwR#?S8&O>3WY#5DQ_?mUz0yna)Kt(psX`m8{VXjRrVDNe6tY*j;Mv>B^-{|hkn*e`)^}LT zsBqs*QOsoB=w71p=X<9kgZ?DdK$-VGxcN%g)=UNX$`=KZ6PvQ&o&f8m zYZmXP4PgXfb5oxS2uY=J#-eiCr?;`jTylg>btzWGKu5`)O~#igetd7H8ZFhTU_>F-?eN=XeB@^OvrSQv1xBy2D)V zkbjEQa%FxgwxW2wVSjY!ff>b?>=3$b)6P-&dqTV~kx42q#H`u{KW##51g01b+^;Hi zr=><dxl<>zWP)lqUp3cf8S~??u)m_9wP4-QjqMD|{6a3 zZQ{KSis!qgc|Z3ywX~vhDVR=wsKU@!V@=$bspai0woFR&QhOmaVS4DCZA|3N}*_OFrBI07_S1+ig)#7D4tYa$__8RdICQ4v_(x8!;)n& zwc+ikLKzMAE`L7~?X`sN6-H7sa>I95@>g->PWsFFG4N&vDK=pD>L)Teqrx=9#VX_7 z7292#NVy7*gRJxeiUUk72I4RukVSFGDDzl#ZR3UHGS;R)xW6L1j`!&P{&*H+w`HLP zBP$WR10c-sN#nw~`M!|nz!~Xj0F6}p3t7QOd?uYA81kuWU#;J`V{9wSw{8E_UI9L> zq_T`TdbFhiHCP&lPptMiO@|T#3|gGE{4vJt9)XH9U$D$gF}rMhmWrM(iGN=3{f@Y5If?#H%wej1M4MjzeR47L8z{*qnZnmXen4xW&FZ z+6O^4JyoCr0s**L0XX~Iv&b}s5>PqhfTB{;RH)S>H8DIoHJ<%fA>|uEtZ58lY5~C`7 zC%L*_UT3s3S?9xhrz9?;luPYlrkrKf1%0;M-$Xl4Gv+BmuB^66znjJ6C?9T>hEEKV zWwe(_@y%qoBcl}RbIK6Qt_k5j9?;KLxa*E0kNS3f!VGD>>M$1>U!v>@-cuRsz@L3V@Yj?peaJcB{@{w!k`jw% z*OazU6A^s+PyX7<=@*2=OH;j4QhpZF zDIJ#ZHu+7aOZV-hOqJ#^ekPKf^J2VM+UGrTxbdpdl=i|en+ z`9|zhi$>ONzz7O`o=!!$sWwCu*==;ZM^~|uv6B_xad^7oYnc`Iw0=?n{;u@>XJ{a+ zKsB!A=mG+h&w;X8Ky{V7LvoV(q!W`%sWZtfd6IgIHyf(+(Yp#iw*&h&o<* z7BdbjeME^ty0X2P1^e(ulo}U@j7C9v2PYjZ(E$uF8mYN0EBdydmibXOnS)r(6EJ)- zP6?9;Vwh68zBlRUq&?n~Hu#c-Z0|tp*>8{uNL^%Axrt9lva5x+%{>e`nPkU(dKS_! zOlVi=0>QQQ)DCw^xmKuh`htK);lr<92Ps&E-t&denf9#k;e!4C?2yr&JfuRm#T~lH zqH-CP`C67*YRx;>h4PdGrRgK`i|*N{BfILr-@0%0cZ24IB-D-)lbLdd%e_`H>3^qK zZB;CbY-dNuRI{I#@mHQ1r%ypp=a)mXkSORoUuxGwXqBua@tLl8u-w7lKcB@iiUh@-R-i zN@9{txRHRr|H{*jWXM!wqAD^pfS>ENCMdN<$vaei_$+10rW_@_Z0EO5OUGIGJQU~mz0v|8=c zSpjr$yYj=8js|=J04laM??fe#y@3(%%vrBpNB50#iH%x}*L-d-B|Cf^`3N;yP-d2_ zUg7i0o1=3$lJ?J0D=E@KpCx*TsfH-E>t>^m>%TUW zlO-#82oI3%ar#|dNpcbE39F2q+)h`On1V0l6sF1PXN#B4={rWOt*r4rTuk1bJxp7% zFb*cIniUhnwoE4<_9TmeJ5F4d$&tZR5Ir%jpe`amN}l5#`k2NFoh=O?UOT4N+brgs zanmmm*Wq9LF;#xiiO6Npw6hmW598+av+w~G;J2wnxX9i!;)W42I*4&><0b~K|1v`h0( zI4AT+JXc)O-@{})ldVmLd+Z7~2@|7BvJ)XS@U?PCD<|r&vJ4wZMb<0s zggl+4ROj}^fe8;UjdHfVwnpD@0qAjQx}P)4adnu&Pjt+Sso9i1K%)Tx@WD67eXQ{4WCGz(X5`DEk?=1oay%4U9rRkCFo96NWxPwh1!B> z8MV9i&Z8KFL;Bhh*_C|miurR&=IEGF_+pj}Q!IQd^sJ=z^ieytPiL%`Z+ePmd*_6v z_GNKzBg7IMQmfv7cNMvz^!>OM5Y2n+X@ZYE?~6sVV0!x51WElpecYA6U!6?j9S?9M<_DTa~`83dz+uAT;L}TRct3XH6%Cd^$BY z)3}#9+4CXGGv*SV_j6I^I8-K0ehWggY6`5oLOg^9j8k1-O3lWvF{}cCT{4GM zn)2i3H@wGK4fXb2$tS=tIisLu0INzb&7;V%qek4bF-{^<{KwIA11@4Y(*4nWewOsS zwq@yHk#`C9x4XR5SWb{06n;*gp9>w991bVBy%EByODo0@J8tS*iDl@&a_81!=I~c zjMJf5V={43{6($aj}q*V#-ztq&-YNu2)DBc(cISD8r_&V9G)4NGgqSFm5)M^iT$0= z&1efUq&tRGqIcK@#AUSmt6Vo4F;9_Z_cGYnQVnLsnG#e+6mF6`zlmd`rKiv7RFPZz zMC3qV?kodeK1?S82kqHQc~8*ZJN+!>aAOhkj8-s2B?*aCoJ2HUSt>qJB344BueAbq zsrfqkdd~=B!qJl%aOXXUfz;hvLKJHx!42gPks`0(e-W*m^e?*J^K^Y4WexKtw-fxv zB(QyRnF&ejlY6^waTR6TbZMq25y%OZmJFuqSW4!9^!#>q99IR){r0IqzBM9WVp?9Y z{TpcLjPR)@;-ve+ak%ZOVRDI%y!Nu1w@U)MMdJzKgCCFYTZ9)FLRY~YW;>NT#igyU zW)@%O(g=0hm%CRFu@svpEK5ybt`Rb!Z-=1yP$JrHm?kS}NWYSC)%~lmpd1`hwf(4y zX9=?9X-kM~$YSNm$d8f@#i}eOm|Es>;6Z(-MN3R987!15SYH{w#Nu=NA!Z^8ah>tP zg-jSyx})P;r~0Z_)m*I8h`F4$1!f=l)A{RcA^JLiSXsf`bVHO+ZVt#4sGlJjTF`P? zYl(uVc}0$R-9o1Ci7ez7>a`q5pC=~TF3!EkHx*i<(8+>{4nJgh{_+r7WciNiv?;qa z{fPo?R;+ZUh$doXZ$#cJqCq{5Y%=W9y+Zru%?$Cl#z60cDNS^;iu6{A)wq{f-nsDM z+zPBq!%2GniEHOkoZ(sEZ zb#LqyD(McfEVbWKJCd$)4+cy6HS0}(U})c9*udcmvCIjuFF)wnUM96!Mvk>!2!0`% z?0eg>qjLTMC$xtfS-1;fJLUaLTKfISs)u+ze%g(__)#N?;NWe!$yM@>x2L8>{_SN^ zh=q*y80stwNmx-LY<(~7^YnQM#xwS`qM4f2a=Md{-{6!ibn9V0=D+J&tfEulLf>R` zpHHd!BgyiMqj*}a7FPJ`l2GupSSe)^cK|aCnVkDQqXEz347nit@|8$=W$~Rh<$LIP zl_pVXhEk&eFDhg*G*Wv}i4=t<8w!M8iZlsCA!CE`07W!B@ssJqB)98MJY+OkTTB(9 zEQu^F<7+w}jd@i-%%c@;^pyhKHndWXBC{?#qNcHY(5(5nRB;-ETS3UQo6vo$ zN52-ctuP!}U()wTGpK|ccF$sQwSPHyxh!pd=Vp_o9b$vZ_3&(Tt;b!P{pYH%hnoTX zpGwJY=e_2(Vxu91d0G&@zhc&b3uDD%8^Q)`Bjdo;C!&XlrC(5f6KFa0=dPzJ(2_JD@n-Wgb>!c+ooyU-(uKK}Ic- z(M1gl5Bb1mf9X|ekD?_q)Lnr{;dM;n*jzY#&SB`of%hN0;>>lLZYx$>KkczV)gYWZ zN|7W-jK2n72j*q@=$@ou(@3>7AAAndKymWA#v-vw!=T=Jh8C;}Xp_Brn0kXsnHzwV>b4&K#98JC-mjk6gWiX8 zmckq{-z+_2)43+Vmlj%P3_j+x;3dX8#aN#AF-ekuP|EaCN+~VvWN+IOe3ValKdI({ zi(xsw1b=ihha;QF7?&F8B2q^{kpNLWjv$k!WwRlgWupmyB{ zV;1-*$hh^`VF^{cf~!N@woL-HZR_}+c@7pT)E{N{6OP_7W0G;g=wN@SVES5W*O$r< zM8eWfH#`@zLsGKLO4KVOExj87Ddu#f(ar-bxB(2Wxv?~w#qlcQJ?n9kmyR%QYWrmM zz0>BtH(lXs|ELt~*UEdn+; zOiTRCi2<0@@ku45sySV34?=B5fyGk2rVpWs2O6~Ywx10;0Sl*=J zvS1!Pafqk}bYOZzcW_%uA+T!qq{j@Z*<~p-`k)4!_J%p|&J~zK93xlN>*cKLVJUug zH@vzb5va2*g0wL*a?qF>#Ix&cUiUm&&_Z9F5sqP}1tR1e0i8$ z_^OxVhFQRXe_HzoOzcnYet~AKP9C&+Ymu+7%u?FqL=`!xhoW6)_TM~tDGFkxe5X{@ zI83f2wc~?&N)qxwo{v3es^1}kIJW#mMN`y~6Pj9~oi6?|4QRM`Kcm}c?^w$+hkna3 zb^xO+rUfDq_muE`I`cc?fGp<4Ahl2RSZSd+>%a;o#9R5UI4_q|#DNPD*IKtL86ACI<&1%0%r8=rMlU*yGnDhX$nJwwwuwu6q2VPZ!4>s|A#_W&% zStJ@x-pI>OkOvROauT-MaZ3HvgB8lWMbB7eI(XObd~DV+e-cuM8Qd-%(kBqRCc^2! z8k;Ung5h%_qUsapcI$)Aqd^i@nGyKcbcSzS58=Z{@YSX|6q73$nk?r(o>9HzMPaR0 zTvtv#WabQ@pnsmv~(w&45w@o%dGwi>FWx%EhHlVA_$C=%`3aNQ;Dpv|kL zQs+deYRF>@V4n{@r|lUKJ2 zZgADPsx98AU1k07gmf#@*XpH}Gx|fxQQcl;MZfEqTOamhH=m-Xpf*pXy^A>o3A%uZH+eFwwRkPSQ5G7VE;@nB0cpt-xt=njoz zmm4c{MML59+#7Fmgu2mEgf4G78KRrZ=Lu#mAF|X5PX`|wxrx3gDhe`6p5j~a0;M{0QFnN@ z8+5$b05E$NC&`*r`+g{DsTHAko=vvGEu2)|sN2oC1(tQ3Wc$Y2TQ7oSh?Xg%As~qmEe@ugWAjNK;UMyUZ=n zSPEZoX?o(|j;sS)kzI5`aV00CnNT#BHT<)}>s5;}JcMp+Q#vl_V>secQI$Tt0dEIT zi2xs>(rBMol^xUT(LPytEjTdTVY{L!$<4y)Zjt`C@tS(RvOn5cIlK! z6CzuSUiY?Sm$ZdiJ37hJrt_gY#^rW!jg$Ib-vj(52<4*whcaHvz~P98Mz+r$ECyh?+2~nwQNEa(S<~4Gef0{pEez*ZLec zgKtG+1{`O$?A<-y%`&OV;mM%P%#(URj2iyx@X?q_k3WxBC{C}Up4Xzu?YzcnjFvWWpbGg9zWB;N#Zg+3=&d7T@TP% z${&3|HFoS^=0hB-+Qneapr6tHMr({b9Fl*sU@X;nSZz)D!SwYFD^%m$ml7w9(@fOq zQ>$)JTUBvYOH)89YLk+;)W%-6#bcXKn;=#D2jUps<@uF<3g~?qab5SYoF0}bc)bzD zdFUw#w70;hJ4E*q5S*v*BO=xF5l}I=8c-#LN+rpJFJV2)GJAX{K7ufn^66u!Fm85> z*Vb@L+|v(5Nsl@W2a=;Q?NIOB=ou+6G?d0NS?GJFt}4n2RKy%&hN)^`#qPJfX_U2$ zAe(&eLCuRt0>v93vf76hqe2rsOHbLfVHoR?N0A!AmdVEV9k(MYAB{q82&Jncn1FPjWRW92@gKqm}1o>R5EU zJs2tMl)=na$m2GRHU|jVp|=7*6A(dluZKy3EzIU9=rLEEmsPVlI51CS!J#b6EatZ~ z7)%Woz@yLyRTTtxU!Q9ZX;@mf%GRT-b+So`?utlTRor}n7jn>CIzBn$98mK3H3+hU zv|K0d&7JDLqe70l5-zf%sSy!{O_x!?J0lk=^+8V?b?ZwA`F z^Kf@RsAa=#wlXs6(+q)c1n1wSuq*e_UW96v z8nf&PUcmqYSlm_6di}Ww1nX&uV@G+6(5-wRFDNQP^fY0CkBTV8n?H+nGu-?{ws^jU zag}t={W?l z<#BME@wd(MfgQMD=)G-NDex>i18IfQkEq*c%DF`_6BOY-{Vd>2XVZet;rvjh(tFt$ zlZa?KPlMGX=gZ5S9X&&mCRd(@`@N_dDyU&=EgOODMB6Bf8gIi${GKE~vv7JP`5Gpy z6ZZi=lLlL>WdFq63fqq2QYOwr!D@F_P9qBTLVf$7&k8+RG^i!($!^{Ryw&330yB!F z=6=N#(m9NypKrG5b~)=+-?H)U-7co~d0-Q?&gYj%OJ%b_S(=ofXUrJ%fPQ5TF#Ni@ z;i~g3O+QaNI$F)}wEXsw{1U-;(#R<)x|C@!X7f5R<{k@1K7VrO*8)wu_bR$2sEljq zu4WqFl4U<++`Wn+Riw0ujv0@ih(LWrMvLd=e@d_SvmHHfcYV!k&(NRSg^wZ zt=lZ@r&mqx@boL2m|P3|Tskjbr5&>SAxUbD^~l=hn9^JAYJ${U^3=3gUh9a%Y;@tg zS38x*i_Ge~T16Z_?wUyDw=+&x21&8{Nalt;eNoF|Abh8YdeYfF;%R8N0z!|da=3gX zC;Y^?fVW<5qL2;sMvYQ;7>llS?(U@qP=73M$d2>U&-8J}iZV)6-3I4v3_0hZPLU)^ zaZrukQhcI`<9WBya9@Vt!|4E3TMGO=#{m^Stql`XBRthgMnhv2Hr6FaUVzm@U;QVJ zVFHmR8q}o00v-nKm=+k@+beO16G~kq5|)|CSx-Gh@>u#hZo?F=hy;2N8B5Ds&mlIoi_8ml1%Si81#C^Hl9y+?ZSA^W#CbL#E zJ|sE~rZ7_*@OU>{EzLllHn@BE9Mb|le{UzIt5aH2yiu$oq-LiYT5V3$k+Bn@*HL54 z*;2Oks1Wjr+N#C9jthp#Y8Y^3Tbu;vT5xplqh4g6&t#{m9(HpmbK#XX*L{dqhcH!< zpyWg!?Xayk)i8-t-&Z`98?)5J=UJO)WG}vgQ_74(bQ>4`Harz)9GN5=*F0G`0h=A7A@^Cr|nczX=!ZUJD>W80b*zgv6uf5jy>K{&h z$!3JPUj?>%_>%>7!AvwyxEJp1t7t0ziBbad{K(0;Ko|M2Egq5*H zmRKkAQ33aa&LwHEfY!w_Tn8&6cXe^}ALjc?2p%s_p1fs_CH41(PZ9xt3n5h834eI6 zN@wV;Kbg{DXaR||D<}9d9_MKD9tV8Tj`x$P_3X;+Bug~PE3k?|)ZqZ$7AP9?+f)W+ zxi;dCrOcvy+JGuG~)nyL9$MIO#f)HRm)-IViTbl{P&k4WAb00DeVbTeS`EoiNg-5cS= zTDZRqRP6=agNg`Gc>#|#(8E8x#RW}@fH$aeSA~p`;Oh`6p10)q5}b4b51*(UoDaHx z3BHQI2+d=DmbrIsC>9)r06eY+9^s-0qoaqf=mF=SA o?hrBxIRTF;KPsZTHhPLs zQO>nW;*3iYY>NWmVmgA1L%l%IblEG4@Frb7lSI%xml06-Spi1}z|)xP;>SxjuT6X9 z&WVD`&j^qp2X8_01eD{Vp#6_G1NMyor*0_1OSO_hEwM<5=aAlPNt`!lC`fsf{3n@5 z_W-y;(z-j$hzN%f;Q7;2bYvDU@L2-zYzPtITzY~5_ZlL?S?;`I-kv9(pD^s$<+}Lz zi3^IbGYa3P$|3%7Laf$CZ2S%IjG z#8uotYAReWS^`NCmEBqNU?BvfkSL0#>|l)?6rmIF(Ez9saDoVG0vw`yqJUQAJWfFs zkd>i>GgZ(c;4C%w2u1i=?sl+e5%>^AcrgFO1yW;>E$s%RcJH1A0}qF~JO<=*z`&R; zV|GY27zp1b{ya~Vm&3dBrjhNi;f@cy;u2cVvdmw#>iLaSMT%jcJidfr{2`a}IcJJ2QiHyM{J_|{f7-V6EM_V09G zX!@^`%K5^H2f%+Pqjd{{^Nr9m*EUs(16t4yC zL9Fb?LD0S%dm^Y0-s&;t?|XCH=_$A%a6kpz_?u?ITVMONefSSf!_xlhf9}$6Xro5I z#OJ>Xe?Q>7nd7E}=k^T>zheo!)OpQ}*nU$1TEjK`ZQ$X*bLWQ;R<>~e?)ATd;>@(O zAlePE1xfq|LBF8LT=6?mFMfzt*zlLms(eRI?z+lv5%eM|U)Ucm)t{NiwD2Pc~C{N$_fd*?fUR@bjr>>Is;Tn0o?%t4pi z-y{gn{HB`ZUqw&8kpteo+~-TR9)k`V;~P-6au@%gK$n$kHMjXv6@RGxf6F=+c7Pkl zzku@nAa`x(CvsxCJexkn{1=e@n@K+{oH;v$_@9XSnUZ`#DUafRuVGnR5Q@L8E2Q_F z=0yF^(ChjKEbsnQA-}&#g6l$6^nb;t^INB1ZP*V}_FwEgPaC5Tgzf}vdFQ>&@qmiz zzCNjzrLhTZ=OvBelBM0Ky>?<0B})tc`BHOg6BEaHDH^ljIiUz3r6Ph5a=pM!z>k0p zbSK*5p@ZY0KW`_Vv6oTux6%D=Pu+(qVi8+d`tOl1oFT1GXGD4KR!?Utup^>;-8iDUugKwGU1e|{0rgN zFP!)*&}IL#Zr=NEbn~lz3kzLTvD1c2XEpqD|L{vW;C+j~gqyoM@F%`?xsS@j9qhl# zA$LpXZ`bk{X}|iRKPqI(3D*Tbzh?&LEhOb~pYFf^+hiUA{>c1)LRr}U%<*y{GoF9h zmVWE5%U>+Ha3K4wAD~a8a2s?a{VQ2N0;bc8l7cjR?H*|C0>{p7TsZ0SnEK0}{q7+D zGUbhuo>$&*0tsOG#f7vZmWk&PjJq8qOPp7aD6c1?)oIFm(-U#KW{) zBn8A$*+4|OA*Qpre{X9sCo12!GjdSd<&CBTPiXM)eEkmT7z^3>gpd^ne)~Xn;KH`R4o9&u{BPsJwtKwZ>gDPOLG#G< z#=7MO7QO#pd4Zo!Aspw;`fyl-U=M+Ej)1yzuNWl)g^`e)F!((_ZiIh&B}M<3@%&tm zopa-3*4xc5Kl?(fDS38=yBEU{Q&`J~+OoGJI6Q^uQ`KXKaA`#%7p_q#e*M!2vF0X# zdw`38ZFFZpf#wvUZz;+r&8hH%>_d1`s{XyNdBUOK?ZQLgLHrhYRj9ySK_B`m|nC z4ta$BYA3ZA7RsA<4-X$4rap9$)_L}ZLP-X=0N4t4mK4ZHRRS^O#)97c$L*7F!O5AY z-=FW(55fD5U&CAA8oKMV)Q>q{sq1Y~6evh;E^JePb@ZS79yyA>!1mMd0tn?L0p#U6 zf$M-%fG}tTj<|o`x$kCA8>RAmaeo@+QwQ+w!LuxRZB^ek-lM!5 ziI~))+hmXD0Gq*1Z2|`0l-aNyI7e`h|8qN5|4XyM@SKY!#R394wwUJJ3^Q}nce&qF zy!sRY_4{%fwTI+F!JmDf4B?ggjH54wK4OTBL?kV8@I&H8_%w<~W8>_bk z6KFbgI>Y15L(B|s3R==qKQ45;ndjy!;FIU%$1g9TQ#lWG4|H4j z9okP*()fI%?$-$YqG#fN2G@>{xpoq3xWouYwONPeb6Nb0sLpqF`kLyU)ilF-JCage zXfgzC{1(!-(r@$J57`rd3Z}MJ*-K@s-K-GC36 zs0w@JF3WDB@XtbU!Bc>JKfxK06S!tqm@(|6Ku*v)4A)gx>$oSk46%h?23;()9deH( z^ys9~Ubo3p;>;jwF-#jMg!c5!WQuEj!>2VN=rpc0w$;qw5JnxXqi@(o^g-KS4E)JR zI$`TW_+ZCZ*vASLczlg=^N*Cj!U3Y;m?5-Hn^TY%-UV{QT|Yp0=UgeZ9c<&QcUnaL zbOyf}_yyfbQs8Flbx1yFD0cfqX^)f|KMd*Dq72~p~r^Y z#(_sc9;uzY&8hS^V^46=)c-s_`bBVGh3MobAO^Wr7tW$g_GGhxB~@tShia~4epTkr zw|*E;kHMb`y&$w8RABp;K?P1+*#wbe;j|a*^JheWYk`x1wO~g{cfi7b8u0NrzZfq3 z>hVtkhWF1~PdtP@Hz`vNr@Iq@Fy+RdyYnA%x8Z@6unaN&x-g|z=Y_3Yjs5ft|C;d~BwXW>T#{Ug?tJ*FhS7=-?$88nsucf0@p zj&?CGMBi{KE{}fw10O#K{{Pgz$Mz5Eg!RdOGe=n|AMeQipwqvF)n&r}QxQ+MGtfn^_Tw(;7klIJL!*AR zZ{L{pi<11~^vgw-{(8rEruirIpK%;e?s)v^p^MQWc<$m8ncs=|_-~QG=feF{d;c5n ze${Tslb+=0F1Pw$nvKgBT@o{7ed`Z*m;WoCV78E-`t_~N_rE|| z=yxptKc|6e;UlGgyn6reqJ_I4&CV`2V)6T4QTvrW7lHe)F#Vo!T=R?X$XXEk22`Hp+z#O8 zw+8lq^)GOQFMlm`itaJ^qkiGOOQOtOvrPm|U%>TgD`aQ)V!tlreZ@Rzn}2T_{Zbv@ zWh3lN|DC-59bT8NII{hNtxEYlkS^=>KRC(vRg%KOUj_a&=HP1;pmDgZzHC?_HE=|D zSY2x1f+<_(1%-ikewtN?$FHKi4^L=?KPDCW=Fqn7zczWvkbFD+UcnMPPiPH1^ZPPC zt`k~=4@3WK?;?l(>Ncdu)8zE!zabM+cMJZ=GU=Ny|7S-o^akF>e+tg`au>hZy9-(< z3x6nVe3uyH|MeNdam;U2XyR+{A?bf4^6!ob>;I;TuNBmNRn)KaJAUy1TxRJ$xh^Gv zEhOn1F?)eOGk>|$xF0?9T|NG4;OOVWo8KWVcVFuQnZKZ4KKDz*=kE}@Oqa031zGL% zF#mfMm#_Pq0snuFgfo7lnt#Pue*_(9^eY3;xGo{v4LD1erhPFrpxU@`rj;j+vRp2JogPL1($^Xv-IB<nrzGm zS)D?Cubt!R^Yb+|oGWOksPw45S8&$P0avb|UO~8mihAV=e1`x)QbF`bKtMpbg7|?X zRRU2eXjz0*5dm3*lY}@wr;Ds9;RPnWd?Gt*0{sgk1HG$yxKU9NAzlb(2%HG;Qdj7R zUYPq5NriGEn6P`glIc6Nce99}-j;IM=?b_#=8 zcD7I|`z<-XhvrCy7S8lH1D{3kAxkRS`KOL#SF-V;R`_X>C>AJYX8{@HRz@;bm(nZr zveIGnq1d$MR|8|SvIDd1_->l#TB`WnCU6+g)pXf$Lc>#3^8#jZDlaJ@-VYEbpi@-k zL= zr4Z3nmUIshR21rNc9(G8b#;0q$2IwSB=`RXB?8+0MFmmkao=ur>*311B{46k4I@!8 z6gmx4wt`C5lB%y}Iz32f^73(XTNSR|!$?sO?T2jdYi~2;)cIhjhKebt$nrd|98i0H zyS1y<(Ghml?DM%RZt-21+<#8p?d9p2^3QACm$fXX0- z3`vM&$aevsf$>qntNm}+D#6+#IaYKacVWRYvSGSZz4MN>`Y{12oec8 z9jNnb7mQe{G!bV+qXI%I$;hQpjH@8yQH5UNIzVPgOs zxlf6_1Cum^BnTjckOZS8lc$l~>TzPopApdfH}EZSTm48 z1cC}Eav+JVnkEu4C6EOy5kvDq$d-KlF_?ic1W*-9GZKp)&C{5GW%R`D$y(B-DwRfU z?i5${Ss1|s_mmfO<#EK_Sy`!!^|dv9cD&6yLQd|r+~c=>QVUVeoU^#S%_*mjLu8)I zO6u9i)|D-yZG9M?!JKp1wn@I*-&bJ{E)R?BH$;~JhPthjwWm4ZfiB7gCB MBvXY62azEL;NTR)+PsRn-YhF-RVrt3}x?aw~9FRAPAC(l1NZh z>{iLNrge^KO_>&Kl<+L`{wGbwVt)^b+7wL$F$e8 zu2|)K@t6I7bW3**aZ*reMoLPD5?TkVZE8Mq@0HB>RNc zWu9ay+Es1tbX6chvaHzr#!C5+m#TPJ^77@!XxA6j$sc!Js&}mNqp#HN@Ulr-DoK0F zYFC)H<-6nLA*mv+Gj>h283mdu>+x;S?!W+J+e3MICw3RD)w$e+u7w9pPT#sU3-4G4 zQMrtbS~!orA%R=XZz(C#`7MV@6(;5GayB*f&t;EWDW%KS)U2+JdzA9ktyW68bgM47 ze17X7C3J4-YH?%k!^I z@>RD|{3apT6tBq^OhC@PTZP2`EPJ3+OefP8w?ZgV@m4s+YIW-&Wp`?0)D8*Wyjsvy zr1^Hz_ts9#BnuNzes7~tbGD$VP~#C?{&A~`vdi7rO>E^2SYdX~OqM6?I8KHp?N}6S zmuYk+J>pwg_qIzlQc9X+tA&!3K@i24Z*V_Q!-Ctp*gGFq>r&GuThKji+P_aGCA5A@ zX(@JvlYoTtfLm=yEZ^{KOZ{!xv)%pO#`R#2;Y|IFzrQUPZPme78~QVj8AY4_nf7qG z3$t^rfIz|go|U$U_&a`}1(h!f@BeUn*SzSO&; zTWR3#6dH7S@k*KNWOiGm_(qve0($F%ox0q8W@D3*_VY_>vNqZ7YMB75jk7Ct-Mvx3 zO4eSmgS+fvCcCO%>eK8dcJBn47Pro+H_9Kn8uIwb4NfgQ$r`U?=X}8~mwAk*r7YV7 zPqaRNJOOLs%^X}d>69?2HRqR81Ftrhi(XgS)&#o7?v&J~HEc+m(uRMmlJF8oBes3v z^+>^lAcB$6HjzmxyCp@}XIxiVz^PLyhysEn^5wRi;)~w-!PMf`#Gl(6cB!L1O$wD8 z@`K=%C8V`8B;-5-PN_{LHS9>Sf`3V+s@k)r3v<)a=sawFk& zaqowhFUW75j@+XpZwa*_3uK$py3SYnx*xV%YtHw9zQg^S$61hGcK6R5e9XGi^XFXl z2fcRwzk6QHnH}x=6aBh(;V|N*RIs*bqrEEdU2y*n8Q5BIzw@QlV!ZDMqr_3qKELmr z{fW=#${!PIYVaG^;k4(kl}8Q#J@I>Q#$luH-2DV;8=-As{~9G`7JOjE_f*X_d|+XB zzBgsPz{PIZ1QVNk%kNKKp*7DL9o>E5PVdY34tJ%iZ(|0&o1x$G9gU*(uygiT2k(4l zI{R-re8+rJ+=jdSW5!X`3!GP`(NUupxTK9LIPRaLSNp79?ta)PfbWJ*hGGQBOz;O@ zyu9?j_}`k2Ik%&;|HcKSJYKF2RtE|G-KS$idx?HIw|R6H5TC3vwvDlPc__qtX#0?l zw=DLKv9TiTj;XOC?GC}1SjeY;ZgrCCfVVQW^G1i?(J?kJwpL`f*A7gfMXUt?v8?@&v>c$aKdDIut`q|8$pJ1Z zy9UHSvFeaLS=JAT!0ichJ|hS)rWMMD4#jz^Mf3J;=o{Ba;GX=Dm0Rm+&Vb4Sj+CA=k<=?a!{V!I~HHr_H`E!$awv zB0WbAk~#@}3Ir?2smc1f&F`7U(NmKiy?w^fxXF4-c<_wh2c=&VK7igX(rI$JN~dXm zFx_h+wgF8Ze{P;<8bzo6Ly>VzFPczSoHmL3ha!_*T7!8iGTNx3;(o|AawPqUdDegL zn00jOKNUG_B4zVOKugGb=YJ~x!{$Eg&pu`v4gV7mXyoYrJ|ZpG*wOMm?jOSQOe-KQ z|GhsQpjJ;Rsg8#40rN?x=dtXj`i0xAl^p+iB)erg0Wv|(cI8q zLo3y1`yQVndrRn*nrrXuE2(D}O#O&ObAv#MdbTO4jNRgcxd|vy-tn_M+gEgo;w`@c z9BJ3zxm7x!b@Z(i4ZP!HIYF+7krZ4-HyEhDU2@@V|@6?xmYNaYv)tFh>lOozZld&@t za!q&Fj8rJkE}A|=&oHwbl7;nKN65(XJ7zQlnk>IqqW-$nK zSx>y~pxJ4JL+R-5gH;5&q$gIl#f%f7NmUBr>mC(5MLRo3^1#LAJ!oCJ8Iu*}oFO=p z1^#+CGUehj*m*e2xxv4z2cz3%rio~Duh=jvWAPywyd#O=!15jx0NF$8GR!(yVJ;a9 zN1lMOVY>7pGlz;zfNbbCkO#_ivm=0jY2zWjbAxr^wEoayO8++EoQH4+OdVEhk2Hf* zJu;S$P=HgIYsS)%N`QRYZ#a^m%P89E0Z7^8>ElC-$q^(6Ma`$%0sXQvxpw;YnG}}5 zEn8Z;%L<2PFO{lT0@o^uOE7CG*=HPEOjhi=NXr#FE;V9hHrAOX(Gw-WvQ_3>ESo%I zG_neGxdgt@-jn&2bG zY6=M^#p6nIh)DT4fB_;8k@^gS^(oEKp1xT1ttt z0}Q|bEfNltPKX=b8Tl?9EX+*+Pq^!nHx#vp0FKuC2u@6)aKPMG1PzOG|CDVAAJ!46 z&oT;JI*0Jx9Bo)01WPL_F~*V|#D}oknJ%G)icA-GCZ5jGhUfWXON)(I?Knq9wTlmv z30#sXfr+V(jM~;6acSC6mvRaDJoA$HK%I$m6Cb3Hz1{;cp25vPX^R^?z%x-dcQ<4PyyiRV!-HhS zKvou;jsjx-^Z}Mh!~kG$&3xEif_+DIdMNK8#TLf`vdYr^TyH8i9jQ6Jk_)nm!FU9$d8@DmWM#2tiyU?R}^O!GGg}T-%`{pcz_XFGKZIo6w@eMW@kdAQBB9 zFg2x8`9b>Iza4b9lVN(=O=!sU0(}}>R(CMY4vp>UGQqWW4;+*R@)r(RnX0Mvh+tX$ z!4Ny#)B%X;e#yrU9iN`BUkc|74j7r@sNHlx89+a2;!NGwwt_IF#inQLLq8#aY)@v& zDu7j<)emX;TK#s}M4Fmx9#S0TMQ-_F?D=lbSC9f+>p9jF8fsws3AOdj;E51BJa;TL zDXQ5PgS}Vc#evTWzcMWlalW!DP*dmAyjT~!v@P=lug;b?j1%s5a?CxF7jX~tzjbcQ zn}O%zXD6gVbHXul1W;9=@8ttMjaDJ-$O3=VVy{|!(azM1regJJPVxLfJ7R6ZgD@JCk^_>3qFpS2hS~k6yJ9|}NsBF_%9C(1mSZDR{dvd4?R^8HlU(>>r(puyg&^CAYb zJ#GIl7oxzqeg3%@EqG4y6{-N&K40PG-u7nZ1Q*aSU;_+ltO}f4`hSV`KY(J!-lu@I z$K1MC4Mg(c$0@4Ap8o3A*Wy6IA`m&Z^^QKCrp;B@z=MlMZ9>|5r;iR((20G%{+fi$ z1{-3qd<;n777wHRaqYb`N4XSPVrGjCZ7**XERY^f@{dpghPqeW?*vHO0Y3M(*V#ex z5g;eKm01TIdp|sG?dY96N}-@{-Slk%qRvmH&bGN1HiY*5BQV#w9Vqz*3sw$?0vc}C zb?y@nt?!TS3j!vsm3H;=9&dK`&K_N*ppyWZr2zbX*MFrIscbmG@|mMriYzI!%VxXh zzmX2j}|AnKe=ZZpEv;Y*$zE8^m81M5aL)xRP|8mc@*nrdeu>R1b%t0G?h+yqq!`yup zMTXC0*$_hnio*r|RN7qYUTJRtG-+eCs?KUsP3spsN`n>Lrj(vzEt8t0+({%YM7VLR zZcf83Ewkz;dnkRffN=SU)4 zI9FUi!C-O;$kHGo=UCT-x=ECd)VB0Lb>!AyZsyt`;Q@+HM{K(VwI z&)r%kcNTN`Z*;Al)b)ZS5PWLNI!-;6MT+Hjrf+1SV#C_g#kyn=q07JkJy0 zL(rUV486|Dfm8SbG!+}xoIb4^0V>=J#&vx_&aq*Q0xwW!WBP|I>)5b{^Z{KGt`?KKo!`4kez~4AeJGB{2K77uLZ|J&N1q@{iiwI*XV9eN+bvu!><>B zK;Sr?Y=iPCS(X9Ri59MtZ{Y2u`Q#j?W!VO4Q-JZE>PFCwoHSwA$+1HX1~pFWT~Bum zW>Ng6r`rZh)XFO-a-gSIW=}XT{gIcTEu$|2H|3tlJu)>C2>oM zUM)d|vCKbgXXBP`c~t~aW1|xE+ih2x&$VXqCQ7Hr=Jt(AYS2_{6bjiN6BVOhUF-5X zbNoqjb3MEGM9|e(<|A8Lvy9z~1SoA|mm-mgF@mP&<+eL9O9(HIAoldw@RP*m#s)`F za|C@NW~vvX&j2Pw^a8Bv*r-Tgo|M-cvpgNA-%z{r?nz!VE!FEn&@uMfrtOgy2M*xM zdHYTOJEqFkB35#Kf8=0$%ekh7=^z#==<8TPLniBqG;S$MKi}57IiWF=kyo{f`MV$fvT1p98NL(DN~&keTP$daGIkGAug+ptqMprLO{|~kiPdW< z3+Sxqu;`Esty)NDdgAojEmnZaC}%hXt4A+O5gBH=DU$UV7Dtj~K-n|H?HFg2pQoF& z-U6;ja+vMGdZxKy@p|I`>tdW9t1N(4(PaS#%EAHwIt^J}L^B{s=UNo-hKhCzViGN0 zkGGFiNrC@HsbL-23d?4~)as6rDxsyS9agGBbLn^^F-#(14s^J*W~(gw313UYY1#7s z8*~!XtD9o(Y0kKSoVL1zHd=-~P@!iPESQu-Px37wv~k84xI;4~cmht!Y=d1|4et~` z1+w%M)Py9wOI$@bHaos(sU%RP;hj}X7)Ry6E4fL~wq;AmRjV+4X?T0~61OwPmqcLo z5U8t5@hFdG@e9HhXJ6X`RRm{Wji5byne47=Y^@rE!2t#I1eX7vyJa*9^HxQ0Eh&WL z+3}BDOQcadY(=T`-J2`sc1E!-s1n+$#$o0v5tpltL~$j|_MO|unRjYjyVtre7_BG& zKB?nv!VJBumWvyI zVd?n7y4U|Qs55K1s02<>qXfe3r5k%&9vcI(G zNGu~&f9V38dik?dy<*r@wpFC^?^SvVIbd92K=+^y3f3+Cdx4(XV&#&d%&mL%FYOr} z>j2snHnR9xqTZCn;u=7qF>J)&7W7zd$+m;*fw5t%_}dGgW$Arw8HNM$F@S)-mjkEB zb-;xX_)U6*d7Tu?NX1_!b>K#g_~k@!g5LVh3fc@r6BdL>Y=|?l$roLi~eu+{1lMa6E=o4;x$jvf1+(Y;Crp z6@ORp(1&y`87My>Ik=CBM#Xw##o+PNB%*TP?dIq| z@w{q@mwfpIK+QJ+F>KjffS+#xbVez^7_We_zO3{thOz7Rjx)eiWv%BFaM{RR>>q;T zM3M|PCi}AA^O$=tH65ihsY@|{f^bgtF)Jy@oSo1y{3%>qR3tSWK_`JyIo*wC2%xQ4 zTs%D+Izrukg9ji&1>km^&~bcD zVTXZH++s_5(sixVIZk(b`mGCHxzjT;fvzYQ_Aeb(zk5qMLAd!W9j~LvvWFz>L6UT& zy4>83*bCzZmmsn@{%m*KQ?c%ZfmPhlbk-Blu{}Qg1^V6u)X=2vs=*M5@H{IOR0wfE zg>hy4{?r#wc>sSadjxeoZKj)+odSO*=W8Q;n}!xjBhkfkFmFqE~9%UUQp$vTJ6S~9Cd6q3V}o%~r2FuDS$nzKa9 zW={}T$x9nY2Ei$00X|DSZK;kiB33eiq62h%-YgZ-1|458OGHeS2@wmp;>1N|R0Gbc|-U zgy5F@xQpKx0M(UDJBPL@z2LBfSG%p<;%hu?`sl%nrpfKHQGKJ1?#1u1rp4_eGno^1 zN)J<(t*LV`PMpcL(!$n*_?`h}?H51N=>g)c5&I~lV0@j#CLX*)DkHUPMKYWG|(*uL?W;GyYO zTld0&0{wE@$i!pMkns5DPi>A+L^DSX{R7*^Mjoe960i%I{+75*#^VqQO?sGA-J0k) zNm-B2WE`~w^3Tg`pp=Q-U+n#pTi0knuLHH0e>h`52vpeQ!Z+EEeJB%}cawmL4hgQ^ z{?Ha0weZVv|K-#Lls}ug)>OUGmHdRtu7Gb&Jw^pD@uS^?YgItRd|_h?j}kf&1oRZ&VdWi8{W9*534)g0^+a2nKhs?4j+cg z1N;iRl`(GM1exMQ3NIQ%u!|8Mn2LL#OB{Xz;Jo9|)E<6+K^$=@g3O8JU(|sdqmFhP zSnj)AU7#$`GR<2BTg)vUZkQR!tC2EYx#+Ekoj5hIAr%7NjbO&92@N4J716c+$c{kn zSh0bvT}#9Tq;z7fU?T0$rYp4@QMJB<{(UyOWN@pPEDG*pn6=(@d~cg3~gj|@MAC`t5_UbW#Bi&9rsxJ68x$Ksr?1?uBs zh46eCYgUlTK)RYsiCRO3+t;_K9Qq%S3W91RNXiwj;(ubfB`rFwyFpi#iAmZ-F)FF8l`b&MZtv)xb?0mEeHb^@)xqqjzT*Z2WE{>rYHqXq5Bbo$ zm6t;m@1QRiDQmpCn)5$mkFI=2$auHoPrXtEc}%F|BxRzG#$Fcvve0Rxg3FulS6yCh ziSIg^a(Vr-yzX$;2h;YUUk@AEG|=uKZjyonvJXEwT6Rgzy8GlL)md=fSPMR5buz*6 zY5Ad{lbY+-xITO*`E+)}&&K71Yk58#)!i#6HP`Os7&l-K(C-n7s0D{4-hAhtfkUUQ zsm`xb|?FC!xgNE^FfTJ0-?bK8sNYSchPjR{Tz`UY~OZOmBuY6nCe>m|aY* zI+St=3ighSxn|-6-Tgq`iS7&zb@v1{Lg(KN9O4J!<96{WHbNXce4J>7P9o z(X``f&~i-a$f^-sq1QH}>T1vDwlsBoAIl0>FxapUqBMi5V!esgs%Fz_<__EY<*a|K z-3QvUtN??C;?VaA{`6WWWV9-9#%q!q)ZPU!n^R_kav4qA)%cq`O}?IARIXxwDe%MI z{1{S2iwBI_xo=s3o_@dM12eu9?$#%NF@4rCsFH|%2d+{M`ytN&hel|VrJpN#ZuhUd zRnE{R6TgO=_l^9Bd#jqxbI%t}p^5{`U*J8SlYliz?T7coqP3}09o|bfqnVY&#QO?A zcelQ>7mlTK!@p^(ph?j@XkW#PTV->j@4>_GwYd8i{0O&C8IjkTUznZidwwd#TORd` zl^@chNv|+)`;_ISsGIMEl@n>v%qmUzFJM1KWVF3Q({6EZ_`B`O`oi98zg+Nh_GW5M zovy?|G8umrSDrhUIry8kkEExvzKET1NdWHaIc4YrYw-zp%>3}zD}H2f=Hz=U>pih# zPL$A{XT%JHWXueOgz455A;@O1;YCb@(-8XB#R4$+8nCx8N1#hb#lQyS>){n)1iDl{ z2P{j_rPC~Cm`j64;r{j{l_euYoE*lz2R0Pia~YB&JYe^L?<|uq7a=OlY7pxl4Pr9h zskkR4!i2f99ic%3n;H#5R^F2qK_2FNWb7VMf~l;<8@ehpR$))7?zS1PP@ZeHf~YT- z=Y2w^_IpIsmls8WEzg285X2i14nofc7CvZT;e$BmEVKa=3#X3{ zs|O!+4MoN)r|lHivrNHQ!o_@W!*#%9OO7^FxuA-TWu*|N1S4{GsG4<=3~#QL zCW@&JjC2={7+tQYaajQzJta;GhSZ7b%S-53HA~^8kM0GhpQ zUWqLn4tK2*yTHZur37-ZqTR(?EGwmBeHd8W@DeUAjuq})C1{<7R0&%*9kUlWovDTl zm!08o=WGF|^9BIh-f}rDMwiaau)lYVF0@2rDF6Li3o{prPo^HuuCf=3pB{M6~NhTA=NlqL>WWupgYZ5QLt zqx4;aMy3y#=x+sfU;8XO6Smab8TJ|JAk;*5b!75Rvq{A2fs^`$r*R(-80rTGj*Q*O zGEv^vM|S=GJo8p4?n$6*Boij+-LkLQ>E+%E?=YPpoOja?X{%q=-_AK`6?}F3pb=m) zn?gqhdQXSQn3=bNuTEwL(D>#0ovr)=eU`62lJ?ofDCZ~?CCTtR_zS0lxX78*EnVmD}w_Ix9_ILNCsbWZb? z7%0tSy|x=qQ0zB3k65;~im`@|k4U!il&IVSkqQ7zB;6BxA=?|tUW(|bO9ixOfRx%X zf7#2jsnI$(koWvn@Z9`>*9UVgBm2{YPKJ4#Lyp~yI!|j~Yw;|W} z4nA&z2QVIQ1j*%(n}Pz`k6MKa=H4Bn;5P3Y)hwpnH1KzBotv>?ownh2ypFRWcg#)N z5a9vzqofdl@URsi0f0=z{HU;5ZaSOqPo?(uHT@VQ0)cn_XPVIn7K ziA>`OXn1Zrk`t`pG{HU`GeKUh*`ybH)G$XK;S=Zp#hjyk$nH_o2NR;DVpPp;+aQ+B zjtLfOkHsN1p~77tE~!O+O$nz~W;hzuM1?VfCLDstyV+!9>;z?Vv$&vcQ_a>!GZ_$C z1Rd#=@Jb*AT}6d;oIyY|MEW3P{guECG=&<>|%zv29g-u zXjZDzdc!LfTUL=f-TsQi9U0g4cUo`kDKNMcs~*cDJK-97N(@MBT;~*@mE|9c`;gV? zwBFS7+`uk25_usAL~iP-HVBCwx|5qSku~Ae*!;A~V3$2Ilf`wSHVeW*Z8Nf0xh@?& z?SK&=Kn=+kazNng8QCC0oq=5(8bcR4i5R-pkjA#2L4%OEA<14Aok0MRTY5SSxUsU> z3={}^BLfR!C4ow3_%pgZr*pJ3eooFUJ*~O0XvgbxjvYRzG27Q|~NjY9YaQ567kZ^A7dunSKi;2wf2-?oJWlgg$#4wv| zeYtZZ6AepGASnEZ38@*^xG?&uqoA4ExbSmO11iT4Fi4}WzG{goN$?(+r<4NY+2UAC zG*ByL&%K|3a^}V-8dgopn>9#40~Rw#Z>Bc%mD#SwU=TTOL7eG1<`V`AKm8;-7K7L$ zE4Q_unPWXkij^Y<+W~P4!9m=nzUQ`_5$E#X4h`k16N|Pla({YM*&4^HMTyVG<7!hRolA9;y*m`2NKJ8B0y%@)K_Io z0>lCrAV%l-2Ptmm1O;)}YcFk6W97)4&>;7w1-~HZOtKGWj%_m8EK)uRjm5;~AcNpD zYqho%ARg>$RyX&xNV;2+Jo-LcQBMuSdGdOTVTmk;(Vj>{BH|>tNkd+ux|!Z$(J4_+ z7)~Zqo9Rs!6bU*u8{r0y%?@(ou;o=2sS+7dj1SKabqi?{UUt*41*3+fL|h|yJBivz ze{MlyPwT9x-y2pXIycc@ly!6))+H*hx?#~k$G+jP1rvFp5;T%gmk5mpXtj+(-#wnm zPd!x@?h;vKwx^qWqwu_&Ia@HY>KBY_q*qwj1F>OtqH`lyC&{^y{?x)yf{tWlB#LGY zlM-o&Y!5fhbkQxq2$+V5L=nf(Kau*Ho^L^3(KhDnt_)Gj%v$Ir7Sd@s*`jkrb~D@F z4L&XCA2y#6^bH@GDGGPfObHl<9*OE#bdp8L3T`A%PlCRl4RYg5O;@eRu4S9L5vPix z+)AeeZ!E2p&`6Rop>#_4h8q&W%2Cq(d$hDxI=^DvgY8ZW>Dm-{#MFL5>_MPS3HvPV zl@XJc5GD1M*oBbV0KnW=HkoAJ3Z6-x1dOGp()l%m1dCgNd97l?N%{mV5-^qxjC5G& zG(A=763_Bi%6RlN)J2J)3sRzVt{2-9xUU%*9Yu{m1dc+Tl*q$Tb&W0lY(*gqH7)Gp zT1KHrjp8!|^JyUiPyoowfLX3c>{X&MOH*aU7;qcl3AKC$a}8x8!CV9GwY>rJS=k)e z2e+sIj{QqO@|Eg(MmEf7TJdYgbffq2D&hDz7HTz`6~Ij*nAQVc*8i($T4hkQda(nvJc19t_A2%rH#poOYCMQ48LBaS3q3BTJ6|Wy96jG{ZRi}&KdOga3=72qJ_OTUq%&;3 z5@kq@?|z&SkkDNK{MGfdP(#ruVASz^kY1?^e<(f894)$St*6BRAlSxd{s0^hqK@w_ z(DB`?mz&E(-M)sz`P&e^?IIl`7e2Gc+?7B4S&|_Y+g+l=HJ2^iw$}qM-UjQr$2$-V zai6k7Wk5;&_A}I8qDZIn6U^L&{}%Wd)EEbpp@<1Y#QAQ|y_+20`$+oIv(0gDKZ6*C zN_t5;aX{ST?ZWR9%=(U=^&~pa(Z96H)9QIbo!9Okw=~~;Ga9n+AAQdd%$w}vcINmu z4?O`R-wCE--sBwjF~|3hl2vaZc~nfJ6t_3|S6zT`51|1W;wsH`#Yh~#s-JI+`i1Lf#y z4KyGeWS!UDe#ZEiGX!{&pXqK&-+KnHG+;s;V)@e}M4j=h36Lv_&mO_)%=D9g2i5{S z-A)5Ii0ZUH3!o9eLp&68)hS`NyY9?3$Tf~XF%nA`f(Qe;AqKQ#I7eA@WMjIYPI;CS zNPSKgwPFOCI){$JB@4|e(l1@8Fn}N1ev?%Ikwr!}Y*yH@W5UL||49FtJ`O58Cn8)Z zF^F|)obIj^2n?igVdFB%K21c*g>r-4IR5+Y7f(mCE`r>--8Jd|q&I+4XGd=8RObhz z-eDbb-+SdA1sqm0Uo`i5h4BNQ+tAUafrh?&SA0cVfT?#I{dms@cqsq8-FU?Zc*~!? zXX1-?OVKlBxyzT0H9pYo`69vRU3<~in-X8*=033At!UPoS{5n$A;}uN4Qv4tlAg8a zr_Aha021%a+*A6{R6n5h5Rx}R0N-?VnR;n}tNpyrnC7Ed|G^I^EiV1wWqq|Qa_Nnq zuY=p@$h|Bd2e+P;A3UuKf#l~6`2oba7uHi{VM%kf)@`mmvd`&&3YpvJ8hdQESlV# z0PGkfuUPeN^wlFRV9u~c7GrfQYX%sLb{ppw*^|S87ylyVFj0P_rJ)s9*$PPAOYJQr zy1**{N-+R7AVK27%u>albtQTHSR1@3KQO3&+Ft2niVMMs4w6liX;YBku;f>$ zwr&Mb;?YKUeVtp^u7s?-u$Og=2>NPhg^x|Q|J3*B=-$2-5fZ8B^|N2cr*9uzHKm5~ zQ-MHaiVIy-d|wVY^>QCrlX=lz*4e#ztvn@t1DJpd;p1PmbatsBA&>CcAn31^J#9Xh+!-?y7O zhw=&OlFUT=bA&Uyb`G7g8U3z~5y`;`)2pG8%I_OYi#xh^4yg1w_KnGcmQU(b2f3!n zd&=z-1P!Kzp*RVUz=ejTq$@Ig?CU#6miF6+-ssototFDzXvA5+yC08E_;U%F8-TkS z8lLi;XH!#0Q+;&VewS9)NX*@~GCI8Y)Q5 z-2A}ZJgln5CueRy76FxB?YsOs=w@_X!t;&ll(EOdb?C0%onOTMQ=w5ihj(qtLM0`2 z=Te^o-`fb46xB7R^kv%E!7+k&4RvaCU$%`q9ItvjQHR6y-LqkaO3Ek+X?;0Bgqgc< z>|aWq>;0fr|Dpn-;&y);b*}HRwvLK6GvmJAzd=UnemQ;Ib8{#b{g*FXuG;)SI(c(nEG>h6LxsQRuR zt?o8x1=X0Av1_1(Y;{dTX2Vh|RGs(2%Hs@Wfyf-0upbhK!_ad|iqhzrAQmu$8AFxl0y-SJdbazm; z>rB7Fw4kZR#;M+?&-Slm31id$dj3BM=l!)88R538NfMOUs$x#4*6 zE0uKq&TLs4x3!%g<8EN80uq9qPDu%9cDpKBmdC}87$((F8l$E6g zu~C}bej-X!2Kr7DW&*{>A+GLqr7(4 zOoSk#om8xeP49dJ(M>S}PAL=M`|BER!N!^R!V;+T+9mr5#rHO*EWD`-FK@M*ko3CQ zQB!)#^pDsp@0ymj^4I!0LNvKKgbqIQAj%G^{hnpI-ICLx%nY&Da*rm}xvM_}hF~@h zSKE|QCiu}ib z?PepFL`AYJV@N4=x_~=WRkHb>UD4<~RqSh9+u{(-{9}UrN8g}{eb9xPOajub3Htl< zzKPU7`X+&VlW7!C5Bb9}EwB_>dr6;XA`MbhWnLHr2Np-w1|U}(X%(42BbAK~QTU&b zqQ(fHoFH3|sHpbm%M;M3T1*bA)@5uCokO=ZY!Us0oDC|CTB>-yW0Dj|9v}M|8P})= ze#!>Z05Zv`KzKAWZ-V?R^D;7Yw70scE|U3hqVrj1z@SGUwMlu@b|ugpC9lW~9rO+S zy4fLyS!iMvq(Lpca108hHsju}6`OS01~)pyF^eWb8Z%KyZlg1&*u}Oq=<61TxNy!~ zXil2#1K<$x&m5s`P0-br`Z$Si5GN*lwzsXxI#w{dHf%CsTQdXvnG8#6&c(vW}m!^#Oyh8XLXIC~@ta68y-}cikQ_gt*ccWzXn2d5BxMQNfQ0`!PIs z1s8{KPgMR8W`x(eO?!EAzqzkaFMa-X!a!XV)p@Q4_ zXG@wy5=tmt!c>aCfpOP~f3*02#Q#i!z^smtdF-FL#E+Xlqnh`U zJ|wrjhUbhjE>R*0Cuo-BRgjG2eV3DL{t;6pUWTo%E5RK*S5O|~!hZm>c}ti-#hV?l z7?Kyva>Mz(&=-}uA@V1Az_jb?am7=z(FX3l;u+wGe==T>ch_+rgqKPFFsqN4YDO)W z`~tF_Y`!e7X4H}GH>e*76HA-_0Lmd7)c1;F;7Z^QMww+&GG~eT*fMQj3ZtAXT_;un z7yfh>!(fKM;FAz?Vl!emfo*!DUu=U&ymhB4Zgv?EzS^TN0;LQR>#!DjX@}apa46 zo=MO)A#1-u3`^o-0Ru3MSfs70v5ayzRx42a-&o6$i}O5^wAdEwF5&1byiHN=W;iQx zk>V1>=ab-om@>Utj5sc0mRa+85|#9>^VOR4-`efRyK$L*N9|vtpWO+)S!TVOcIUb= zv6w&b`GoPZ+ZhJ3?ax_HNm_{T=Uk(v-IS0yqcnQwchfs`-m``qDx{t|A3 zxY#+rq>cCa-1E_8R?;J16e{%fBIX;jb-F8YM_%)vqWn_))JZ>X%C|P!?L35M*dtET z)}`TX*4L9VaqhC(E41{>%|=Xc+6L~*-Tp_j+L_Q&W5I_3gqS{XZMRqc;B7%J;(P5t zVU_l>u;|w`7qb2)M2P=&7H4u-eB$`5*w=1LucLcp#Y^PE`_4@4q0LoT5v@0cUD43p zGhte@*x?Y=EE(wZ{7W%kL!8ixM^F-$8j+}QbmDpjdH`fH?hDPCMi$4Ld@ z40(5ohS7bKa&mhcNF*Wd8F- zmDHSdoB#9Kw#~TsR?LYaj>Aues!(%QvgWazmfb-Z;Aw+|MWni~fec+vWOP}w;=04% z&JJ%HESC?(B16x%EFCwSX^{{0L%wDqX;PfKFepz9c9->SJ}|Om-6V?Tr~BJON*NBG zR`D%e(j2hag!}Z&M8`Zz(ebEj$2g?4RsF(#7=Bf8Bc>#KU`pF3+&sKo_8@k-lo*RF zg~vA72e7HZYmI5kxX!txT(M2ft=Ekn1n70S9s_HlxRT~sMD!XJg zz|=l6McA&quv%*n{A;aYcLO)vs~E?|IYn}@!*_fhm3LUO0!DruV2W-I42krNil#*Y zuU|9k=5Nw%0;ZMj1B}YYH*S<%7--jiDKtAwD4Y*gEI&%xIV0RWq;h=8ofcKiyo8zI1xuJtKkIn~^ z#I_<~k$%V%XfzIX<6rdCt?ygpn!Xfh#(BgEQgMNG&Q{4~lG*!uZ(YYDUfOd!8?*Py z|5F!V6;*e`C+!XHqA>db;#}$!?qY^AWWkpX4H$Ru8Ez~pd$8n7#6)D6cnzPsbP};; z`atZY4jXu^oresga1%cduh*He(3&l|IRjN|({`DVA>)%`` z_Q^kfsbYZCpFRJh@WR@&T@G$FBkd&k803-Jjz0tZHx1ZqG>rh&zZM!8{|p~Be0L=7 z>@%3D_3>k#@$PrC^1qY>SWl0r&y*fL)jC2mu^%3i z9#=j;p?A#sk}DO#5bfnEH^2(Eb{OF#owyVUNdH zF61-7r_>LjVqXyJxrQx|IZXN~PXS#2PofCtvj8C>F01=5JOT55%2FV@7Bug~4-i*n z-C0{Oz_iM6z~IXKg^%Q-0H z!U@8rkb!l={rhYI<`jO=`L_oP9Ulp0x{ zu$tC(m2UPte?)rVHSmK7Jn&<4i_gikK93cD*CDCB*Yf;PrTVV9hzzzVB12X=4-yVt zpu7_E=8p-zhYbZ@z`u;@BiB>&&9B4pZ_kR{T!L039J#gl@z9_C(|yY26J3QykHNpFe}i5xeK-9r_Qee| zMNvHWKV9=fUs7m}tgk&LfZyTkoVRAjIX_Y4<+bEtQ-SBOiQuQRd7JMp49h*fX6f$; z<+KqGE0(@I-yhF_-gI{Re60$5f%t6zL;a6o1NpD<5ntHHN4%vC;J=~$qJ{VV1h#9e;q>t8p29QZzq@cLIl8T3YBf%lCmYg4mT z7t_zB+1T^-!>xb2DvtU8O8-@BdibknojmYIs%DKoXiOQ|Tz*>y`3 zm2IueypU;HS!qg%icp!dWoe1#4Po6xDz9l~UVxO;NCbmTGZj=+eB}icl8YeUSD)YS zk0*0|J?A;Y%)I6`^LRc7d9+dQ3p1<}$Lv%5nO}6d2OeY1R_}%mD^ByiA0`%XW19B0 za&UV}IJhTdL+7oS{fZp2zVp_U4Vx9Sj}4E*pQR)tbU(v5D;C!F!|-E~&hyCFW%0Af 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
$z7WnmS54T&N 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 z Date: Tue, 20 Jan 2015 16:21:13 -0800 Subject: [PATCH 13/94] documentation update --- R-package/R/predict.xgb.Booster.R | 2 ++ R-package/R/xgb.cv.R | 4 +--- R-package/R/xgb.importance.R | 2 +- R-package/R/xgb.model.dt.tree.R | 2 +- R-package/R/xgb.plot.tree.R | 2 +- R-package/man/predict-xgb.Booster-method.Rd | 6 +++++- R-package/man/xgb.cv.Rd | 6 ++++-- R-package/man/xgb.dump.Rd | 2 +- R-package/man/xgb.importance.Rd | 3 ++- R-package/man/xgb.model.dt.tree.Rd | 3 ++- R-package/man/xgb.plot.tree.Rd | 9 +++++++-- 11 files changed, 27 insertions(+), 14 deletions(-) diff --git a/R-package/R/predict.xgb.Booster.R b/R-package/R/predict.xgb.Booster.R index 122e116c7..49f1ad4f0 100644 --- a/R-package/R/predict.xgb.Booster.R +++ b/R-package/R/predict.xgb.Booster.R @@ -7,6 +7,8 @@ setClass("xgb.Booster") #' @param object Object of class "xgb.Boost" #' @param newdata takes \code{matrix}, \code{dgCMatrix}, local data file or #' \code{xgb.DMatrix}. +#' @param missing Missing is only used when input is dense matrix, pick a float +# value that represents missing value. Sometime a data use 0 or other extreme value to represents missing values. #' @param outputmargin whether the prediction should be shown in the original #' value of sum of functions, when outputmargin=TRUE, the prediction is #' untransformed margin value. In logistic regression, outputmargin=T will diff --git a/R-package/R/xgb.cv.R b/R-package/R/xgb.cv.R index 0aae574fb..c60d9d96f 100644 --- a/R-package/R/xgb.cv.R +++ b/R-package/R/xgb.cv.R @@ -32,7 +32,7 @@ #' @param nfold number of folds used #' @param label option field, when data is Matrix #' @param missing Missing is only used when input is dense matrix, pick a float -#' value that represents missing value. Sometime a data use 0 or other extreme value to represents missing values. +# value that represents missing value. Sometime a data use 0 or other extreme value to represents missing values. #' @param prediction A logical value indicating whether to return the prediction vector. #' @param showsd \code{boolean}, whether show standard deviation of cross validation #' @param metrics, list of evaluation metrics to be used in corss validation, @@ -50,8 +50,6 @@ #' @param feval custimized evaluation function. Returns #' \code{list(metric='metric-name', value='metric-value')} with given #' prediction and dtrain, -#' @param missing Missing is only used when input is dense matrix, pick a float -# value that represents missing value. Sometime a data use 0 or other extreme value to represents missing values. #' @param verbose \code{boolean}, print the statistics during the process. #' @param ... other parameters to pass to \code{params}. #' diff --git a/R-package/R/xgb.importance.R b/R-package/R/xgb.importance.R index c2688848b..3b7f69401 100644 --- a/R-package/R/xgb.importance.R +++ b/R-package/R/xgb.importance.R @@ -33,7 +33,7 @@ #' data(agaricus.test, package='xgboost') #' #' #Both dataset are list with two items, a sparse matrix and labels -#' (labels = outcome column which will be learned). +#' #(labels = outcome column which will be learned). #' #Each column of the sparse Matrix is a feature in one hot encoding format. #' train <- agaricus.train #' test <- agaricus.test diff --git a/R-package/R/xgb.model.dt.tree.R b/R-package/R/xgb.model.dt.tree.R index b67597126..e77208720 100644 --- a/R-package/R/xgb.model.dt.tree.R +++ b/R-package/R/xgb.model.dt.tree.R @@ -43,7 +43,7 @@ #' data(agaricus.train, package='xgboost') #' #' #Both dataset are list with two items, a sparse matrix and labels -#' (labels = outcome column which will be learned). +#' #(labels = outcome column which will be learned). #' #Each column of the sparse Matrix is a feature in one hot encoding format. #' train <- agaricus.train #' diff --git a/R-package/R/xgb.plot.tree.R b/R-package/R/xgb.plot.tree.R index 443446916..b5f7a959e 100644 --- a/R-package/R/xgb.plot.tree.R +++ b/R-package/R/xgb.plot.tree.R @@ -43,7 +43,7 @@ #' data(agaricus.train, package='xgboost') #' #' #Both dataset are list with two items, a sparse matrix and labels -#' (labels = outcome column which will be learned). +#' #(labels = outcome column which will be learned). #' #Each column of the sparse Matrix is a feature in one hot encoding format. #' train <- agaricus.train #' diff --git a/R-package/man/predict-xgb.Booster-method.Rd b/R-package/man/predict-xgb.Booster-method.Rd index afa0c70a5..d8da7975e 100644 --- a/R-package/man/predict-xgb.Booster-method.Rd +++ b/R-package/man/predict-xgb.Booster-method.Rd @@ -6,7 +6,7 @@ \title{Predict method for eXtreme Gradient Boosting model} \usage{ \S4method{predict}{xgb.Booster}(object, newdata, missing = NULL, - outputmargin = FALSE, ntreelimit = NULL) + outputmargin = FALSE, ntreelimit = NULL, predleaf = FALSE) } \arguments{ \item{object}{Object of class "xgb.Boost"} @@ -14,6 +14,8 @@ \item{newdata}{takes \code{matrix}, \code{dgCMatrix}, local data file or \code{xgb.DMatrix}.} +\item{missing}{Missing is only used when input is dense matrix, pick a float} + \item{outputmargin}{whether the prediction should be shown in the original value of sum of functions, when outputmargin=TRUE, the prediction is untransformed margin value. In logistic regression, outputmargin=T will @@ -22,6 +24,8 @@ output value before logistic transformation.} \item{ntreelimit}{limit number of trees used in prediction, this parameter is only valid for gbtree, but not for gblinear. set it to be value bigger than 0. It will use all trees by default.} + +\item{predleaf}{whether predict leaf index instead. If set to TRUE, the output will be a matrix object.} } \description{ Predicted values based on xgboost model object. diff --git a/R-package/man/xgb.cv.Rd b/R-package/man/xgb.cv.Rd index 7ba5eb727..0867134ae 100644 --- a/R-package/man/xgb.cv.Rd +++ b/R-package/man/xgb.cv.Rd @@ -5,8 +5,8 @@ \title{Cross Validation} \usage{ xgb.cv(params = list(), data, nrounds, nfold, label = NULL, - missing = NULL, showsd = TRUE, metrics = list(), obj = NULL, - feval = NULL, verbose = T, ...) + missing = NULL, prediction = FALSE, showsd = TRUE, metrics = list(), + obj = NULL, feval = NULL, verbose = T, ...) } \arguments{ \item{params}{the list of parameters. Commonly used ones are: @@ -34,6 +34,8 @@ xgb.cv(params = list(), data, nrounds, nfold, label = NULL, \item{missing}{Missing is only used when input is dense matrix, pick a float} +\item{prediction}{A logical value indicating whether to return the prediction vector.} + \item{showsd}{\code{boolean}, whether show standard deviation of cross validation} \item{metrics,}{list of evaluation metrics to be used in corss validation, diff --git a/R-package/man/xgb.dump.Rd b/R-package/man/xgb.dump.Rd index 473227357..7958a72e8 100644 --- a/R-package/man/xgb.dump.Rd +++ b/R-package/man/xgb.dump.Rd @@ -37,7 +37,7 @@ test <- agaricus.test bst <- xgboost(data = train$data, label = train$label, max.depth = 2, eta = 1, nround = 2,objective = "binary:logistic") # save the model in file 'xgb.model.dump' -xgb.dump(bst, 'xgb.model.dump', with.stats = T) +xgb.dump(bst, 'xgb.model.dump', with.stats = TRUE) # print the model without saving it to a file print(xgb.dump(bst)) diff --git a/R-package/man/xgb.importance.Rd b/R-package/man/xgb.importance.Rd index c173b1e8e..1b2946729 100644 --- a/R-package/man/xgb.importance.Rd +++ b/R-package/man/xgb.importance.Rd @@ -38,7 +38,8 @@ There are 3 columns : data(agaricus.train, package='xgboost') data(agaricus.test, package='xgboost') -#Both dataset are list with two items, a sparse matrix and labels (labels = outcome column which will be learned). +#Both dataset are list with two items, a sparse matrix and labels +#(labels = outcome column which will be learned). #Each column of the sparse Matrix is a feature in one hot encoding format. train <- agaricus.train test <- agaricus.test diff --git a/R-package/man/xgb.model.dt.tree.Rd b/R-package/man/xgb.model.dt.tree.Rd index fb5bd94bd..0030fb14d 100644 --- a/R-package/man/xgb.model.dt.tree.Rd +++ b/R-package/man/xgb.model.dt.tree.Rd @@ -44,7 +44,8 @@ The content of the \code{data.table} is organised that way: \examples{ data(agaricus.train, package='xgboost') -#Both dataset are list with two items, a sparse matrix and labels (labels = outcome column which will be learned). +#Both dataset are list with two items, a sparse matrix and labels +#(labels = outcome column which will be learned). #Each column of the sparse Matrix is a feature in one hot encoding format. train <- agaricus.train diff --git a/R-package/man/xgb.plot.tree.Rd b/R-package/man/xgb.plot.tree.Rd index ce69d4431..8aec827ec 100644 --- a/R-package/man/xgb.plot.tree.Rd +++ b/R-package/man/xgb.plot.tree.Rd @@ -5,7 +5,7 @@ \title{Plot a boosted tree model} \usage{ xgb.plot.tree(feature_names = NULL, filename_dump = NULL, model = NULL, - n_first_tree = NULL, CSSstyle = NULL) + n_first_tree = NULL, CSSstyle = NULL, width = NULL, height = NULL) } \arguments{ \item{feature_names}{names of each feature as a character vector. Can be extracted from a sparse matrix (see example). If model dump already contains feature names, this argument should be \code{NULL}.} @@ -17,6 +17,10 @@ xgb.plot.tree(feature_names = NULL, filename_dump = NULL, model = NULL, \item{n_first_tree}{limit the plot to the n first trees. If \code{NULL}, all trees of the model are plotted. Performance can be low for huge models.} \item{CSSstyle}{a \code{character} vector storing a css style to customize the appearance of nodes. Look at the \href{https://github.com/knsv/mermaid/wiki}{Mermaid wiki} for more information.} + +\item{width}{the width of the diagram in pixels.} + +\item{height}{the height of the diagram in pixels.} } \value{ A \code{DiagrammeR} of the model. @@ -40,7 +44,8 @@ It uses \href{https://github.com/knsv/mermaid/}{Mermaid} library for that purpos \examples{ data(agaricus.train, package='xgboost') -#Both dataset are list with two items, a sparse matrix and labels (labels = outcome column which will be learned). +#Both dataset are list with two items, a sparse matrix and labels +#(labels = outcome column which will be learned). #Each column of the sparse Matrix is a feature in one hot encoding format. train <- agaricus.train From 42110f3d70355ed05cd0aec176e2511b9a4f7489 Mon Sep 17 00:00:00 2001 From: hetong007 Date: Tue, 20 Jan 2015 16:24:01 -0800 Subject: [PATCH 14/94] documentation update --- R-package/R/xgb.model.dt.tree.R | 2 +- R-package/man/xgb.model.dt.tree.Rd | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/R-package/R/xgb.model.dt.tree.R b/R-package/R/xgb.model.dt.tree.R index e77208720..ec50728fe 100644 --- a/R-package/R/xgb.model.dt.tree.R +++ b/R-package/R/xgb.model.dt.tree.R @@ -49,7 +49,7 @@ #' #' bst <- xgboost(data = train$data, label = train$label, max.depth = 2, #' eta = 1, nround = 2,objective = "binary:logistic") -#' xgb.dump(bst, 'xgb.model.dump', with.stats = T) +#' xgb.dump(bst, 'xgb.model.dump', with.stats = TRUE) #' #' #agaricus.test$data@@Dimnames[[2]] represents the column names of the sparse matrix. #' xgb.model.dt.tree(agaricus.train$data@@Dimnames[[2]], filename_dump = 'xgb.model.dump') diff --git a/R-package/man/xgb.model.dt.tree.Rd b/R-package/man/xgb.model.dt.tree.Rd index 0030fb14d..f91a2afe9 100644 --- a/R-package/man/xgb.model.dt.tree.Rd +++ b/R-package/man/xgb.model.dt.tree.Rd @@ -51,7 +51,7 @@ train <- agaricus.train bst <- xgboost(data = train$data, label = train$label, max.depth = 2, eta = 1, nround = 2,objective = "binary:logistic") -xgb.dump(bst, 'xgb.model.dump', with.stats = T) +xgb.dump(bst, 'xgb.model.dump', with.stats = TRUE) #agaricus.test$data@Dimnames[[2]] represents the column names of the sparse matrix. xgb.model.dt.tree(agaricus.train$data@Dimnames[[2]], filename_dump = 'xgb.model.dump') From 417ac4a6316656984f7acbfa4c9c220254a24ee4 Mon Sep 17 00:00:00 2001 From: tqchen Date: Tue, 20 Jan 2015 17:15:54 -0800 Subject: [PATCH 15/94] rm socket from source --- src/utils/socket.h | 389 --------------------------------------------- 1 file changed, 389 deletions(-) delete mode 100644 src/utils/socket.h diff --git a/src/utils/socket.h b/src/utils/socket.h deleted file mode 100644 index 73caa2fea..000000000 --- a/src/utils/socket.h +++ /dev/null @@ -1,389 +0,0 @@ -#ifndef XGBOOST_UTILS_SOCKET_H -#define XGBOOST_UTILS_SOCKET_H -/*! - * \file socket.h - * \brief this file aims to provide a wrapper of sockets - * \author Tianqi Chen - */ -#if defined(_WIN32) -#include -#include -#else -#include -#include -#include -#include -#include -#include -#include -#include -#endif -#include -#include -#include "./utils.h" - -namespace xgboost { -namespace utils { -#if defined(_WIN32) -typedef int ssize_t; -typedef int sock_size_t; -#else -typedef int SOCKET; -typedef size_t sock_size_t; -const int INVALID_SOCKET = -1; -#endif - -/*! \brief data structure for network address */ -struct SockAddr { - sockaddr_in addr; - // constructor - SockAddr(void) {} - SockAddr(const char *url, int port) { - this->Set(url, port); - } - inline static std::string GetHostName(void) { - std::string buf; buf.resize(256); - utils::Check(gethostname(&buf[0], 256) != -1, "fail to get host name"); - return std::string(buf.c_str()); - } - /*! - * \brief set the address - * \param url the url of the address - * \param port the port of address - */ - inline void Set(const char *host, int port) { - hostent *hp = gethostbyname(host); - Check(hp != NULL, "cannot obtain address of %s", host); - memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_port = htons(port); - memcpy(&addr.sin_addr, hp->h_addr_list[0], hp->h_length); - } - /*! \brief return port of the address*/ - inline int port(void) const { - return ntohs(addr.sin_port); - } - /*! \return a string representation of the address */ - inline std::string AddrStr(void) const { - std::string buf; buf.resize(256); -#ifdef _WIN32 - const char *s = inet_ntop(AF_INET, (PVOID)&addr.sin_addr, &buf[0], buf.length()); -#else - const char *s = inet_ntop(AF_INET, &addr.sin_addr, &buf[0], buf.length()); -#endif - Assert(s != NULL, "cannot decode address"); - return std::string(s); - } -}; -/*! - * \brief a wrapper of TCP socket that hopefully be cross platform - */ -class TCPSocket { - public: - /*! \brief the file descriptor of socket */ - SOCKET sockfd; - // constructor - TCPSocket(void) : sockfd(INVALID_SOCKET) { - } - explicit TCPSocket(SOCKET sockfd) : sockfd(sockfd) { - } - ~TCPSocket(void) { - // do nothing in destructor - // user need to take care of close - } - // default conversion to int - inline operator SOCKET() const { - return sockfd; - } - /*! - * \brief create the socket, call this before using socket - * \param af domain - */ - inline void Create(int af = PF_INET) { - sockfd = socket(PF_INET, SOCK_STREAM, 0); - if (sockfd == INVALID_SOCKET) { - SockError("Create"); - } - } - /*! - * \brief start up the socket module - * call this before using the sockets - */ - inline static void Startup(void) { -#ifdef _WIN32 - WSADATA wsa_data; - if (WSAStartup(MAKEWORD(2, 2), &wsa_data) != -1) { - SockError("Startup"); - } - if (LOBYTE(wsa_data.wVersion) != 2 || HIBYTE(wsa_data.wVersion) != 2) { - WSACleanup(); - utils::Error("Could not find a usable version of Winsock.dll\n"); - } -#endif - } - /*! - * \brief shutdown the socket module after use, all sockets need to be closed - */ - inline static void Finalize(void) { -#ifdef _WIN32 - WSACleanup(); -#endif - } - /*! - * \brief set this socket to use non-blocking mode - * \param non_block whether set it to be non-block, if it is false - * it will set it back to block mode - */ - inline void SetNonBlock(bool non_block) { -#ifdef _WIN32 - u_long mode = non_block ? 1 : 0; - if (ioctlsocket(sockfd, FIONBIO, &mode) != NO_ERROR) { - SockError("SetNonBlock"); - } -#else - int flag = fcntl(sockfd, F_GETFL, 0); - if (flag == -1) { - SockError("SetNonBlock-1"); - } - if (non_block) { - flag |= O_NONBLOCK; - } else { - flag &= ~O_NONBLOCK; - } - if (fcntl(sockfd, F_SETFL, flag) == -1) { - SockError("SetNonBlock-2"); - } -#endif - } - /*! - * \brief perform listen of the socket - * \param backlog backlog parameter - */ - inline void Listen(int backlog = 16) { - listen(sockfd, backlog); - } - /*! \brief get a new connection */ - TCPSocket Accept(void) { - SOCKET newfd = accept(sockfd, NULL, NULL); - if (newfd == INVALID_SOCKET) { - SockError("Accept"); - } - return TCPSocket(newfd); - } - /*! - * \brief bind the socket to an address - * \param addr - */ - inline void Bind(const SockAddr &addr) { - if (bind(sockfd, (sockaddr*)&addr.addr, sizeof(addr.addr)) == -1) { - SockError("Bind"); - } - } - /*! - * \brief try bind the socket to host, from start_port to end_port - * \param start_port starting port number to try - * \param end_port ending port number to try - * \param out_addr the binding address, if successful - * \return whether the binding is successful - */ - inline int TryBindHost(int start_port, int end_port) { - for (int port = start_port; port < end_port; ++port) { - SockAddr addr("0.0.0.0", port); - if (bind(sockfd, (sockaddr*)&addr.addr, sizeof(addr.addr)) == 0) { - return port; - } - if (errno != EADDRINUSE) { - SockError("TryBindHost"); - } - } - return -1; - } - /*! - * \brief connect to an address - * \param addr the address to connect to - */ - inline void Connect(const SockAddr &addr) { - if (connect(sockfd, (sockaddr*)&addr.addr, sizeof(addr.addr)) == -1) { - SockError("Connect"); - } - } - /*! \brief close the connection */ - inline void Close(void) { - if (sockfd != -1) { -#ifdef _WIN32 - closesocket(sockfd); -#else - close(sockfd); -#endif - sockfd = INVALID_SOCKET; - } else { - Error("TCPSocket::Close double close the socket or close without create"); - } - } - /*! - * \brief send data using the socket - * \param buf the pointer to the buffer - * \param len the size of the buffer - * \param flags extra flags - * \return size of data actually sent - */ - inline size_t Send(const void *buf_, size_t len, int flag = 0) { - const char *buf = reinterpret_cast(buf_); - if (len == 0) return 0; - ssize_t ret = send(sockfd, buf, static_cast(len), flag); - if (ret == -1) { - if (errno == EAGAIN || errno == EWOULDBLOCK) return 0; - SockError("Send"); - } - return ret; - } - /*! - * \brief receive data using the socket - * \param buf_ the pointer to the buffer - * \param len the size of the buffer - * \param flags extra flags - * \return size of data actually received - */ - inline size_t Recv(void *buf_, size_t len, int flags = 0) { - char *buf = reinterpret_cast(buf_); - if (len == 0) return 0; - ssize_t ret = recv(sockfd, buf, static_cast(len), flags); - if (ret == -1) { - if (errno == EAGAIN || errno == EWOULDBLOCK) return 0; - SockError("Recv"); - } - return ret; - } - /*! - * \brief peform block write that will attempt to send all data out - * can still return smaller than request when error occurs - * \param buf the pointer to the buffer - * \param len the size of the buffer - * \return size of data actually sent - */ - inline size_t SendAll(const void *buf_, size_t len) { - const char *buf = reinterpret_cast(buf_); - size_t ndone = 0; - while (ndone < len) { - ssize_t ret = send(sockfd, buf, static_cast(len - ndone), 0); - if (ret == -1) { - if (errno == EAGAIN || errno == EWOULDBLOCK) return ndone; - SockError("Recv"); - } - buf += ret; - ndone += ret; - } - return ndone; - } - /*! - * \brief peforma block read that will attempt to read all data - * can still return smaller than request when error occurs - * \param buf_ the buffer pointer - * \param len length of data to recv - * \return size of data actually sent - */ - inline size_t RecvAll(void *buf_, size_t len) { - char *buf = reinterpret_cast(buf_); - size_t ndone = 0; - while (ndone < len) { - ssize_t ret = recv(sockfd, buf, static_cast(len - ndone), MSG_WAITALL); - if (ret == -1) { - if (errno == EAGAIN || errno == EWOULDBLOCK) return ndone; - SockError("Recv"); - } - if (ret == 0) return ndone; - buf += ret; - ndone += ret; - } - return ndone; - } - - private: - // report an socket error - inline static void SockError(const char *msg) { - int errsv = errno; - Error("Socket %s Error:%s", msg, strerror(errsv)); - } -}; -/*! \brief helper data structure to perform select */ -struct SelectHelper { - public: - SelectHelper(void) { - this->Clear(); - } - /*! - * \brief add file descriptor to watch for read - * \param fd file descriptor to be watched - */ - inline void WatchRead(SOCKET fd) { - read_fds.push_back(fd); - if (fd > maxfd) maxfd = fd; - } - /*! - * \brief add file descriptor to watch for write - * \param fd file descriptor to be watched - */ - inline void WatchWrite(SOCKET fd) { - write_fds.push_back(fd); - if (fd > maxfd) maxfd = fd; - } - /*! - * \brief Check if the descriptor is ready for read - * \param fd file descriptor to check status - */ - inline bool CheckRead(SOCKET fd) const { - return FD_ISSET(fd, &read_set) != 0; - } - /*! - * \brief Check if the descriptor is ready for write - * \param fd file descriptor to check status - */ - inline bool CheckWrite(SOCKET fd) const { - return FD_ISSET(fd, &write_set) != 0; - } - /*! - * \brief clear all the monitored descriptors - */ - inline void Clear(void) { - read_fds.clear(); - write_fds.clear(); - maxfd = 0; - } - /*! - * \brief peform select on the set defined - * \param timeout specify timeout in micro-seconds(ms) if equals 0, means select will always block - * \return number of active descriptors selected - */ - inline int Select(long timeout = 0) { - FD_ZERO(&read_set); - FD_ZERO(&write_set); - for (size_t i = 0; i < read_fds.size(); ++i) { - FD_SET(read_fds[i], &read_set); - } - for (size_t i = 0; i < write_fds.size(); ++i) { - FD_SET(write_fds[i], &write_set); - } - int ret; - if (timeout == 0) { - ret = select(static_cast(maxfd + 1), &read_set, &write_set, NULL, NULL); - } else { - timeval tm; - tm.tv_usec = (timeout % 1000) * 1000; - tm.tv_sec = timeout / 1000; - ret = select(static_cast(maxfd + 1), &read_set, &write_set, NULL, &tm); - } - if (ret == -1) { - int errsv = errno; - Error("Select Error: %s", strerror(errsv)); - } - return ret; - } - - private: - SOCKET maxfd; - fd_set read_set, write_set; - std::vector read_fds, write_fds; -}; -} -} -#endif From 34e2fbd2c4670d6fcf5ede61079372d2debc5109 Mon Sep 17 00:00:00 2001 From: hetong Date: Tue, 20 Jan 2015 21:29:51 -0800 Subject: [PATCH 16/94] fix some issues from the cran check --- R-package/DESCRIPTION | 2 +- R-package/R/xgb.cv.R | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/R-package/DESCRIPTION b/R-package/DESCRIPTION index 0384fc599..63ed9581c 100644 --- a/R-package/DESCRIPTION +++ b/R-package/DESCRIPTION @@ -22,7 +22,7 @@ Depends: Imports: Matrix (>= 1.1-0), methods, - data.table (>= 1.9), + data.table (>= 1.9.4), magrittr (>= 1.5), stringr, DiagrammeR diff --git a/R-package/R/xgb.cv.R b/R-package/R/xgb.cv.R index c60d9d96f..cdbaf3b2e 100644 --- a/R-package/R/xgb.cv.R +++ b/R-package/R/xgb.cv.R @@ -129,4 +129,4 @@ xgb.cv <- function(params=list(), data, nrounds, nfold, label = NULL, missing = return(list(dt = dt,pred = predictValues)) } return(dt) -} \ No newline at end of file +} From e475b7d84e3ed83055683c705635e60fbea852a0 Mon Sep 17 00:00:00 2001 From: El Potaeto Date: Wed, 21 Jan 2015 13:26:34 +0100 Subject: [PATCH 17/94] Avoid some Cran check error messages --- R-package/R/xgb.cv.R | 5 +++++ R-package/R/xgb.dump.R | 5 +++++ R-package/R/xgb.importance.R | 4 +++- R-package/R/xgb.model.dt.tree.R | 5 +++++ R-package/R/xgb.plot.tree.R | 2 ++ src/tree/updater_colmaker-inl.hpp | 3 ++- 6 files changed, 22 insertions(+), 2 deletions(-) diff --git a/R-package/R/xgb.cv.R b/R-package/R/xgb.cv.R index cdbaf3b2e..2129b418a 100644 --- a/R-package/R/xgb.cv.R +++ b/R-package/R/xgb.cv.R @@ -130,3 +130,8 @@ xgb.cv <- function(params=list(), data, nrounds, nfold, label = NULL, missing = } return(dt) } + +# Avoid error messages during CRAN check. +# The reason is that these variables are never declared +# They are mainly column names inferred by Data.table... +globalVariables(".") \ No newline at end of file diff --git a/R-package/R/xgb.dump.R b/R-package/R/xgb.dump.R index 7658557dd..a0938ded1 100644 --- a/R-package/R/xgb.dump.R +++ b/R-package/R/xgb.dump.R @@ -55,3 +55,8 @@ xgb.dump <- function(model = NULL, fname = NULL, fmap = "", with.stats=FALSE) { return(TRUE) } } + +# Avoid error messages during CRAN check. +# The reason is that these variables are never declared +# They are mainly column names inferred by Data.table... +globalVariables(".") \ No newline at end of file diff --git a/R-package/R/xgb.importance.R b/R-package/R/xgb.importance.R index 3b7f69401..c1d772aff 100644 --- a/R-package/R/xgb.importance.R +++ b/R-package/R/xgb.importance.R @@ -80,4 +80,6 @@ treeDump <- function(feature_names, text){ linearDump <- function(feature_names, text){ which(text == "weight:") %>% {a=.+1;text[a:length(text)]} %>% as.numeric %>% data.table(Feature = feature_names, Weight = .) -} \ No newline at end of file +} + +globalVariables(".") \ No newline at end of file diff --git a/R-package/R/xgb.model.dt.tree.R b/R-package/R/xgb.model.dt.tree.R index ec50728fe..57d14933c 100644 --- a/R-package/R/xgb.model.dt.tree.R +++ b/R-package/R/xgb.model.dt.tree.R @@ -162,3 +162,8 @@ xgb.model.dt.tree <- function(feature_names = NULL, filename_dump = NULL, model allTrees } + +# Avoid error messages during CRAN check. +# The reason is that these variables are never declared +# They are mainly column names inferred by Data.table... +globalVariables("ID", "Tree", "Yes", ".", "Feature", "Cover", "Quality", "No", "Gain", "Frequence") \ No newline at end of file diff --git a/R-package/R/xgb.plot.tree.R b/R-package/R/xgb.plot.tree.R index b5f7a959e..3d1fc795c 100644 --- a/R-package/R/xgb.plot.tree.R +++ b/R-package/R/xgb.plot.tree.R @@ -87,3 +87,5 @@ xgb.plot.tree <- function(feature_names = NULL, filename_dump = NULL, model = NU path <- allTrees[Feature!="Leaf", c(yesPath, noPath)] %>% .[order(.)] %>% paste(sep = "", collapse = ";") %>% paste("graph LR", .,collapse = "", sep = ";") %>% paste(CSSstyle, yes, no, sep = ";") DiagrammeR(path, width, height) } + +globalVariables("Feature", "yesPath", "ID", "Cover", "Quality", "Split", "Yes", "Yes.Feature", "noPath", "No", "No.Feature", ".") \ No newline at end of file diff --git a/src/tree/updater_colmaker-inl.hpp b/src/tree/updater_colmaker-inl.hpp index 61b7fbf7a..bbf6242c5 100644 --- a/src/tree/updater_colmaker-inl.hpp +++ b/src/tree/updater_colmaker-inl.hpp @@ -153,7 +153,8 @@ class ColMaker: public IUpdater { } unsigned n = static_cast(param.colsample_bytree * feat_index.size()); random::Shuffle(feat_index); - utils::Check(n > 0, "colsample_bytree is too small that no feature can be included"); + //utils::Check(n > 0, "colsample_bytree is too small that no feature can be included"); + utils::Check(n > 0, "colsample_bytree=%g is too small that no feature can be included", param.colsample_bytree); feat_index.resize(n); } {// setup temp space for each thread From f1d9fe8153aca9d1bbec30fc5671a1f28ba0debf Mon Sep 17 00:00:00 2001 From: El Potaeto Date: Wed, 21 Jan 2015 13:31:17 +0100 Subject: [PATCH 18/94] fix a bug introduced in previous commit --- R-package/R/xgb.importance.R | 3 +++ R-package/R/xgb.model.dt.tree.R | 2 +- R-package/R/xgb.plot.tree.R | 5 ++++- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/R-package/R/xgb.importance.R b/R-package/R/xgb.importance.R index c1d772aff..094171382 100644 --- a/R-package/R/xgb.importance.R +++ b/R-package/R/xgb.importance.R @@ -82,4 +82,7 @@ linearDump <- function(feature_names, text){ which(text == "weight:") %>% {a=.+1;text[a:length(text)]} %>% as.numeric %>% data.table(Feature = feature_names, Weight = .) } +# Avoid error messages during CRAN check. +# The reason is that these variables are never declared +# They are mainly column names inferred by Data.table... globalVariables(".") \ No newline at end of file diff --git a/R-package/R/xgb.model.dt.tree.R b/R-package/R/xgb.model.dt.tree.R index 57d14933c..10e1e42d8 100644 --- a/R-package/R/xgb.model.dt.tree.R +++ b/R-package/R/xgb.model.dt.tree.R @@ -166,4 +166,4 @@ xgb.model.dt.tree <- function(feature_names = NULL, filename_dump = NULL, model # Avoid error messages during CRAN check. # The reason is that these variables are never declared # They are mainly column names inferred by Data.table... -globalVariables("ID", "Tree", "Yes", ".", "Feature", "Cover", "Quality", "No", "Gain", "Frequence") \ No newline at end of file +globalVariables(c("ID", "Tree", "Yes", ".", "Feature", "Cover", "Quality", "No", "Gain", "Frequence")) \ No newline at end of file diff --git a/R-package/R/xgb.plot.tree.R b/R-package/R/xgb.plot.tree.R index 3d1fc795c..662ccb21b 100644 --- a/R-package/R/xgb.plot.tree.R +++ b/R-package/R/xgb.plot.tree.R @@ -88,4 +88,7 @@ xgb.plot.tree <- function(feature_names = NULL, filename_dump = NULL, model = NU DiagrammeR(path, width, height) } -globalVariables("Feature", "yesPath", "ID", "Cover", "Quality", "Split", "Yes", "Yes.Feature", "noPath", "No", "No.Feature", ".") \ No newline at end of file +# Avoid error messages during CRAN check. +# The reason is that these variables are never declared +# They are mainly column names inferred by Data.table... +globalVariables(c("Feature", "yesPath", "ID", "Cover", "Quality", "Split", "Yes", "Yes.Feature", "noPath", "No", "No.Feature", ".")) \ No newline at end of file From 7f1aff7858faaf6808799e31cdef8789b9db5b16 Mon Sep 17 00:00:00 2001 From: pommedeterresautee Date: Wed, 21 Jan 2015 22:07:30 +0100 Subject: [PATCH 19/94] forget one variable --- R-package/R/xgb.model.dt.tree.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R-package/R/xgb.model.dt.tree.R b/R-package/R/xgb.model.dt.tree.R index 10e1e42d8..930538c5b 100644 --- a/R-package/R/xgb.model.dt.tree.R +++ b/R-package/R/xgb.model.dt.tree.R @@ -166,4 +166,4 @@ xgb.model.dt.tree <- function(feature_names = NULL, filename_dump = NULL, model # Avoid error messages during CRAN check. # The reason is that these variables are never declared # They are mainly column names inferred by Data.table... -globalVariables(c("ID", "Tree", "Yes", ".", "Feature", "Cover", "Quality", "No", "Gain", "Frequence")) \ No newline at end of file +globalVariables(c("ID", "Tree", "Yes", ".", ".N", "Feature", "Cover", "Quality", "No", "Gain", "Frequence")) \ No newline at end of file From d188c997f0a64627bdbd950090dca722e6f2bd13 Mon Sep 17 00:00:00 2001 From: El Potaeto Date: Wed, 21 Jan 2015 23:56:27 +0100 Subject: [PATCH 20/94] add RStudio parameters to exclusion --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index c6d3ab790..4bf2f8858 100644 --- a/.gitignore +++ b/.gitignore @@ -52,4 +52,5 @@ xgboost.mpi xgboost.mock train* rabit - +.Rbuildignore +R-package.Rproj From f84884431049faeafffccb05c88624920b984eca Mon Sep 17 00:00:00 2001 From: tqchen Date: Sun, 25 Jan 2015 10:05:47 -0800 Subject: [PATCH 21/94] better warning at multiclass, fix cran check --- src/io/page_fmatrix-inl.hpp | 2 +- src/learner/learner-inl.hpp | 13 ++++++++++--- src/learner/objective-inl.hpp | 3 ++- src/utils/matrix_csr.h | 1 - 4 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/io/page_fmatrix-inl.hpp b/src/io/page_fmatrix-inl.hpp index 0527da827..44cb9abdc 100644 --- a/src/io/page_fmatrix-inl.hpp +++ b/src/io/page_fmatrix-inl.hpp @@ -339,7 +339,7 @@ class FMatrixPage : public IFMatrix { } if (ktop % 100000 == 0) { utils::Printf("\r \r"); - utils::Printf("InitCol: %lu rows ", static_cast(ktop)); + utils::Printf("InitCol: %lu rows ", static_cast(ktop)); } } } diff --git a/src/learner/learner-inl.hpp b/src/learner/learner-inl.hpp index 630f8fa20..616cf03e9 100644 --- a/src/learner/learner-inl.hpp +++ b/src/learner/learner-inl.hpp @@ -227,14 +227,19 @@ class BoostLearner : public rabit::ISerializable { */ inline void SaveModel(const char *fname) const { FILE *fp; + bool use_stdout = false;; +#ifndef XGBOOST_STRICT_CXX98_ if (!strcmp(fname, "stdout")) { fp = stdout; - } else { + use_stdout = true; + } else +#endif + { fp = utils::FopenCheck(fname, "wb"); } utils::FileStream fo(fp); std::string header; - if (save_base64 != 0|| fp == stdout) { + if (save_base64 != 0|| use_stdout) { fo.Write("bs64\t", 5); utils::Base64OutStream bout(fp); this->SaveModel(bout); @@ -243,7 +248,9 @@ class BoostLearner : public rabit::ISerializable { fo.Write("binf", 4); this->SaveModel(fo); } - if (fp != stdout) fclose(fp); + if (!use_stdout) { + fclose(fp); + } } /*! * \brief check if data matrix is ready to be used by training, diff --git a/src/learner/objective-inl.hpp b/src/learner/objective-inl.hpp index 7702774f9..9887e7a05 100644 --- a/src/learner/objective-inl.hpp +++ b/src/learner/objective-inl.hpp @@ -206,7 +206,8 @@ class SoftmaxMultiClassObj : public IObjFunction { Softmax(&rec); const unsigned j = i % nstep; int label = static_cast(info.labels[j]); - utils::Check(label < nclass, "SoftmaxMultiClassObj: label exceed num_class"); + utils::Check(label >= 0 && label < nclass, + "SoftmaxMultiClassObj: label must be in [0, num_class)"); const float wt = info.GetWeight(j); for (int k = 0; k < nclass; ++k) { float p = rec[k]; diff --git a/src/utils/matrix_csr.h b/src/utils/matrix_csr.h index bc9479cc3..14e0667ee 100644 --- a/src/utils/matrix_csr.h +++ b/src/utils/matrix_csr.h @@ -208,7 +208,6 @@ struct SparseCSRFileBuilder { fo->Write(BeginPtr(buffer_data), (rptr[end] - rptr[begin]) * sizeof(IndexType)); } } - printf("CSV::begin_dat=%lu\n", begin_data); } protected: inline void WriteBuffer(void) { From 8971f0ff5051580a1c7cdda3682cec91d323c1f3 Mon Sep 17 00:00:00 2001 From: Tong He Date: Sun, 25 Jan 2015 10:21:24 -0800 Subject: [PATCH 22/94] Update xgboost.R --- R-package/R/xgboost.R | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/R-package/R/xgboost.R b/R-package/R/xgboost.R index 02a554f68..39e047e8e 100644 --- a/R-package/R/xgboost.R +++ b/R-package/R/xgboost.R @@ -24,8 +24,7 @@ #' @param verbose If 0, xgboost will stay silent. If 1, xgboost will print #' information of performance. If 2, xgboost will print information of both #' performance and construction progress information -#' @param missing Missing is only used when input is dense matrix, pick a float -# value that represents missing value. Sometime a data use 0 or other extreme value to represents missing values. +#' @param missing Missing is only used when input is dense matrix, pick a float value that represents missing value. Sometime a data use 0 or other extreme value to represents missing values. #' @param ... other parameters to pass to \code{params}. #' #' @details From 33101d5cade2c5d8f4a152127584af8213278a89 Mon Sep 17 00:00:00 2001 From: hetong Date: Sun, 25 Jan 2015 10:31:48 -0800 Subject: [PATCH 23/94] edit document --- R-package/NAMESPACE | 2 +- R-package/man/agaricus.test.Rd | 3 +-- R-package/man/agaricus.train.Rd | 3 +-- R-package/man/getinfo.Rd | 7 +++---- R-package/man/predict-xgb.Booster-method.Rd | 3 +-- R-package/man/setinfo.Rd | 7 +++---- R-package/man/slice.Rd | 7 +++---- R-package/man/xgb.DMatrix.Rd | 3 +-- R-package/man/xgb.DMatrix.save.Rd | 3 +-- R-package/man/xgb.cv.Rd | 3 +-- R-package/man/xgb.dump.Rd | 3 +-- R-package/man/xgb.importance.Rd | 3 +-- R-package/man/xgb.load.Rd | 3 +-- R-package/man/xgb.model.dt.tree.Rd | 3 +-- R-package/man/xgb.plot.tree.Rd | 3 +-- R-package/man/xgb.save.Rd | 3 +-- R-package/man/xgb.train.Rd | 3 +-- R-package/man/xgboost.Rd | 7 +++---- 18 files changed, 26 insertions(+), 43 deletions(-) diff --git a/R-package/NAMESPACE b/R-package/NAMESPACE index d29ad7a18..12225c966 100644 --- a/R-package/NAMESPACE +++ b/R-package/NAMESPACE @@ -1,4 +1,4 @@ -# Generated by roxygen2 (4.1.0): do not edit by hand +# Generated by roxygen2 (4.0.1): do not edit by hand export(getinfo) export(setinfo) diff --git a/R-package/man/agaricus.test.Rd b/R-package/man/agaricus.test.Rd index 556425379..c050d3ecd 100644 --- a/R-package/man/agaricus.test.Rd +++ b/R-package/man/agaricus.test.Rd @@ -1,5 +1,4 @@ -% Generated by roxygen2 (4.1.0): do not edit by hand -% Please edit documentation in R/xgboost.R +% Generated by roxygen2 (4.0.1): do not edit by hand \docType{data} \name{agaricus.test} \alias{agaricus.test} diff --git a/R-package/man/agaricus.train.Rd b/R-package/man/agaricus.train.Rd index 879b3d5df..02571cf54 100644 --- a/R-package/man/agaricus.train.Rd +++ b/R-package/man/agaricus.train.Rd @@ -1,5 +1,4 @@ -% Generated by roxygen2 (4.1.0): do not edit by hand -% Please edit documentation in R/xgboost.R +% Generated by roxygen2 (4.0.1): do not edit by hand \docType{data} \name{agaricus.train} \alias{agaricus.train} diff --git a/R-package/man/getinfo.Rd b/R-package/man/getinfo.Rd index 37e0ad0be..23e3adc84 100644 --- a/R-package/man/getinfo.Rd +++ b/R-package/man/getinfo.Rd @@ -1,5 +1,4 @@ -% Generated by roxygen2 (4.1.0): do not edit by hand -% Please edit documentation in R/getinfo.xgb.DMatrix.R +% Generated by roxygen2 (4.0.1): do not edit by hand \docType{methods} \name{getinfo} \alias{getinfo} @@ -13,9 +12,9 @@ getinfo(object, ...) \arguments{ \item{object}{Object of class "xgb.DMatrix"} -\item{...}{other parameters} - \item{name}{the name of the field to get} + +\item{...}{other parameters} } \description{ Get information of an xgb.DMatrix object diff --git a/R-package/man/predict-xgb.Booster-method.Rd b/R-package/man/predict-xgb.Booster-method.Rd index d8da7975e..66cdfc36c 100644 --- a/R-package/man/predict-xgb.Booster-method.Rd +++ b/R-package/man/predict-xgb.Booster-method.Rd @@ -1,5 +1,4 @@ -% Generated by roxygen2 (4.1.0): do not edit by hand -% Please edit documentation in R/predict.xgb.Booster.R +% Generated by roxygen2 (4.0.1): do not edit by hand \docType{methods} \name{predict,xgb.Booster-method} \alias{predict,xgb.Booster-method} diff --git a/R-package/man/setinfo.Rd b/R-package/man/setinfo.Rd index 4ed262b46..7ea992110 100644 --- a/R-package/man/setinfo.Rd +++ b/R-package/man/setinfo.Rd @@ -1,5 +1,4 @@ -% Generated by roxygen2 (4.1.0): do not edit by hand -% Please edit documentation in R/setinfo.xgb.DMatrix.R +% Generated by roxygen2 (4.0.1): do not edit by hand \docType{methods} \name{setinfo} \alias{setinfo} @@ -13,11 +12,11 @@ setinfo(object, ...) \arguments{ \item{object}{Object of class "xgb.DMatrix"} -\item{...}{other parameters} - \item{name}{the name of the field to get} \item{info}{the specific field of information to set} + +\item{...}{other parameters} } \description{ Set information of an xgb.DMatrix object diff --git a/R-package/man/slice.Rd b/R-package/man/slice.Rd index a7812e886..a749aa8ff 100644 --- a/R-package/man/slice.Rd +++ b/R-package/man/slice.Rd @@ -1,5 +1,4 @@ -% Generated by roxygen2 (4.1.0): do not edit by hand -% Please edit documentation in R/slice.xgb.DMatrix.R +% Generated by roxygen2 (4.0.1): do not edit by hand \docType{methods} \name{slice} \alias{slice} @@ -14,9 +13,9 @@ slice(object, ...) \arguments{ \item{object}{Object of class "xgb.DMatrix"} -\item{...}{other parameters} - \item{idxset}{a integer vector of indices of rows needed} + +\item{...}{other parameters} } \description{ Get a new DMatrix containing the specified rows of diff --git a/R-package/man/xgb.DMatrix.Rd b/R-package/man/xgb.DMatrix.Rd index 86000220f..227fb515f 100644 --- a/R-package/man/xgb.DMatrix.Rd +++ b/R-package/man/xgb.DMatrix.Rd @@ -1,5 +1,4 @@ -% Generated by roxygen2 (4.1.0): do not edit by hand -% Please edit documentation in R/xgb.DMatrix.R +% Generated by roxygen2 (4.0.1): do not edit by hand \name{xgb.DMatrix} \alias{xgb.DMatrix} \title{Contruct xgb.DMatrix object} diff --git a/R-package/man/xgb.DMatrix.save.Rd b/R-package/man/xgb.DMatrix.save.Rd index 6bbc277b3..803de912b 100644 --- a/R-package/man/xgb.DMatrix.save.Rd +++ b/R-package/man/xgb.DMatrix.save.Rd @@ -1,5 +1,4 @@ -% Generated by roxygen2 (4.1.0): do not edit by hand -% Please edit documentation in R/xgb.DMatrix.save.R +% Generated by roxygen2 (4.0.1): do not edit by hand \name{xgb.DMatrix.save} \alias{xgb.DMatrix.save} \title{Save xgb.DMatrix object to binary file} diff --git a/R-package/man/xgb.cv.Rd b/R-package/man/xgb.cv.Rd index 0867134ae..12aaf9bf1 100644 --- a/R-package/man/xgb.cv.Rd +++ b/R-package/man/xgb.cv.Rd @@ -1,5 +1,4 @@ -% Generated by roxygen2 (4.1.0): do not edit by hand -% Please edit documentation in R/xgb.cv.R +% Generated by roxygen2 (4.0.1): do not edit by hand \name{xgb.cv} \alias{xgb.cv} \title{Cross Validation} diff --git a/R-package/man/xgb.dump.Rd b/R-package/man/xgb.dump.Rd index 7958a72e8..d1968217b 100644 --- a/R-package/man/xgb.dump.Rd +++ b/R-package/man/xgb.dump.Rd @@ -1,5 +1,4 @@ -% Generated by roxygen2 (4.1.0): do not edit by hand -% Please edit documentation in R/xgb.dump.R +% Generated by roxygen2 (4.0.1): do not edit by hand \name{xgb.dump} \alias{xgb.dump} \title{Save xgboost model to text file} diff --git a/R-package/man/xgb.importance.Rd b/R-package/man/xgb.importance.Rd index 1b2946729..1588639b4 100644 --- a/R-package/man/xgb.importance.Rd +++ b/R-package/man/xgb.importance.Rd @@ -1,5 +1,4 @@ -% Generated by roxygen2 (4.1.0): do not edit by hand -% Please edit documentation in R/xgb.importance.R +% Generated by roxygen2 (4.0.1): do not edit by hand \name{xgb.importance} \alias{xgb.importance} \title{Show importance of features in a model} diff --git a/R-package/man/xgb.load.Rd b/R-package/man/xgb.load.Rd index 433b38c79..d2c5d94b6 100644 --- a/R-package/man/xgb.load.Rd +++ b/R-package/man/xgb.load.Rd @@ -1,5 +1,4 @@ -% Generated by roxygen2 (4.1.0): do not edit by hand -% Please edit documentation in R/xgb.load.R +% Generated by roxygen2 (4.0.1): do not edit by hand \name{xgb.load} \alias{xgb.load} \title{Load xgboost model from binary file} diff --git a/R-package/man/xgb.model.dt.tree.Rd b/R-package/man/xgb.model.dt.tree.Rd index f91a2afe9..51c965970 100644 --- a/R-package/man/xgb.model.dt.tree.Rd +++ b/R-package/man/xgb.model.dt.tree.Rd @@ -1,5 +1,4 @@ -% Generated by roxygen2 (4.1.0): do not edit by hand -% Please edit documentation in R/xgb.model.dt.tree.R +% Generated by roxygen2 (4.0.1): do not edit by hand \name{xgb.model.dt.tree} \alias{xgb.model.dt.tree} \title{Convert tree model dump to data.table} diff --git a/R-package/man/xgb.plot.tree.Rd b/R-package/man/xgb.plot.tree.Rd index 8aec827ec..dc95dfec0 100644 --- a/R-package/man/xgb.plot.tree.Rd +++ b/R-package/man/xgb.plot.tree.Rd @@ -1,5 +1,4 @@ -% Generated by roxygen2 (4.1.0): do not edit by hand -% Please edit documentation in R/xgb.plot.tree.R +% Generated by roxygen2 (4.0.1): do not edit by hand \name{xgb.plot.tree} \alias{xgb.plot.tree} \title{Plot a boosted tree model} diff --git a/R-package/man/xgb.save.Rd b/R-package/man/xgb.save.Rd index ded444446..0ccdf13da 100644 --- a/R-package/man/xgb.save.Rd +++ b/R-package/man/xgb.save.Rd @@ -1,5 +1,4 @@ -% Generated by roxygen2 (4.1.0): do not edit by hand -% Please edit documentation in R/xgb.save.R +% Generated by roxygen2 (4.0.1): do not edit by hand \name{xgb.save} \alias{xgb.save} \title{Save xgboost model to binary file} diff --git a/R-package/man/xgb.train.Rd b/R-package/man/xgb.train.Rd index 58ef94135..a05e2eeb9 100644 --- a/R-package/man/xgb.train.Rd +++ b/R-package/man/xgb.train.Rd @@ -1,5 +1,4 @@ -% Generated by roxygen2 (4.1.0): do not edit by hand -% Please edit documentation in R/xgb.train.R +% Generated by roxygen2 (4.0.1): do not edit by hand \name{xgb.train} \alias{xgb.train} \title{eXtreme Gradient Boosting Training} diff --git a/R-package/man/xgboost.Rd b/R-package/man/xgboost.Rd index 21b1ad220..dec97f239 100644 --- a/R-package/man/xgboost.Rd +++ b/R-package/man/xgboost.Rd @@ -1,5 +1,4 @@ -% Generated by roxygen2 (4.1.0): do not edit by hand -% Please edit documentation in R/xgboost.R +% Generated by roxygen2 (4.0.1): do not edit by hand \name{xgboost} \alias{xgboost} \title{eXtreme Gradient Boosting (Tree) library} @@ -13,8 +12,6 @@ xgboost(data = NULL, label = NULL, missing = NULL, params = list(), \item{label}{the response variable. User should not set this field,} -\item{missing}{Missing is only used when input is dense matrix, pick a float} - \item{params}{the list of parameters. Commonly used ones are: \itemize{ \item \code{objective} objective function, common ones are @@ -36,6 +33,8 @@ xgboost(data = NULL, label = NULL, missing = NULL, params = list(), information of performance. If 2, xgboost will print information of both performance and construction progress information} +\item{missing}{Missing is only used when input is dense matrix, pick a float value that represents missing value. Sometime a data use 0 or other extreme value to represents missing values.} + \item{...}{other parameters to pass to \code{params}.} } \description{ From f75387f7016f5b21b8d10ec97204475615eec9c9 Mon Sep 17 00:00:00 2001 From: hetong Date: Sun, 25 Jan 2015 10:37:11 -0800 Subject: [PATCH 24/94] update document --- R-package/R/predict.xgb.Booster.R | 4 ++-- R-package/R/xgb.DMatrix.R | 2 +- R-package/R/xgb.cv.R | 4 ++-- R-package/R/xgboost.R | 5 +++-- R-package/man/predict-xgb.Booster-method.Rd | 3 ++- R-package/man/xgb.DMatrix.Rd | 3 ++- R-package/man/xgb.cv.Rd | 3 ++- R-package/man/xgboost.Rd | 6 ++++-- 8 files changed, 18 insertions(+), 12 deletions(-) diff --git a/R-package/R/predict.xgb.Booster.R b/R-package/R/predict.xgb.Booster.R index 49f1ad4f0..1e458e708 100644 --- a/R-package/R/predict.xgb.Booster.R +++ b/R-package/R/predict.xgb.Booster.R @@ -7,8 +7,8 @@ setClass("xgb.Booster") #' @param object Object of class "xgb.Boost" #' @param newdata takes \code{matrix}, \code{dgCMatrix}, local data file or #' \code{xgb.DMatrix}. -#' @param missing Missing is only used when input is dense matrix, pick a float -# value that represents missing value. Sometime a data use 0 or other extreme value to represents missing values. +#' @param missing Missing is only used when input is dense matrix, pick a float +#' value that represents missing value. Sometime a data use 0 or other extreme value to represents missing values. #' @param outputmargin whether the prediction should be shown in the original #' value of sum of functions, when outputmargin=TRUE, the prediction is #' untransformed margin value. In logistic regression, outputmargin=T will diff --git a/R-package/R/xgb.DMatrix.R b/R-package/R/xgb.DMatrix.R index b7a5a9897..8c3ea80bc 100644 --- a/R-package/R/xgb.DMatrix.R +++ b/R-package/R/xgb.DMatrix.R @@ -6,7 +6,7 @@ #' indicating the data file. #' @param info a list of information of the xgb.DMatrix object #' @param missing Missing is only used when input is dense matrix, pick a float -# value that represents missing value. Sometime a data use 0 or other extreme value to represents missing values. +#' value that represents missing value. Sometime a data use 0 or other extreme value to represents missing values. # #' @param ... other information to pass to \code{info}. #' diff --git a/R-package/R/xgb.cv.R b/R-package/R/xgb.cv.R index 2129b418a..ed088df52 100644 --- a/R-package/R/xgb.cv.R +++ b/R-package/R/xgb.cv.R @@ -32,7 +32,7 @@ #' @param nfold number of folds used #' @param label option field, when data is Matrix #' @param missing Missing is only used when input is dense matrix, pick a float -# value that represents missing value. Sometime a data use 0 or other extreme value to represents missing values. +#' value that represents missing value. Sometime a data use 0 or other extreme value to represents missing values. #' @param prediction A logical value indicating whether to return the prediction vector. #' @param showsd \code{boolean}, whether show standard deviation of cross validation #' @param metrics, list of evaluation metrics to be used in corss validation, @@ -134,4 +134,4 @@ xgb.cv <- function(params=list(), data, nrounds, nfold, label = NULL, missing = # Avoid error messages during CRAN check. # The reason is that these variables are never declared # They are mainly column names inferred by Data.table... -globalVariables(".") \ No newline at end of file +globalVariables(".") diff --git a/R-package/R/xgboost.R b/R-package/R/xgboost.R index 39e047e8e..c72c4d5b0 100644 --- a/R-package/R/xgboost.R +++ b/R-package/R/xgboost.R @@ -5,7 +5,7 @@ #' @param data takes \code{matrix}, \code{dgCMatrix}, local data file or #' \code{xgb.DMatrix}. #' @param label the response variable. User should not set this field, -# if data is local data file or \code{xgb.DMatrix}. +#' if data is local data file or \code{xgb.DMatrix}. #' @param params the list of parameters. Commonly used ones are: #' \itemize{ #' \item \code{objective} objective function, common ones are @@ -24,7 +24,8 @@ #' @param verbose If 0, xgboost will stay silent. If 1, xgboost will print #' information of performance. If 2, xgboost will print information of both #' performance and construction progress information -#' @param missing Missing is only used when input is dense matrix, pick a float value that represents missing value. Sometime a data use 0 or other extreme value to represents missing values. +#' @param missing Missing is only used when input is dense matrix, pick a float +#' value that represents missing value. Sometime a data use 0 or other extreme value to represents missing values. #' @param ... other parameters to pass to \code{params}. #' #' @details diff --git a/R-package/man/predict-xgb.Booster-method.Rd b/R-package/man/predict-xgb.Booster-method.Rd index 66cdfc36c..204a8167f 100644 --- a/R-package/man/predict-xgb.Booster-method.Rd +++ b/R-package/man/predict-xgb.Booster-method.Rd @@ -13,7 +13,8 @@ \item{newdata}{takes \code{matrix}, \code{dgCMatrix}, local data file or \code{xgb.DMatrix}.} -\item{missing}{Missing is only used when input is dense matrix, pick a float} +\item{missing}{Missing is only used when input is dense matrix, pick a float +value that represents missing value. Sometime a data use 0 or other extreme value to represents missing values.} \item{outputmargin}{whether the prediction should be shown in the original value of sum of functions, when outputmargin=TRUE, the prediction is diff --git a/R-package/man/xgb.DMatrix.Rd b/R-package/man/xgb.DMatrix.Rd index 227fb515f..31efde687 100644 --- a/R-package/man/xgb.DMatrix.Rd +++ b/R-package/man/xgb.DMatrix.Rd @@ -11,7 +11,8 @@ indicating the data file.} \item{info}{a list of information of the xgb.DMatrix object} -\item{missing}{Missing is only used when input is dense matrix, pick a float} +\item{missing}{Missing is only used when input is dense matrix, pick a float +value that represents missing value. Sometime a data use 0 or other extreme value to represents missing values.} \item{...}{other information to pass to \code{info}.} } diff --git a/R-package/man/xgb.cv.Rd b/R-package/man/xgb.cv.Rd index 12aaf9bf1..149ec392f 100644 --- a/R-package/man/xgb.cv.Rd +++ b/R-package/man/xgb.cv.Rd @@ -31,7 +31,8 @@ xgb.cv(params = list(), data, nrounds, nfold, label = NULL, \item{label}{option field, when data is Matrix} -\item{missing}{Missing is only used when input is dense matrix, pick a float} +\item{missing}{Missing is only used when input is dense matrix, pick a float +value that represents missing value. Sometime a data use 0 or other extreme value to represents missing values.} \item{prediction}{A logical value indicating whether to return the prediction vector.} diff --git a/R-package/man/xgboost.Rd b/R-package/man/xgboost.Rd index dec97f239..035eec9e7 100644 --- a/R-package/man/xgboost.Rd +++ b/R-package/man/xgboost.Rd @@ -10,7 +10,8 @@ xgboost(data = NULL, label = NULL, missing = NULL, params = list(), \item{data}{takes \code{matrix}, \code{dgCMatrix}, local data file or \code{xgb.DMatrix}.} -\item{label}{the response variable. User should not set this field,} +\item{label}{the response variable. User should not set this field, +if data is local data file or \code{xgb.DMatrix}.} \item{params}{the list of parameters. Commonly used ones are: \itemize{ @@ -33,7 +34,8 @@ xgboost(data = NULL, label = NULL, missing = NULL, params = list(), information of performance. If 2, xgboost will print information of both performance and construction progress information} -\item{missing}{Missing is only used when input is dense matrix, pick a float value that represents missing value. Sometime a data use 0 or other extreme value to represents missing values.} +\item{missing}{Missing is only used when input is dense matrix, pick a float +value that represents missing value. Sometime a data use 0 or other extreme value to represents missing values.} \item{...}{other parameters to pass to \code{params}.} } From 52a2b652d3e73ff6251eeca726b450d8ffb9b401 Mon Sep 17 00:00:00 2001 From: El Potaeto Date: Sun, 25 Jan 2015 20:16:56 +0100 Subject: [PATCH 25/94] Documentation: no need to save model in txt... --- R-package/R/xgb.model.dt.tree.R | 3 +-- R-package/man/xgb.model.dt.tree.Rd | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/R-package/R/xgb.model.dt.tree.R b/R-package/R/xgb.model.dt.tree.R index 930538c5b..373f29403 100644 --- a/R-package/R/xgb.model.dt.tree.R +++ b/R-package/R/xgb.model.dt.tree.R @@ -49,10 +49,9 @@ #' #' bst <- xgboost(data = train$data, label = train$label, max.depth = 2, #' eta = 1, nround = 2,objective = "binary:logistic") -#' xgb.dump(bst, 'xgb.model.dump', with.stats = TRUE) #' #' #agaricus.test$data@@Dimnames[[2]] represents the column names of the sparse matrix. -#' xgb.model.dt.tree(agaricus.train$data@@Dimnames[[2]], filename_dump = 'xgb.model.dump') +#' xgb.model.dt.tree(agaricus.train$data@@Dimnames[[2]], model = bst) #' #' @export xgb.model.dt.tree <- function(feature_names = NULL, filename_dump = NULL, model = NULL, text = NULL, n_first_tree = NULL){ diff --git a/R-package/man/xgb.model.dt.tree.Rd b/R-package/man/xgb.model.dt.tree.Rd index f91a2afe9..069e7ad77 100644 --- a/R-package/man/xgb.model.dt.tree.Rd +++ b/R-package/man/xgb.model.dt.tree.Rd @@ -51,9 +51,8 @@ train <- agaricus.train bst <- xgboost(data = train$data, label = train$label, max.depth = 2, eta = 1, nround = 2,objective = "binary:logistic") -xgb.dump(bst, 'xgb.model.dump', with.stats = TRUE) #agaricus.test$data@Dimnames[[2]] represents the column names of the sparse matrix. -xgb.model.dt.tree(agaricus.train$data@Dimnames[[2]], filename_dump = 'xgb.model.dump') +xgb.model.dt.tree(agaricus.train$data@Dimnames[[2]], model = bst) } From 5e941269639a9b9a3322d412ceb46887f540107e Mon Sep 17 00:00:00 2001 From: El Potaeto Date: Sun, 25 Jan 2015 21:07:06 +0100 Subject: [PATCH 26/94] fix mermaid --- R-package/DESCRIPTION | 2 +- R-package/NAMESPACE | 2 +- R-package/R/xgb.plot.tree.R | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/R-package/DESCRIPTION b/R-package/DESCRIPTION index 63ed9581c..26fc3c8ae 100644 --- a/R-package/DESCRIPTION +++ b/R-package/DESCRIPTION @@ -25,4 +25,4 @@ Imports: data.table (>= 1.9.4), magrittr (>= 1.5), stringr, - DiagrammeR + DiagrammeR (>=0.3) diff --git a/R-package/NAMESPACE b/R-package/NAMESPACE index d29ad7a18..92485b9c7 100644 --- a/R-package/NAMESPACE +++ b/R-package/NAMESPACE @@ -18,7 +18,7 @@ exportMethods(predict) import(methods) importClassesFrom(Matrix,dgCMatrix) importClassesFrom(Matrix,dgeMatrix) -importFrom(DiagrammeR,DiagrammeR) +importFrom(DiagrammeR,mermaid) importFrom(data.table,":=") importFrom(data.table,as.data.table) importFrom(data.table,copy) diff --git a/R-package/R/xgb.plot.tree.R b/R-package/R/xgb.plot.tree.R index 662ccb21b..0747f0a14 100644 --- a/R-package/R/xgb.plot.tree.R +++ b/R-package/R/xgb.plot.tree.R @@ -15,7 +15,7 @@ #' @importFrom stringr str_split #' @importFrom stringr str_extract #' @importFrom stringr str_trim -#' @importFrom DiagrammeR DiagrammeR +#' @importFrom DiagrammeR mermaid #' @param feature_names names of each feature as a character vector. Can be extracted from a sparse matrix (see example). If model dump already contains feature names, this argument should be \code{NULL}. #' @param filename_dump the path to the text file storing the model. Model dump must include the gain per feature and per tree (parameter \code{with.stats = T} in function \code{xgb.dump}). Possible to provide a model directly (see \code{model} argument). #' @param model generated by the \code{xgb.train} function. Avoid the creation of a dump file. @@ -85,7 +85,7 @@ xgb.plot.tree <- function(feature_names = NULL, filename_dump = NULL, model = NU no <- allTrees[Feature!="Leaf", c(No)] %>% paste(collapse = ",") %>% paste("class ", ., " redNode", sep = "") path <- allTrees[Feature!="Leaf", c(yesPath, noPath)] %>% .[order(.)] %>% paste(sep = "", collapse = ";") %>% paste("graph LR", .,collapse = "", sep = ";") %>% paste(CSSstyle, yes, no, sep = ";") - DiagrammeR(path, width, height) + mermaid(path, width, height) } # Avoid error messages during CRAN check. From 5188bad8733e578fe9849fbd2ae89e9aee4bc1bc Mon Sep 17 00:00:00 2001 From: hetong007 Date: Sun, 25 Jan 2015 14:16:46 -0800 Subject: [PATCH 27/94] fix cv attr --- R-package/R/slice.xgb.DMatrix.R | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/R-package/R/slice.xgb.DMatrix.R b/R-package/R/slice.xgb.DMatrix.R index 419170a66..b70a8ee92 100644 --- a/R-package/R/slice.xgb.DMatrix.R +++ b/R-package/R/slice.xgb.DMatrix.R @@ -28,6 +28,18 @@ setMethod("slice", signature = "xgb.DMatrix", if (class(object) != "xgb.DMatrix") { stop("slice: first argument dtrain must be xgb.DMatrix") } - ret <- .Call("XGDMatrixSliceDMatrix_R", object, idxset, PACKAGE = "xgboost") + ret <- .Call("XGDMatrixSliceDMatrix_R", object, idxset, + PACKAGE = "xgboost") + + attr_list <- attributes(object) + nr <- xgb.numrow(object) + len <- sapply(attr_list,length) + ind <- which(len==nr) + if (length(ind)>0) { + nms <- names(attr_list)[ind] + for (i in 1:length(ind)) { + attr(ret,nms[i]) <- attr(object,nms[i])[idxset] + } + } return(structure(ret, class = "xgb.DMatrix")) }) From 15dee73795d58351a24068bc59f66876702470fb Mon Sep 17 00:00:00 2001 From: El Potaeto Date: Mon, 26 Jan 2015 00:00:14 +0100 Subject: [PATCH 28/94] change in Description --- R-package/DESCRIPTION | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/R-package/DESCRIPTION b/R-package/DESCRIPTION index 26fc3c8ae..358fec0f3 100644 --- a/R-package/DESCRIPTION +++ b/R-package/DESCRIPTION @@ -3,7 +3,7 @@ Type: Package Title: eXtreme Gradient Boosting Version: 0.3-3 Date: 2014-12-28 -Author: Tianqi Chen , Tong He +Author: Tianqi Chen , Tong He , Michaël Benesty Maintainer: Tong He Description: This package is a R wrapper of xgboost, which is short for eXtreme Gradient Boosting. It is an efficient and scalable implementation of @@ -25,4 +25,4 @@ Imports: data.table (>= 1.9.4), magrittr (>= 1.5), stringr, - DiagrammeR (>=0.3) + DiagrammeR (>= 0.3) From 4266827105300ec3237b0081810b827b6e796c72 Mon Sep 17 00:00:00 2001 From: Tianqi Chen Date: Mon, 26 Jan 2015 09:04:34 -0800 Subject: [PATCH 29/94] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d74292118..949a10ba6 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ xgboost: eXtreme Gradient Boosting ====== An optimized general purpose gradient boosting library. The library is parallelized, and also provides an optimized distributed version. -It implements machine learning algorithm under gradient boosting framework, including generalized linear model and gradient boosted regression tree. +It implements machine learning algorithm under gradient boosting framework, including generalized linear model and gradient boosted regression tree (GBDT). XGBoost can also also distributed and scale to even larger data. Contributors: https://github.com/tqchen/xgboost/graphs/contributors From 97e058dbd725ca5197c5ff913af07bebec7039d8 Mon Sep 17 00:00:00 2001 From: Tianqi Chen Date: Mon, 26 Jan 2015 09:04:55 -0800 Subject: [PATCH 30/94] Update README.md --- multi-node/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/multi-node/README.md b/multi-node/README.md index b94cc2c77..0beb55de7 100644 --- a/multi-node/README.md +++ b/multi-node/README.md @@ -1,6 +1,6 @@ Distributed XGBoost ====== -This folder contains information of Distributed XGBoost. +This folder contains information of Distributed XGBoost (Distributed GBDT). * The distributed version is built on Rabit:[Reliable Allreduce and Broadcast Library](https://github.com/tqchen/rabit) - Rabit is a portable library that provides fault-tolerance for Allreduce calls for distributed machine learning From c34367b2077bdee159efecdbe115e6eb5fd28677 Mon Sep 17 00:00:00 2001 From: tqchen Date: Mon, 26 Jan 2015 10:27:44 -0800 Subject: [PATCH 31/94] add msd --- .gitignore | 2 ++ demo/.gitignore | 1 + demo/yearpredMSD/csv2libsvm.py | 14 ++++++++++++++ demo/yearpredMSD/runexp.sh | 20 ++++++++++++++++++++ 4 files changed, 37 insertions(+) create mode 100644 demo/.gitignore create mode 100755 demo/yearpredMSD/csv2libsvm.py create mode 100755 demo/yearpredMSD/runexp.sh diff --git a/.gitignore b/.gitignore index d454c6d1d..ee5928043 100644 --- a/.gitignore +++ b/.gitignore @@ -45,3 +45,5 @@ Debug *save *csv .Rproj.user +xgboost +xgboost.mock diff --git a/demo/.gitignore b/demo/.gitignore new file mode 100644 index 000000000..e52797d15 --- /dev/null +++ b/demo/.gitignore @@ -0,0 +1 @@ +*.libsvm \ No newline at end of file diff --git a/demo/yearpredMSD/csv2libsvm.py b/demo/yearpredMSD/csv2libsvm.py new file mode 100755 index 000000000..d7c1d15c1 --- /dev/null +++ b/demo/yearpredMSD/csv2libsvm.py @@ -0,0 +1,14 @@ +#!/usr/bin/python +import sys + +if len(sys.argv) < 3: + print 'Usage: ' + print 'convert a all numerical csv to libsvm' + +fo = open(sys.argv[2], 'w') +for l in open(sys.argv[1]): + arr = l.split(',') + fo.write('%s' % arr[0]) + for i in xrange(len(arr) - 1): + fo.write(' %d:%s' % (i, arr[i+1])) +fo.close() diff --git a/demo/yearpredMSD/runexp.sh b/demo/yearpredMSD/runexp.sh new file mode 100755 index 000000000..fa75b837e --- /dev/null +++ b/demo/yearpredMSD/runexp.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +if [ -f YearPredictionMSD.txt ] +then + echo "use existing data to run experiment" +else + echo "getting data from uci, make sure you are connected to internet" + wget https://archive.ics.uci.edu/ml/machine-learning-databases/00203/YearPredictionMSD.txt.zip + unzip YearPredictionMSD.txt.zip +fi +echo "start making data.." +# map feature using indicator encoding, also produce featmap.txt +python csv2libsvm.py YearPredictionMSD.txt yearpredMSD.libsvm +head -n 463715 yearpredMSD.libsvm > yearpredMSD.libsvm.train +tail -n 51630 yearpredMSD.libsvm > yearpredMSD.libsvm.test +echo "finish making the data" +../../xgboost yearpredMSD.conf + + + \ No newline at end of file From e72174f0f86e69576427536ace371c9477b2d738 Mon Sep 17 00:00:00 2001 From: tqchen Date: Mon, 26 Jan 2015 10:29:34 -0800 Subject: [PATCH 32/94] add readme --- demo/yearpredMSD/README.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 demo/yearpredMSD/README.md diff --git a/demo/yearpredMSD/README.md b/demo/yearpredMSD/README.md new file mode 100644 index 000000000..989a1062c --- /dev/null +++ b/demo/yearpredMSD/README.md @@ -0,0 +1,6 @@ +Demonstrating how to use XGBoost on [Year Prediction task of Million Song Dataset](https://archive.ics.uci.edu/ml/datasets/YearPredictionMSD) + +1. Run runexp.sh +```bash +./runexp.sh +``` From a264bc3969ce6b6f9d4b1716b4efe690b6bf319b Mon Sep 17 00:00:00 2001 From: tqchen Date: Mon, 26 Jan 2015 10:30:12 -0800 Subject: [PATCH 33/94] ok --- demo/yearpredMSD/runexp.sh | 3 --- 1 file changed, 3 deletions(-) diff --git a/demo/yearpredMSD/runexp.sh b/demo/yearpredMSD/runexp.sh index fa75b837e..8853c3f20 100755 --- a/demo/yearpredMSD/runexp.sh +++ b/demo/yearpredMSD/runexp.sh @@ -15,6 +15,3 @@ head -n 463715 yearpredMSD.libsvm > yearpredMSD.libsvm.train tail -n 51630 yearpredMSD.libsvm > yearpredMSD.libsvm.test echo "finish making the data" ../../xgboost yearpredMSD.conf - - - \ No newline at end of file From deb4983273f944e6e4dddaa837ec46fa0a6a7ede Mon Sep 17 00:00:00 2001 From: tqchen Date: Mon, 26 Jan 2015 10:40:04 -0800 Subject: [PATCH 34/94] ok --- demo/yearpredMSD/README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/demo/yearpredMSD/README.md b/demo/yearpredMSD/README.md index 989a1062c..3fe35056a 100644 --- a/demo/yearpredMSD/README.md +++ b/demo/yearpredMSD/README.md @@ -4,3 +4,6 @@ Demonstrating how to use XGBoost on [Year Prediction task of Million Song Datase ```bash ./runexp.sh ``` + +You can also use the script to prepare LIBSVM format, and run the [Distributed Version](../../multi-node). +Note that though that normally you only need to use single machine for dataset at this scale, and use distributed version for larger scale dataset. From e06c1da842d691a3ce2f4a93ea7ac86a923f7a7f Mon Sep 17 00:00:00 2001 From: pommedeterresautee Date: Tue, 27 Jan 2015 22:26:57 +0100 Subject: [PATCH 35/94] new plot feature importance function --- R-package/DESCRIPTION | 4 +- R-package/NAMESPACE | 12 ++++++ R-package/R/xgb.plot.importance.R | 60 ++++++++++++++++++++++++++++ R-package/man/xgb.plot.importance.Rd | 40 +++++++++++++++++++ 4 files changed, 115 insertions(+), 1 deletion(-) create mode 100644 R-package/R/xgb.plot.importance.R create mode 100644 R-package/man/xgb.plot.importance.Rd diff --git a/R-package/DESCRIPTION b/R-package/DESCRIPTION index 358fec0f3..59cb4bdd4 100644 --- a/R-package/DESCRIPTION +++ b/R-package/DESCRIPTION @@ -25,4 +25,6 @@ Imports: data.table (>= 1.9.4), magrittr (>= 1.5), stringr, - DiagrammeR (>= 0.3) + DiagrammeR (>= 0.3), + ggplot2(>= 1.0.0), + Ckmeans.1d.dp diff --git a/R-package/NAMESPACE b/R-package/NAMESPACE index 92485b9c7..fab1546a2 100644 --- a/R-package/NAMESPACE +++ b/R-package/NAMESPACE @@ -10,6 +10,7 @@ export(xgb.dump) export(xgb.importance) export(xgb.load) export(xgb.model.dt.tree) +export(xgb.plot.importance) export(xgb.plot.tree) export(xgb.save) export(xgb.train) @@ -18,6 +19,7 @@ exportMethods(predict) import(methods) importClassesFrom(Matrix,dgCMatrix) importClassesFrom(Matrix,dgeMatrix) +importFrom(Ckmeans.1d.dp,Ckmeans.1d.dp) importFrom(DiagrammeR,mermaid) importFrom(data.table,":=") importFrom(data.table,as.data.table) @@ -26,6 +28,16 @@ importFrom(data.table,data.table) importFrom(data.table,rbindlist) importFrom(data.table,set) importFrom(data.table,setnames) +importFrom(ggplot2,aes) +importFrom(ggplot2,coord_flip) +importFrom(ggplot2,element_blank) +importFrom(ggplot2,element_text) +importFrom(ggplot2,geom_bar) +importFrom(ggplot2,ggplot) +importFrom(ggplot2,ggtitle) +importFrom(ggplot2,theme) +importFrom(ggplot2,xlab) +importFrom(ggplot2,ylab) importFrom(magrittr,"%>%") importFrom(magrittr,add) importFrom(magrittr,not) diff --git a/R-package/R/xgb.plot.importance.R b/R-package/R/xgb.plot.importance.R new file mode 100644 index 000000000..b76784036 --- /dev/null +++ b/R-package/R/xgb.plot.importance.R @@ -0,0 +1,60 @@ +#' Plot feature importance bar graph +#' +#' Read a data.table containing feature importance details and plot it. +#' + +#' @importFrom ggplot2 ggplot +#' @importFrom ggplot2 aes +#' @importFrom ggplot2 geom_bar +#' @importFrom ggplot2 coord_flip +#' @importFrom ggplot2 xlab +#' @importFrom ggplot2 ylab +#' @importFrom ggplot2 ggtitle +#' @importFrom ggplot2 theme +#' @importFrom ggplot2 element_text +#' @importFrom ggplot2 element_blank +#' @importFrom Ckmeans.1d.dp Ckmeans.1d.dp +#' @importFrom magrittr %>% +#' @param importance_matrix a \code{data.table} returned by the \code{xgb.importance} function. +#' @param numberOfClusters a \code{numeric} vector containing the min and the max range of the possible number of clusters of bars. +#' +#' @return A \code{ggplot2} bar graph representing each feature by a horizontal bar. Longer is the bar, more important is the feature. Features are classified by importance and clustered by importance. The group is represented through the color of the bar. +#' +#' @details +#' The purpose of this function is to easily represent the importance of each feature of a model. +#' The function return a ggplot graph, therefore each of its characteristic can be overriden (to customize it). +#' In particular you may want to override the title of the graph. To do so, add \code{+ ggtitle("A GRAPH NAME")} next to the value returned by this function. +#' +#' @examples +#' data(agaricus.train, package='xgboost') +#' +#' #Both dataset are list with two items, a sparse matrix and labels +#' #(labels = outcome column which will be learned). +#' #Each column of the sparse Matrix is a feature in one hot encoding format. +#' train <- agaricus.train +#' +#' bst <- xgboost(data = train$data, label = train$label, max.depth = 2, +#' eta = 1, nround = 2,objective = "binary:logistic") +#' +#' #train$data@@Dimnames[[2]] represents the column names of the sparse matrix. +#' importance_matrix <- xgb.importance(train$data@@Dimnames[[2]], model = bst) +#' xgb.plot.importance(importance_matrix) +#' +#' @export +xgb.plot.importance <- function(importance_matrix = NULL, numberOfClusters = c(1:10)){ + if (!"data.table" %in% class(importance_matrix)) { + stop("importance_matrix: Should be a data.table.") + } + + clusters <- suppressWarnings(Ckmeans.1d.dp(importance_matrix[,Gain], numberOfClusters)) + importance_matrix[,"Cluster":=clusters$cluster %>% as.character] + + plot <- ggplot(importance_matrix, aes(x=reorder(Feature, Gain), y = Gain, width= 0.05), environment = environment())+ geom_bar(aes(fill=Cluster), stat="identity", position="identity") + coord_flip() + xlab("Features") + ylab("Gain") + ggtitle("Feature importance") + theme(plot.title = element_text(lineheight=.9, face="bold"), panel.grid.major.y = element_blank() ) + + return(plot) +} + +# Avoid error messages during CRAN check. +# The reason is that these variables are never declared +# They are mainly column names inferred by Data.table... +globalVariables(c("Feature","Gain")) \ No newline at end of file diff --git a/R-package/man/xgb.plot.importance.Rd b/R-package/man/xgb.plot.importance.Rd new file mode 100644 index 000000000..5a8dab5c5 --- /dev/null +++ b/R-package/man/xgb.plot.importance.Rd @@ -0,0 +1,40 @@ +% Generated by roxygen2 (4.1.0): do not edit by hand +% Please edit documentation in R/xgb.plot.importance.R +\name{xgb.plot.importance} +\alias{xgb.plot.importance} +\title{Plot feature importance bar graph} +\usage{ +xgb.plot.importance(importance_matrix = NULL, numberOfClusters = c(1:10)) +} +\arguments{ +\item{importance_matrix}{a \code{data.table} returned by the \code{xgb.importance} function.} + +\item{numberOfClusters}{a \code{numeric} vector containing the min and the max range of the possible number of clusters of bars.} +} +\value{ +A \code{ggplot2} bar graph representing each feature by a horizontal bar. Longer is the bar, more important is the feature. Features are classified by importance and clustered by importance. The group is represented through the color of the bar. +} +\description{ +Read a data.table containing feature importance details and plot it. +} +\details{ +The purpose of this function is to easily represent the importance of each feature of a model. +The function return a ggplot graph, therefore each of its characteristic can be overriden (to customize it). +In particular you may want to override the title of the graph. To do so, add \code{+ ggtitle("A GRAPH NAME")} next to the value returned by this function. +} +\examples{ +data(agaricus.train, package='xgboost') + +#Both dataset are list with two items, a sparse matrix and labels +#(labels = outcome column which will be learned). +#Each column of the sparse Matrix is a feature in one hot encoding format. +train <- agaricus.train + +bst <- xgboost(data = train$data, label = train$label, max.depth = 2, + eta = 1, nround = 2,objective = "binary:logistic") + +#train$data@Dimnames[[2]] represents the column names of the sparse matrix. +importance_matrix <- xgb.importance(train$data@Dimnames[[2]], model = bst) +xgb.plot.importance(importance_matrix) +} + From 5687af9774e397465ba51ed697978389848263bd Mon Sep 17 00:00:00 2001 From: pommedeterresautee Date: Tue, 27 Jan 2015 22:29:29 +0100 Subject: [PATCH 36/94] fix error message during check --- R-package/R/xgb.plot.importance.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R-package/R/xgb.plot.importance.R b/R-package/R/xgb.plot.importance.R index b76784036..e59cdb484 100644 --- a/R-package/R/xgb.plot.importance.R +++ b/R-package/R/xgb.plot.importance.R @@ -57,4 +57,4 @@ xgb.plot.importance <- function(importance_matrix = NULL, numberOfClusters = c(1 # Avoid error messages during CRAN check. # The reason is that these variables are never declared # They are mainly column names inferred by Data.table... -globalVariables(c("Feature","Gain")) \ No newline at end of file +globalVariables(c("Feature","Gain", "Cluster")) \ No newline at end of file From d6ef74386d531142b6ae97241a1f8fe47c4c1f8c Mon Sep 17 00:00:00 2001 From: pommedeterresautee Date: Tue, 27 Jan 2015 22:36:35 +0100 Subject: [PATCH 37/94] ... --- R-package/R/xgb.plot.importance.R | 1 - 1 file changed, 1 deletion(-) diff --git a/R-package/R/xgb.plot.importance.R b/R-package/R/xgb.plot.importance.R index e59cdb484..627266a7e 100644 --- a/R-package/R/xgb.plot.importance.R +++ b/R-package/R/xgb.plot.importance.R @@ -2,7 +2,6 @@ #' #' Read a data.table containing feature importance details and plot it. #' - #' @importFrom ggplot2 ggplot #' @importFrom ggplot2 aes #' @importFrom ggplot2 geom_bar From 3e0fba392d34771ad0f1d7eaf4cb79d8eefaae55 Mon Sep 17 00:00:00 2001 From: tqchen Date: Tue, 27 Jan 2015 16:29:52 -0800 Subject: [PATCH 38/94] fix the integer overflow --- R-package/src/xgboost_R.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/R-package/src/xgboost_R.cpp b/R-package/src/xgboost_R.cpp index b4757542d..f1958c709 100644 --- a/R-package/src/xgboost_R.cpp +++ b/R-package/src/xgboost_R.cpp @@ -71,8 +71,8 @@ extern "C" { SEXP missing) { _WrapperBegin(); SEXP dim = getAttrib(mat, R_DimSymbol); - int nrow = INTEGER(dim)[0]; - int ncol = INTEGER(dim)[1]; + bst_ulong nrow = static_cast(INTEGER(dim)[0]); + bst_ulong ncol = static_cast(INTEGER(dim)[1]); double *din = REAL(mat); std::vector data(nrow * ncol); #pragma omp parallel for schedule(static) From 16db3ce6207f174ed6065a566794b80eb522deb9 Mon Sep 17 00:00:00 2001 From: tqchen Date: Tue, 27 Jan 2015 16:31:53 -0800 Subject: [PATCH 39/94] quick fix --- R-package/src/xgboost_R.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/R-package/src/xgboost_R.cpp b/R-package/src/xgboost_R.cpp index f1958c709..aa17b30cc 100644 --- a/R-package/src/xgboost_R.cpp +++ b/R-package/src/xgboost_R.cpp @@ -76,8 +76,8 @@ extern "C" { double *din = REAL(mat); std::vector data(nrow * ncol); #pragma omp parallel for schedule(static) - for (int i = 0; i < nrow; ++i) { - for (int j = 0; j < ncol; ++j) { + for (bst_omp_uint i = 0; i < nrow; ++i) { + for (bst_ulong j = 0; j < ncol; ++j) { data[i * ncol +j] = din[i + nrow * j]; } } From 170dcc49be79b662898b24f881a2a84a8916146c Mon Sep 17 00:00:00 2001 From: El Potaeto Date: Wed, 28 Jan 2015 21:42:58 +0100 Subject: [PATCH 40/94] doc --- R-package/NAMESPACE | 2 +- R-package/man/agaricus.test.Rd | 3 ++- R-package/man/agaricus.train.Rd | 3 ++- R-package/man/getinfo.Rd | 7 ++++--- R-package/man/predict-xgb.Booster-method.Rd | 3 ++- R-package/man/setinfo.Rd | 7 ++++--- R-package/man/slice.Rd | 7 ++++--- R-package/man/xgb.DMatrix.Rd | 3 ++- R-package/man/xgb.DMatrix.save.Rd | 3 ++- R-package/man/xgb.cv.Rd | 3 ++- R-package/man/xgb.dump.Rd | 3 ++- R-package/man/xgb.importance.Rd | 3 ++- R-package/man/xgb.load.Rd | 3 ++- R-package/man/xgb.model.dt.tree.Rd | 3 ++- R-package/man/xgb.plot.tree.Rd | 3 ++- R-package/man/xgb.save.Rd | 3 ++- R-package/man/xgb.train.Rd | 3 ++- R-package/man/xgboost.Rd | 9 +++++---- 18 files changed, 44 insertions(+), 27 deletions(-) diff --git a/R-package/NAMESPACE b/R-package/NAMESPACE index a6a0f1799..fab1546a2 100644 --- a/R-package/NAMESPACE +++ b/R-package/NAMESPACE @@ -1,4 +1,4 @@ -# Generated by roxygen2 (4.0.1): do not edit by hand +# Generated by roxygen2 (4.1.0): do not edit by hand export(getinfo) export(setinfo) diff --git a/R-package/man/agaricus.test.Rd b/R-package/man/agaricus.test.Rd index c050d3ecd..556425379 100644 --- a/R-package/man/agaricus.test.Rd +++ b/R-package/man/agaricus.test.Rd @@ -1,4 +1,5 @@ -% Generated by roxygen2 (4.0.1): do not edit by hand +% Generated by roxygen2 (4.1.0): do not edit by hand +% Please edit documentation in R/xgboost.R \docType{data} \name{agaricus.test} \alias{agaricus.test} diff --git a/R-package/man/agaricus.train.Rd b/R-package/man/agaricus.train.Rd index 02571cf54..879b3d5df 100644 --- a/R-package/man/agaricus.train.Rd +++ b/R-package/man/agaricus.train.Rd @@ -1,4 +1,5 @@ -% Generated by roxygen2 (4.0.1): do not edit by hand +% Generated by roxygen2 (4.1.0): do not edit by hand +% Please edit documentation in R/xgboost.R \docType{data} \name{agaricus.train} \alias{agaricus.train} diff --git a/R-package/man/getinfo.Rd b/R-package/man/getinfo.Rd index 23e3adc84..37e0ad0be 100644 --- a/R-package/man/getinfo.Rd +++ b/R-package/man/getinfo.Rd @@ -1,4 +1,5 @@ -% Generated by roxygen2 (4.0.1): do not edit by hand +% Generated by roxygen2 (4.1.0): do not edit by hand +% Please edit documentation in R/getinfo.xgb.DMatrix.R \docType{methods} \name{getinfo} \alias{getinfo} @@ -12,9 +13,9 @@ getinfo(object, ...) \arguments{ \item{object}{Object of class "xgb.DMatrix"} -\item{name}{the name of the field to get} - \item{...}{other parameters} + +\item{name}{the name of the field to get} } \description{ Get information of an xgb.DMatrix object diff --git a/R-package/man/predict-xgb.Booster-method.Rd b/R-package/man/predict-xgb.Booster-method.Rd index 204a8167f..17d0e5a62 100644 --- a/R-package/man/predict-xgb.Booster-method.Rd +++ b/R-package/man/predict-xgb.Booster-method.Rd @@ -1,4 +1,5 @@ -% Generated by roxygen2 (4.0.1): do not edit by hand +% Generated by roxygen2 (4.1.0): do not edit by hand +% Please edit documentation in R/predict.xgb.Booster.R \docType{methods} \name{predict,xgb.Booster-method} \alias{predict,xgb.Booster-method} diff --git a/R-package/man/setinfo.Rd b/R-package/man/setinfo.Rd index 7ea992110..4ed262b46 100644 --- a/R-package/man/setinfo.Rd +++ b/R-package/man/setinfo.Rd @@ -1,4 +1,5 @@ -% Generated by roxygen2 (4.0.1): do not edit by hand +% Generated by roxygen2 (4.1.0): do not edit by hand +% Please edit documentation in R/setinfo.xgb.DMatrix.R \docType{methods} \name{setinfo} \alias{setinfo} @@ -12,11 +13,11 @@ setinfo(object, ...) \arguments{ \item{object}{Object of class "xgb.DMatrix"} +\item{...}{other parameters} + \item{name}{the name of the field to get} \item{info}{the specific field of information to set} - -\item{...}{other parameters} } \description{ Set information of an xgb.DMatrix object diff --git a/R-package/man/slice.Rd b/R-package/man/slice.Rd index a749aa8ff..a7812e886 100644 --- a/R-package/man/slice.Rd +++ b/R-package/man/slice.Rd @@ -1,4 +1,5 @@ -% Generated by roxygen2 (4.0.1): do not edit by hand +% Generated by roxygen2 (4.1.0): do not edit by hand +% Please edit documentation in R/slice.xgb.DMatrix.R \docType{methods} \name{slice} \alias{slice} @@ -13,9 +14,9 @@ slice(object, ...) \arguments{ \item{object}{Object of class "xgb.DMatrix"} -\item{idxset}{a integer vector of indices of rows needed} - \item{...}{other parameters} + +\item{idxset}{a integer vector of indices of rows needed} } \description{ Get a new DMatrix containing the specified rows of diff --git a/R-package/man/xgb.DMatrix.Rd b/R-package/man/xgb.DMatrix.Rd index 31efde687..ea644a291 100644 --- a/R-package/man/xgb.DMatrix.Rd +++ b/R-package/man/xgb.DMatrix.Rd @@ -1,4 +1,5 @@ -% Generated by roxygen2 (4.0.1): do not edit by hand +% Generated by roxygen2 (4.1.0): do not edit by hand +% Please edit documentation in R/xgb.DMatrix.R \name{xgb.DMatrix} \alias{xgb.DMatrix} \title{Contruct xgb.DMatrix object} diff --git a/R-package/man/xgb.DMatrix.save.Rd b/R-package/man/xgb.DMatrix.save.Rd index 803de912b..6bbc277b3 100644 --- a/R-package/man/xgb.DMatrix.save.Rd +++ b/R-package/man/xgb.DMatrix.save.Rd @@ -1,4 +1,5 @@ -% Generated by roxygen2 (4.0.1): do not edit by hand +% Generated by roxygen2 (4.1.0): do not edit by hand +% Please edit documentation in R/xgb.DMatrix.save.R \name{xgb.DMatrix.save} \alias{xgb.DMatrix.save} \title{Save xgb.DMatrix object to binary file} diff --git a/R-package/man/xgb.cv.Rd b/R-package/man/xgb.cv.Rd index 149ec392f..75b31cee1 100644 --- a/R-package/man/xgb.cv.Rd +++ b/R-package/man/xgb.cv.Rd @@ -1,4 +1,5 @@ -% Generated by roxygen2 (4.0.1): do not edit by hand +% Generated by roxygen2 (4.1.0): do not edit by hand +% Please edit documentation in R/xgb.cv.R \name{xgb.cv} \alias{xgb.cv} \title{Cross Validation} diff --git a/R-package/man/xgb.dump.Rd b/R-package/man/xgb.dump.Rd index d1968217b..7958a72e8 100644 --- a/R-package/man/xgb.dump.Rd +++ b/R-package/man/xgb.dump.Rd @@ -1,4 +1,5 @@ -% Generated by roxygen2 (4.0.1): do not edit by hand +% Generated by roxygen2 (4.1.0): do not edit by hand +% Please edit documentation in R/xgb.dump.R \name{xgb.dump} \alias{xgb.dump} \title{Save xgboost model to text file} diff --git a/R-package/man/xgb.importance.Rd b/R-package/man/xgb.importance.Rd index 1588639b4..1b2946729 100644 --- a/R-package/man/xgb.importance.Rd +++ b/R-package/man/xgb.importance.Rd @@ -1,4 +1,5 @@ -% Generated by roxygen2 (4.0.1): do not edit by hand +% Generated by roxygen2 (4.1.0): do not edit by hand +% Please edit documentation in R/xgb.importance.R \name{xgb.importance} \alias{xgb.importance} \title{Show importance of features in a model} diff --git a/R-package/man/xgb.load.Rd b/R-package/man/xgb.load.Rd index d2c5d94b6..433b38c79 100644 --- a/R-package/man/xgb.load.Rd +++ b/R-package/man/xgb.load.Rd @@ -1,4 +1,5 @@ -% Generated by roxygen2 (4.0.1): do not edit by hand +% Generated by roxygen2 (4.1.0): do not edit by hand +% Please edit documentation in R/xgb.load.R \name{xgb.load} \alias{xgb.load} \title{Load xgboost model from binary file} diff --git a/R-package/man/xgb.model.dt.tree.Rd b/R-package/man/xgb.model.dt.tree.Rd index 3ac938a9b..069e7ad77 100644 --- a/R-package/man/xgb.model.dt.tree.Rd +++ b/R-package/man/xgb.model.dt.tree.Rd @@ -1,4 +1,5 @@ -% Generated by roxygen2 (4.0.1): do not edit by hand +% Generated by roxygen2 (4.1.0): do not edit by hand +% Please edit documentation in R/xgb.model.dt.tree.R \name{xgb.model.dt.tree} \alias{xgb.model.dt.tree} \title{Convert tree model dump to data.table} diff --git a/R-package/man/xgb.plot.tree.Rd b/R-package/man/xgb.plot.tree.Rd index dc95dfec0..8aec827ec 100644 --- a/R-package/man/xgb.plot.tree.Rd +++ b/R-package/man/xgb.plot.tree.Rd @@ -1,4 +1,5 @@ -% Generated by roxygen2 (4.0.1): do not edit by hand +% Generated by roxygen2 (4.1.0): do not edit by hand +% Please edit documentation in R/xgb.plot.tree.R \name{xgb.plot.tree} \alias{xgb.plot.tree} \title{Plot a boosted tree model} diff --git a/R-package/man/xgb.save.Rd b/R-package/man/xgb.save.Rd index 0ccdf13da..ded444446 100644 --- a/R-package/man/xgb.save.Rd +++ b/R-package/man/xgb.save.Rd @@ -1,4 +1,5 @@ -% Generated by roxygen2 (4.0.1): do not edit by hand +% Generated by roxygen2 (4.1.0): do not edit by hand +% Please edit documentation in R/xgb.save.R \name{xgb.save} \alias{xgb.save} \title{Save xgboost model to binary file} diff --git a/R-package/man/xgb.train.Rd b/R-package/man/xgb.train.Rd index a05e2eeb9..58ef94135 100644 --- a/R-package/man/xgb.train.Rd +++ b/R-package/man/xgb.train.Rd @@ -1,4 +1,5 @@ -% Generated by roxygen2 (4.0.1): do not edit by hand +% Generated by roxygen2 (4.1.0): do not edit by hand +% Please edit documentation in R/xgb.train.R \name{xgb.train} \alias{xgb.train} \title{eXtreme Gradient Boosting Training} diff --git a/R-package/man/xgboost.Rd b/R-package/man/xgboost.Rd index 035eec9e7..39364c64e 100644 --- a/R-package/man/xgboost.Rd +++ b/R-package/man/xgboost.Rd @@ -1,4 +1,5 @@ -% Generated by roxygen2 (4.0.1): do not edit by hand +% Generated by roxygen2 (4.1.0): do not edit by hand +% Please edit documentation in R/xgboost.R \name{xgboost} \alias{xgboost} \title{eXtreme Gradient Boosting (Tree) library} @@ -13,6 +14,9 @@ xgboost(data = NULL, label = NULL, missing = NULL, params = list(), \item{label}{the response variable. User should not set this field, if data is local data file or \code{xgb.DMatrix}.} +\item{missing}{Missing is only used when input is dense matrix, pick a float +value that represents missing value. Sometime a data use 0 or other extreme value to represents missing values.} + \item{params}{the list of parameters. Commonly used ones are: \itemize{ \item \code{objective} objective function, common ones are @@ -34,9 +38,6 @@ if data is local data file or \code{xgb.DMatrix}.} information of performance. If 2, xgboost will print information of both performance and construction progress information} -\item{missing}{Missing is only used when input is dense matrix, pick a float -value that represents missing value. Sometime a data use 0 or other extreme value to represents missing values.} - \item{...}{other parameters to pass to \code{params}.} } \description{ From f71aa2874c9cfb264f2e13a1abeddf5169e43bbb Mon Sep 17 00:00:00 2001 From: El Potaeto Date: Wed, 28 Jan 2015 21:43:18 +0100 Subject: [PATCH 41/94] Vignette, 1st version --- R-package/vignettes/discoverYourData.Rmd | 153 +++++++++++++++++++++++ 1 file changed, 153 insertions(+) create mode 100644 R-package/vignettes/discoverYourData.Rmd diff --git a/R-package/vignettes/discoverYourData.Rmd b/R-package/vignettes/discoverYourData.Rmd new file mode 100644 index 000000000..1be5ef8ae --- /dev/null +++ b/R-package/vignettes/discoverYourData.Rmd @@ -0,0 +1,153 @@ +--- +title: "Understand your dataset with XGBoost" +date: "Wednesday, January 28, 2015" +output: html_document +--- + +The purpose of this Vignette is to show you how to use **Xgboost** to discover and better understand your own dataset. + +You may know Xgboost as a state of the art tool to build some kind of Machine learning models. It has been used to win several [Kaggle](http://www.kaggle.com/) competition. + +During these competition, the purpose is to make prediction. This Vignette is not about showing you how to predict anything. The purpose of this document is to explain *how to use **Xgboost** to understand the link between the features of your data and an outcome*. + +For the purpose of this tutorial we will first load the required packages. + +Note that **VCD** is used for one of its embedded dataset only. + +```{r libLoading, results='hold', message=F, warning=F} +require(xgboost) +require(Matrix) +require(data.table) +if (!require(vcd)) install.packages('vcd') +``` + +According to its documentation, **Xgboost** works only on *numbers*. + +Sometimes the dataset we have to work on have *categorical* data. + +A *categorical* variable is one which have a fixed number of different values. By exemple, if for each observation a variable called *Colour* can have only *red*, *blue* or *green* as value, it is a *categorical* variable. + +In **R**, *categorical* variable is called *Factor*. +Type `?factor` in console for more information. + +In this demo we will see how to transform a dense dataframe with *categorical* variables to a sparse matrix before analyzing it in Xgboost. +The method we are going to see is usually called "one hot encoding". + +#load Arthritis dataset in memory. +```{r, results='hide'} +data(Arthritis) +``` +create a copy of the dataset with data.table package (data.table is 100% compliant with R dataframe but its syntax is a lot more consistent and its performance are really good). +```{r} +df <- data.table(Arthritis, keep.rownames = F) +``` + +Let's have a look to the data.table +```{r} +print(df[1:10]) +``` + + +2 columns have factor type, one has ordinal type (ordinal variable is a categorical variable with values wich can be ordered, here: None > Some > Marked). +```{r} +str(df) +``` + + +Let's add some new categorical features to see if it helps. Of course these feature are highly correlated to the Age feature. Usually it's not a good thing in ML, but Tree algorithms (including boosted trees) are able to select the best features, even in case of highly correlated features. + +For the first feature we create groups of age by rounding the real age. Note that we transform it to factor (categorical data) so the algorithm treat them as independant values. +```{r} +df[,AgeDiscret:= as.factor(round(Age/10,0))][1:10] +``` + + +Here is an even stronger simplification of the real age with an arbitrary split at 30 years old. I choose this value based on nothing. We will see later if simplifying the information based on arbitrary values is a good strategy (I am sure you already have an idea of how well it will work!). +```{r} +df[,AgeCat:= as.factor(ifelse(Age > 30, "Old", "Young"))][1:10] +``` + + +We remove ID as there is nothing to learn from this feature (it will just add some noise as the dataset is small). +```{r, results='hide'} +df[,ID:=NULL] +``` + + +List the different values for the column Treatment: Placebo, Treated. +```{r} +print(levels(df[,Treatment])) +``` + + +Next step, we will transform the categorical data to dummy variables. +This method is also called one hot encoding. +The purpose is to transform each value of each categorical feature in one binary feature. +# +Let's take, the column Treatment will be replaced by two columns, Placebo, and Treated. Each of them will be binary. For example an observation which had the value Placebo in column Treatment before the transformation will have, after the transformation, the value 1 in the new column Placebo and the value 0 in the new column Treated. +# +Formulae Improved~.-1 used below means transform all categorical features but column Improved to binary values. +Column Improved is excluded because it will be our output column, the one we want to predict. +```{r, warning=FALSE,message=FALSE} +sparse_matrix <- sparse.model.matrix(Improved~.-1, data = df) +print(sparse_matrix[1:10,]) + +``` + +Create the output vector (not sparse) +1. Set, for all rows, field in Y column to 0; +2. set Y to 1 when Improved == Marked; +3. Return Y column +```{r} +output_vector = df[,Y:=0][Improved == "Marked",Y:=1][,Y] +``` + + +Following is the same process as other demo +```{r} +bst <- xgboost(data = sparse_matrix, label = output_vector, max.depth = 9, + eta = 1, nround = 10,objective = "binary:logistic") +xgb.dump(bst, 'xgb.model.dump', with.stats = T) + +``` + +sparse_matrix@Dimnames[[2]] represents the column names of the sparse matrix. +```{r} +importance <- xgb.importance(sparse_matrix@Dimnames[[2]], 'xgb.model.dump') +print(importance) + +``` + +According to the matrix below, the most important feature in this dataset to predict if the treatment will work is the Age. The second most important feature is having received a placebo or not. The sex is third. Then we see our generated features (AgeDiscret). We can see that their contribution is very low (Gain column). + +Does these results make sense? +Let's check some Chi2 between each of these features and the outcome. + +```{r} +print(chisq.test(df$Age, df$Y)) +``` + +Pearson correlation between Age and illness disapearing is 35 + +```{r} +print(chisq.test(df$AgeDiscret, df$Y)) +``` + +Our first simplification of Age gives a Pearson correlation of 8. + +```{r} +print(chisq.test(df$AgeCat, df$Y)) +xgb.plot.importance(importance_matrix = importance) +``` + +The perfectly random split I did between young and old at 30 years old have a low correlation of 2. It's a result we may expect as may be in my mind > 30 years is being old (I am 32 and starting feeling old, this may explain that), but for the illness we are studying, the age to be vulnerable is not the same. Don't let your "gut" lower the quality of your model. In *data science* expression, there is the word *science* :-) + +As you can see, in general destroying information by simplying it won't improve your model. Chi2 just demonstrates that. But in more complex cases, creating a new feature based on existing one which makes link with the outcome more obvious may help the algorithm and improve the model. The case studied here is not enough complex to show that. Check Kaggle forum for some challenging datasets. +However it's almost always worse when you add some arbitrary rules. +Moreover, you can notice that even if we have added some not useful new features highly correlated with other features, the boosting tree algorithm have been able to choose the best one, which in this case is the Age. Linear model may not be that strong in these scenario. + +```{r} +xgb.plot.tree(sparse_matrix@Dimnames[[2]], model = bst, n_first_tree = 1) +``` + + From 7ec17038f0d7196ea8e618a0fd55325e5cdff97b Mon Sep 17 00:00:00 2001 From: El Potaeto Date: Thu, 29 Jan 2015 10:30:50 +0100 Subject: [PATCH 42/94] improve text of the Vignette --- R-package/vignettes/discoverYourData.Rmd | 187 ++++++++++++++++------- 1 file changed, 130 insertions(+), 57 deletions(-) diff --git a/R-package/vignettes/discoverYourData.Rmd b/R-package/vignettes/discoverYourData.Rmd index 1be5ef8ae..cbb3d49f3 100644 --- a/R-package/vignettes/discoverYourData.Rmd +++ b/R-package/vignettes/discoverYourData.Rmd @@ -1,153 +1,226 @@ --- -title: "Understand your dataset with XGBoost" +title: "Understand your dataset with Xgboost" date: "Wednesday, January 28, 2015" -output: html_document +output: + html_document: + number_sections: yes + toc: yes --- +Introduction +============ + The purpose of this Vignette is to show you how to use **Xgboost** to discover and better understand your own dataset. -You may know Xgboost as a state of the art tool to build some kind of Machine learning models. It has been used to win several [Kaggle](http://www.kaggle.com/) competition. +You may know **Xgboost** as a state of the art tool to build some kind of Machine learning models. It has been used to win several [Kaggle](http://www.kaggle.com/) competition ([more information](https://github.com/tqchen/xgboost)). During these competition, the purpose is to make prediction. This Vignette is not about showing you how to predict anything. The purpose of this document is to explain *how to use **Xgboost** to understand the link between the features of your data and an outcome*. For the purpose of this tutorial we will first load the required packages. -Note that **VCD** is used for one of its embedded dataset only. - ```{r libLoading, results='hold', message=F, warning=F} require(xgboost) require(Matrix) require(data.table) if (!require(vcd)) install.packages('vcd') ``` +*Note that **VCD** is used for one of its embedded dataset only (and not for its own functions).* -According to its documentation, **Xgboost** works only on *numbers*. +Preparation of the dataset +========================== + +According to its documentation, **Xgboost** works only on `numeric` variables. Sometimes the dataset we have to work on have *categorical* data. A *categorical* variable is one which have a fixed number of different values. By exemple, if for each observation a variable called *Colour* can have only *red*, *blue* or *green* as value, it is a *categorical* variable. -In **R**, *categorical* variable is called *Factor*. +In **R**, *categorical* variable is called `factor`. Type `?factor` in console for more information. -In this demo we will see how to transform a dense dataframe with *categorical* variables to a sparse matrix before analyzing it in Xgboost. -The method we are going to see is usually called "one hot encoding". +In this demo we will see how to transform a dense dataframe with *categorical* variables to a sparse matrix before analyzing it in **Xgboost**. + +The method we are going to see is usually called [one hot encoding](http://en.wikipedia.org/wiki/One-hot). + +The first step is to load Arthritis dataset in memory and create a copy of the dataset with `data.table` package (`data.table` is 100% compliant with **R** dataframe but its syntax is a lot more consistent and its performance are really good). -#load Arthritis dataset in memory. ```{r, results='hide'} data(Arthritis) -``` -create a copy of the dataset with data.table package (data.table is 100% compliant with R dataframe but its syntax is a lot more consistent and its performance are really good). -```{r} df <- data.table(Arthritis, keep.rownames = F) ``` -Let's have a look to the data.table +Let's have a look to the 10 first lines of the `data.table`: + ```{r} print(df[1:10]) ``` +Now we will check the format of each column. -2 columns have factor type, one has ordinal type (ordinal variable is a categorical variable with values wich can be ordered, here: None > Some > Marked). ```{r} str(df) ``` +2 columns have `factor` type, one has `ordinal` type (`ordinal` variable is a categorical variable with values wich can be ordered, here: `None` > `Some` > `Marked`). -Let's add some new categorical features to see if it helps. Of course these feature are highly correlated to the Age feature. Usually it's not a good thing in ML, but Tree algorithms (including boosted trees) are able to select the best features, even in case of highly correlated features. +Let's add some new categorical features to see if it helps. + +Of course these feature are highly correlated to the Age feature. Usually it's not a good thing in ML, but tree algorithms (including boosted trees) are able to select the best features, even in case of highly correlated features. + +For the first feature we create groups of age by rounding the real age. Note that we transform it to `factor` so the algorithm treat them as independant values. -For the first feature we create groups of age by rounding the real age. Note that we transform it to factor (categorical data) so the algorithm treat them as independant values. ```{r} df[,AgeDiscret:= as.factor(round(Age/10,0))][1:10] ``` +Followinf is an even stronger simplification of the real age with an arbitrary split at 30 years old. I choose this value **based on nothing**. We will see later if simplifying the information based on arbitrary values is a good strategy (I am sure you already have an idea of how well it will work!). -Here is an even stronger simplification of the real age with an arbitrary split at 30 years old. I choose this value based on nothing. We will see later if simplifying the information based on arbitrary values is a good strategy (I am sure you already have an idea of how well it will work!). ```{r} df[,AgeCat:= as.factor(ifelse(Age > 30, "Old", "Young"))][1:10] ``` - We remove ID as there is nothing to learn from this feature (it will just add some noise as the dataset is small). + ```{r, results='hide'} df[,ID:=NULL] ``` +Let's list the different values for the column Treatment. -List the different values for the column Treatment: Placebo, Treated. ```{r} print(levels(df[,Treatment])) ``` Next step, we will transform the categorical data to dummy variables. -This method is also called one hot encoding. -The purpose is to transform each value of each categorical feature in one binary feature. -# -Let's take, the column Treatment will be replaced by two columns, Placebo, and Treated. Each of them will be binary. For example an observation which had the value Placebo in column Treatment before the transformation will have, after the transformation, the value 1 in the new column Placebo and the value 0 in the new column Treated. -# -Formulae Improved~.-1 used below means transform all categorical features but column Improved to binary values. +This is the [one hot encoding](http://en.wikipedia.org/wiki/One-hot) part. +The purpose is to transform each value of each *categorical* feature in a binary feature. + +For example, the column Treatment will be replaced by two columns, Placebo, and Treated. Each of them will be *binary*. For example an observation which had the value Placebo in column Treatment before the transformation will have, after the transformation, the value 1 in the new column Placebo and the value 0 in the new column Treated. + +Formulae `Improved~.-1` used below means transform all *categorical* features but column Improved to binary values. + Column Improved is excluded because it will be our output column, the one we want to predict. + ```{r, warning=FALSE,message=FALSE} sparse_matrix <- sparse.model.matrix(Improved~.-1, data = df) print(sparse_matrix[1:10,]) - ``` -Create the output vector (not sparse) +Create the output vector (not as a sparse `Matrix`): + 1. Set, for all rows, field in Y column to 0; 2. set Y to 1 when Improved == Marked; -3. Return Y column +3. Return Y column. + ```{r} output_vector = df[,Y:=0][Improved == "Marked",Y:=1][,Y] ``` +Build the model +=============== + +The code below is very usual. For more information, you can look at the documentation of `xgboost()` function. -Following is the same process as other demo ```{r} -bst <- xgboost(data = sparse_matrix, label = output_vector, max.depth = 9, +bst <- xgboost(data = sparse_matrix, label = output_vector, max.depth = 4, eta = 1, nround = 10,objective = "binary:logistic") -xgb.dump(bst, 'xgb.model.dump', with.stats = T) ``` -sparse_matrix@Dimnames[[2]] represents the column names of the sparse matrix. +You can see plenty of `train-error: 0.XXXXX` lines followed by a number. It decreases. Each line shows how well your model explains your data. Lower is better. + +A model which fits too well may [overfit](http://en.wikipedia.org/wiki/Overfitting) (meaning it copy paste too much the past, and is not that good to predict the future). + +Here you can see the numbers decrease until line 7 and then increase. It probably means I am overfitting. To fix that I may reduce the number of rounds to `nround = 4`. I will let things like that because I don't really care for the purpose of this example :-) + + +Feature importance +================== + +Measure feature importance +-------------------------- + +In the code below, `sparse_matrix@Dimnames[[2]]` represents the column names of the sparse matrix. These names are the values of the feature (because one binary column == one value of one *categorical* feature) + ```{r} -importance <- xgb.importance(sparse_matrix@Dimnames[[2]], 'xgb.model.dump') +importance <- xgb.importance(sparse_matrix@Dimnames[[2]], model = bst) print(importance) - ``` -According to the matrix below, the most important feature in this dataset to predict if the treatment will work is the Age. The second most important feature is having received a placebo or not. The sex is third. Then we see our generated features (AgeDiscret). We can see that their contribution is very low (Gain column). +The column `Gain` provide the information we are looking for. -Does these results make sense? -Let's check some Chi2 between each of these features and the outcome. +`Gain` is the improvement in accuracy brought by a feature to the branches it is on. The idea is that before adding a new split on a feature X to the branch there was some wrongly classified elements, after adding the split on this feature, there are two new branches, and each of these branch is more accurate (one branch saying if your observation is on this branch then it should be classified as 1, and the other branch saying the exact opposite, both new branch being more accurate than the one before the insertion of the feature). + +`Cover` measure the relative quantity of observations concerned by a feature. + +`Frequence` is a simpler way to measure the `Gain`. It just counts the number of times a feature is used in all generated trees. You should not use it (unless you know why you want to use it). + +As you can see, features are classified by `Gain`. + +Plotting the feature importance +------------------------------- + +All these things are nice, but it would be even better to plot the result. Fortunately, such function already exists. ```{r} -print(chisq.test(df$Age, df$Y)) -``` - -Pearson correlation between Age and illness disapearing is 35 - -```{r} -print(chisq.test(df$AgeDiscret, df$Y)) -``` - -Our first simplification of Age gives a Pearson correlation of 8. - -```{r} -print(chisq.test(df$AgeCat, df$Y)) xgb.plot.importance(importance_matrix = importance) ``` -The perfectly random split I did between young and old at 30 years old have a low correlation of 2. It's a result we may expect as may be in my mind > 30 years is being old (I am 32 and starting feeling old, this may explain that), but for the illness we are studying, the age to be vulnerable is not the same. Don't let your "gut" lower the quality of your model. In *data science* expression, there is the word *science* :-) +Feature have been automatically divided in 2 clusters: the interesting features... and the others. -As you can see, in general destroying information by simplying it won't improve your model. Chi2 just demonstrates that. But in more complex cases, creating a new feature based on existing one which makes link with the outcome more obvious may help the algorithm and improve the model. The case studied here is not enough complex to show that. Check Kaggle forum for some challenging datasets. -However it's almost always worse when you add some arbitrary rules. -Moreover, you can notice that even if we have added some not useful new features highly correlated with other features, the boosting tree algorithm have been able to choose the best one, which in this case is the Age. Linear model may not be that strong in these scenario. +According to the plot above, the most important feature in this dataset to predict if the treatment will work is : -```{r} -xgb.plot.tree(sparse_matrix@Dimnames[[2]], model = bst, n_first_tree = 1) +* the Age; +* having received a placebo or not ; +* the sex is third but already included in the not interesting feature ; +* then we see our generated features (AgeDiscret). We can see that their contribution is very low. + +*Note: Depending of the case you may have more than two clusters. Default value is to limit them to 10, but you can increase this limit. Look at the function documentation for more information.* + +Does these results make sense? +------------------------------ + +Let's check some **Chi2** between each of these features and the outcome. + +Higher **Chi2** means better correlation. + +```{r, warning=FALSE, message=FALSE} +c2 <- chisq.test(df$Age, df$Y) +print(c2) +``` + +Pearson correlation between Age and illness disapearing is **`r round(c2$statistic, 2 )`**. + +```{r, warning=FALSE, message=FALSE} +c2 <- chisq.test(df$AgeDiscret, df$Y) +print(c2) +``` + +Our first simplification of Age gives a Pearson correlation is **`r round(c2$statistic, 2)`**. + +```{r, warning=FALSE, message=FALSE} +c2 <- chisq.test(df$AgeCat, df$Y) +print(c2) +``` + +The perfectly random split I did between young and old at 30 years old have a low correlation of **`r round(c2$statistic, 2)`**. It's a result we may expect as may be in my mind > 30 years is being old (I am 32 and starting feeling old, this may explain that), but for the illness we are studying, the age to be vulnerable is not the same. Don't let your *gut* lower the quality of your model. In *data science* expression, there is the word *science* :-) + +Conclusion +========== + +As you can see, in general *destroying information by simplying it won't improve your model*. **Chi2** just demonstrates that. + +But in more complex cases, creating a new feature based on existing one which makes link with the outcome more obvious may help the algorithm and improve the model. + +The case studied here is not enough complex to show that. Check Kaggle forum for some challenging datasets. However it's almost always worse when you add some arbitrary rules. + +Moreover, you can notice that even if we have added some not useful new features highly correlated with other features, the boosting tree algorithm have been able to choose the best one, which in this case is the Age. + +Linear model may not be that strong in these scenario. + +```{r, fig.align='center', include=FALSE} +#xgb.plot.tree(sparse_matrix@Dimnames[[2]], model = bst, n_first_tree = 1, width = 1200, height = 800) ``` From 4d79ed9bb1a4e507396075f3147c3c36dc2acea1 Mon Sep 17 00:00:00 2001 From: Tong He Date: Thu, 29 Jan 2015 13:30:47 -0800 Subject: [PATCH 43/94] Update runall.R --- R-package/demo/runall.R | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/R-package/demo/runall.R b/R-package/demo/runall.R index 83e873dca..2d0384156 100644 --- a/R-package/demo/runall.R +++ b/R-package/demo/runall.R @@ -5,4 +5,5 @@ demo(boost_from_prediction) demo(predict_first_ntree) demo(generalized_linear_model) demo(cross_validation) - +demo(create_sparse_matrix) +demo(predict_leaf_indices) From d788bf9aeb8e3559cf8037ad27ccfb38a2bcdaf7 Mon Sep 17 00:00:00 2001 From: Tong He Date: Thu, 29 Jan 2015 15:27:29 -0800 Subject: [PATCH 44/94] Update DESCRIPTION --- R-package/DESCRIPTION | 1 + 1 file changed, 1 insertion(+) diff --git a/R-package/DESCRIPTION b/R-package/DESCRIPTION index 63ed9581c..adc1635e0 100644 --- a/R-package/DESCRIPTION +++ b/R-package/DESCRIPTION @@ -17,6 +17,7 @@ Description: This package is a R wrapper of xgboost, which is short for eXtreme License: Apache License (== 2.0) | file LICENSE URL: https://github.com/tqchen/xgboost BugReports: https://github.com/tqchen/xgboost/issues +VignetteBuilder: knitr Depends: R (>= 2.10) Imports: From f3b2c7415331979fe242b3e377e99e5cd9570919 Mon Sep 17 00:00:00 2001 From: Tong He Date: Thu, 29 Jan 2015 15:30:46 -0800 Subject: [PATCH 45/94] Update README.md --- R-package/README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/R-package/README.md b/R-package/README.md index ca65df9a3..db55b9eb7 100644 --- a/R-package/README.md +++ b/R-package/README.md @@ -5,8 +5,7 @@ For up-to-date version(which is recommended), please install from github. Windows user will need to install [RTools](http://cran.r-project.org/bin/windows/Rtools/) first. ```r -require(devtools) -install_github('tqchen/xgboost',subdir='R-package') +devtools::install_github('tqchen/xgboost',subdir='R-package') ``` For stable version on CRAN, please run From 964c668d44d8f5d53b56a9274167b7a544c44d69 Mon Sep 17 00:00:00 2001 From: Tong He Date: Thu, 29 Jan 2015 16:20:13 -0800 Subject: [PATCH 46/94] Update DESCRIPTION --- R-package/DESCRIPTION | 1 + 1 file changed, 1 insertion(+) diff --git a/R-package/DESCRIPTION b/R-package/DESCRIPTION index adc1635e0..6d7a22018 100644 --- a/R-package/DESCRIPTION +++ b/R-package/DESCRIPTION @@ -18,6 +18,7 @@ License: Apache License (== 2.0) | file LICENSE URL: https://github.com/tqchen/xgboost BugReports: https://github.com/tqchen/xgboost/issues VignetteBuilder: knitr +Suggests: knitr Depends: R (>= 2.10) Imports: From 42a4da91b582faf1117789d6ec740461c9955f68 Mon Sep 17 00:00:00 2001 From: tqchen Date: Fri, 30 Jan 2015 16:40:58 -0800 Subject: [PATCH 47/94] chges --- src/utils/io.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/utils/io.h b/src/utils/io.h index dff691ee0..810510b6d 100644 --- a/src/utils/io.h +++ b/src/utils/io.h @@ -3,6 +3,7 @@ #include #include #include +#include #include "./utils.h" /*! * \file io.h From 9725cf2aeb26d5366ab659a59334b601b980f90b Mon Sep 17 00:00:00 2001 From: tqchen Date: Fri, 30 Jan 2015 16:41:06 -0800 Subject: [PATCH 48/94] Squashed 'subtree/rabit/' changes from 4ebe657..fb13cab fb13cab change makefile 1479e37 fixed small bug in mpi submission script 0ca7a63 Update README.md 5ef4830 ok 93a1338 chg note git-subtree-dir: subtree/rabit git-subtree-split: fb13cab216b795f86dc90547b71c0f730766affa --- Makefile | 3 ++- README.md | 9 +++++++++ rabit-learn/README.md | 2 +- tracker/rabit_mpi.py | 2 +- 4 files changed, 13 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 20045bbd6..47c16fbde 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,6 @@ -export CC = gcc +ifndef export CXX = g++ +endif export MPICXX = mpicxx export LDFLAGS= -Llib export WARNFLAGS= -Wall -Wextra -Wno-unused-parameter -Wno-unknown-pragmas -pedantic diff --git a/README.md b/README.md index 929ea9723..752c27d43 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ rabit is a light weight library that provides a fault tolerant interface of Allr * [Tutorial](guide) * [API Documentation](http://homes.cs.washington.edu/~tqchen/rabit/doc) * You can also directly read the [interface header](include/rabit.h) +* [Machine Learning Tools](rabit-learn) Features ==== @@ -26,3 +27,11 @@ Use Rabit * Add lib to the library path and include to the include path of compiler * Languages: You can use rabit in C++ and python - It is also possible to port the library to other languages + +Contributing +==== +Rabit is an open-source library, contributions are welcomed, including: +* The rabit core library. +* Customized tracker script for new platforms and interface of new languages. +* Toolkits, benchmarks, resource (links to related repos). +* Tutorial and examples about the library. diff --git a/rabit-learn/README.md b/rabit-learn/README.md index bd16ea826..7c3082c41 100644 --- a/rabit-learn/README.md +++ b/rabit-learn/README.md @@ -8,7 +8,7 @@ It also contain links to the Machine Learning packages that uses rabit. Toolkits ==== * [KMeans Clustering](kmeans) -* [XGBoost: eXtreme Gradient Boosting](https://github.com/tqchen/xgboost/tree/unity/multi-node) +* [XGBoost: eXtreme Gradient Boosting](https://github.com/tqchen/xgboost/tree/master/multi-node) - xgboost is a very fast boosted tree(also known as GBDT) library, that can run more than 10 times faster than existing packages - Rabit carries xgboost to distributed enviroment, inheritating all the benefits of xgboost diff --git a/tracker/rabit_mpi.py b/tracker/rabit_mpi.py index 599a9a7c5..0ec80d208 100755 --- a/tracker/rabit_mpi.py +++ b/tracker/rabit_mpi.py @@ -35,7 +35,7 @@ def mpi_submit(nslave, worker_args): if args.hostfile is None: cmd = ' '.join(['mpirun -n %d' % (nslave)] + args.command + worker_args) else: - ' '.join(['mpirun -n %d --hostfile %s' % (nslave, args.hostfile)] + args.command + worker_args) + cmd = ' '.join(['mpirun -n %d --hostfile %s' % (nslave, args.hostfile)] + args.command + worker_args) print cmd subprocess.check_call(cmd, shell = True) From b32d4faa82ee7546614d3f728957d92b620c73f1 Mon Sep 17 00:00:00 2001 From: tqchen Date: Fri, 30 Jan 2015 16:50:10 -0800 Subject: [PATCH 49/94] quick fix seed --- Makefile | 2 +- src/learner/learner-inl.hpp | 6 +++--- src/utils/base64.h | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index a4bbe876f..abdeb0bb8 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ export CC = gcc -export CXX = g++ +export CXX = clang++ export MPICXX = mpicxx export LDFLAGS= -pthread -lm export CFLAGS = -Wall -O3 -msse2 -Wno-unknown-pragmas -fPIC diff --git a/src/learner/learner-inl.hpp b/src/learner/learner-inl.hpp index 616cf03e9..f7af5bfff 100644 --- a/src/learner/learner-inl.hpp +++ b/src/learner/learner-inl.hpp @@ -104,7 +104,7 @@ class BoostLearner : public rabit::ISerializable { } if (!strcmp(name, "eval_metric")) evaluator_.AddEval(val); if (!strcmp("seed", name)) { - this->seed = seed; random::Seed(atoi(val)); + seed = atoi(val); random::Seed(seed); } if (!strcmp("seed_per_iter", name)) seed_per_iteration = atoi(val); if (!strcmp("save_base64", name)) save_base64 = atoi(val); @@ -269,8 +269,8 @@ class BoostLearner : public rabit::ISerializable { * \param p_train pointer to the data matrix */ inline void UpdateOneIter(int iter, const DMatrix &train) { - if (seed_per_iteration || rabit::IsDistributed()) { - random::Seed(this->seed * kRandSeedMagic); + if (seed_per_iteration != 0 || rabit::IsDistributed()) { + random::Seed(this->seed * kRandSeedMagic + iter); } this->PredictRaw(train, &preds_); obj_->GetGradient(preds_, train.info, iter, &gpair_); diff --git a/src/utils/base64.h b/src/utils/base64.h index 36699199f..66e090ab0 100644 --- a/src/utils/base64.h +++ b/src/utils/base64.h @@ -139,7 +139,7 @@ class Base64InStream: public IStream { private: FILE *fp; - unsigned char tmp_ch; + int tmp_ch; int num_prev; unsigned char buf_prev[2]; // whether we need to do strict check From 3791ae5cf0a03aa64c763692cb4a5865816f37b6 Mon Sep 17 00:00:00 2001 From: tqchen Date: Fri, 30 Jan 2015 16:50:27 -0800 Subject: [PATCH 50/94] Squashed 'subtree/rabit/' changes from fb13cab..1bb8fe9 1bb8fe9 chg makefile git-subtree-dir: subtree/rabit git-subtree-split: 1bb8fe96150175216cf5c59a21a7908418ae52b4 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 47c16fbde..b090961c6 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -ifndef +ifndef CXX export CXX = g++ endif export MPICXX = mpicxx From 02e98e053475408a36f388825fb13b67f067b592 Mon Sep 17 00:00:00 2001 From: tqchen Date: Fri, 30 Jan 2015 21:47:49 -0800 Subject: [PATCH 51/94] chg back to g++ --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index abdeb0bb8..a4bbe876f 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ export CC = gcc -export CXX = clang++ +export CXX = g++ export MPICXX = mpicxx export LDFLAGS= -pthread -lm export CFLAGS = -Wall -O3 -msse2 -Wno-unknown-pragmas -fPIC From f35950dc46c219c4942f1420c7760cd425e1b7c5 Mon Sep 17 00:00:00 2001 From: pommedeterresautee Date: Sun, 1 Feb 2015 13:02:33 +0100 Subject: [PATCH 52/94] small change in package version --- R-package/DESCRIPTION | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/R-package/DESCRIPTION b/R-package/DESCRIPTION index 59cb4bdd4..0182aa197 100644 --- a/R-package/DESCRIPTION +++ b/R-package/DESCRIPTION @@ -25,6 +25,6 @@ Imports: data.table (>= 1.9.4), magrittr (>= 1.5), stringr, - DiagrammeR (>= 0.3), - ggplot2(>= 1.0.0), - Ckmeans.1d.dp + DiagrammeR (>= 0.4), + ggplot2 (>= 1.0.0), + Ckmeans.1d.dp (>= 3.02) From 9f5929497acabd4b433bb9b3f4981bdfa4adad82 Mon Sep 17 00:00:00 2001 From: pommedeterresautee Date: Sun, 1 Feb 2015 13:09:27 +0100 Subject: [PATCH 53/94] version stringr --- R-package/DESCRIPTION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R-package/DESCRIPTION b/R-package/DESCRIPTION index 0182aa197..adf7559ef 100644 --- a/R-package/DESCRIPTION +++ b/R-package/DESCRIPTION @@ -24,7 +24,7 @@ Imports: methods, data.table (>= 1.9.4), magrittr (>= 1.5), - stringr, + stringr (>= 0.6.2), DiagrammeR (>= 0.4), ggplot2 (>= 1.0.0), Ckmeans.1d.dp (>= 3.02) From a17e29b130fef998a9bbd359e1dd155ff32fea95 Mon Sep 17 00:00:00 2001 From: pommedeterresautee Date: Sun, 1 Feb 2015 14:08:48 +0100 Subject: [PATCH 54/94] Fix bug in Cross Validation when showsd = FALSE --- R-package/R/xgb.cv.R | 5 +++-- R-package/demo/cross_validation.R | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/R-package/R/xgb.cv.R b/R-package/R/xgb.cv.R index ed088df52..18c8cb716 100644 --- a/R-package/R/xgb.cv.R +++ b/R-package/R/xgb.cv.R @@ -114,10 +114,11 @@ xgb.cv <- function(params=list(), data, nrounds, nfold, label = NULL, missing = colnames <- str_split(string = history[1], pattern = "\t")[[1]] %>% .[2:length(.)] %>% str_extract(".*:") %>% str_replace(":","") %>% str_replace("-", ".") colnamesMean <- paste(colnames, "mean") - colnamesStd <- paste(colnames, "std") + if(showsd) colnamesStd <- paste(colnames, "std") colnames <- c() - for(i in 1:length(colnamesMean)) colnames <- c(colnames, colnamesMean[i], colnamesStd[i]) + if(showsd) for(i in 1:length(colnamesMean)) colnames <- c(colnames, colnamesMean[i], colnamesStd[i]) + else colnames <- colnamesMean type <- rep(x = "numeric", times = length(colnames)) dt <- read.table(text = "", colClasses = type, col.names = colnames) %>% as.data.table diff --git a/R-package/demo/cross_validation.R b/R-package/demo/cross_validation.R index 47a0adea0..ed78f93ed 100644 --- a/R-package/demo/cross_validation.R +++ b/R-package/demo/cross_validation.R @@ -19,7 +19,7 @@ cat('running cross validation, disable standard deviation display\n') # [iteration] metric_name:mean_value+std_value # std_value is standard deviation of the metric xgb.cv(param, dtrain, nround, nfold=5, - metrics={'error'}, , showsd = FALSE) + metrics={'error'}, showsd = FALSE) ### # you can also do cross validation with cutomized loss function From b31cbdb0a4c770033ddc8fbc599d0dfd740bfa38 Mon Sep 17 00:00:00 2001 From: El Potaeto Date: Fri, 30 Jan 2015 17:53:05 +0100 Subject: [PATCH 55/94] modif CSS --- R-package/vignettes/discoverYourData.Rmd | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/R-package/vignettes/discoverYourData.Rmd b/R-package/vignettes/discoverYourData.Rmd index cbb3d49f3..899efba11 100644 --- a/R-package/vignettes/discoverYourData.Rmd +++ b/R-package/vignettes/discoverYourData.Rmd @@ -1,10 +1,11 @@ --- title: "Understand your dataset with Xgboost" -date: "Wednesday, January 28, 2015" output: html_document: + css: vignette.css number_sections: yes toc: yes +date: "Wednesday, January 28, 2015" --- Introduction @@ -73,7 +74,7 @@ For the first feature we create groups of age by rounding the real age. Note tha df[,AgeDiscret:= as.factor(round(Age/10,0))][1:10] ``` -Followinf is an even stronger simplification of the real age with an arbitrary split at 30 years old. I choose this value **based on nothing**. We will see later if simplifying the information based on arbitrary values is a good strategy (I am sure you already have an idea of how well it will work!). +Following is an even stronger simplification of the real age with an arbitrary split at 30 years old. I choose this value **based on nothing**. We will see later if simplifying the information based on arbitrary values is a good strategy (I am sure you already have an idea of how well it will work!). ```{r} df[,AgeCat:= as.factor(ifelse(Age > 30, "Old", "Young"))][1:10] From 451944c52bb6a39d578d92ee0662107a1706774f Mon Sep 17 00:00:00 2001 From: El Potaeto Date: Fri, 30 Jan 2015 17:53:15 +0100 Subject: [PATCH 56/94] CSS --- R-package/vignettes/vignette.css | 201 +++++++++++++++++++++++++++++++ 1 file changed, 201 insertions(+) create mode 100644 R-package/vignettes/vignette.css diff --git a/R-package/vignettes/vignette.css b/R-package/vignettes/vignette.css new file mode 100644 index 000000000..c09bf1813 --- /dev/null +++ b/R-package/vignettes/vignette.css @@ -0,0 +1,201 @@ +body{ + margin: 0 auto; + background-color: white; + +/* --------- FONT FAMILY -------- + following are some optional font families. Usually a family + is safer to choose than a specific font, + which may not be on the users computer */ +/ font-family:Georgia, Palatino, serif; + font-family: "Open Sans", "Book Antiqua", Palatino, serif; +/ font-family:Arial, Helvetica, sans-serif; +/ font-family:Tahoma, Verdana, Geneva, sans-serif; +/ font-family:Courier, monospace; +/ font-family:"Times New Roman", Times, serif; + +/* -------------- COLOR OPTIONS ------------ + following are additional color options for base font + you could uncomment another one to easily change the base color + or add one to a specific element style below */ + color: #333333; /* dark gray not black */ +/ color: #000000; /* black */ +/ color: #666666; /* medium gray black */ +/ color: #E3E3E3; /* very light gray */ +/ color: white; + + line-height: 1; + max-width: 960px; + padding: 20px; + font-size: 17px; +} + + +p { + line-height: 150%; +/ max-width: 540px; + max-width: 960px; + font-weight: 400; +/ color: #333333 +} + + +h1, h2, h3, h4 { +/ color: #111111; + font-weight: 400; +} + +h2, h3, h4, h5, p { + margin-bottom: 20px; + padding: 0; +} + +h1 { + margin-bottom: 10px; + font-size:230%; + padding: 0px; + font-variant:small-caps; +} + +h2 { + font-size:130% +/ margin: 24px 0 6px; +} + +h3 { + font-size:110% + text-decoration: underline; + font-style: italic; +} +h4 { + font-size:100% + font-variant:small-caps; + +} +h5 { + font-size:100% + font-weight: 100; + font-style: italic; +} + +h6 { + font-size:100% + font-weight: 100; + color:red; + font-variant:small-caps; + font-style: italic; +} +a { + color: #606AAA; + margin: 0; + padding: 0; + vertical-align: baseline; +} +a:hover { + text-decoration: blink; + color: green; +} +a:visited { + color: gray; +} +ul, ol { + padding: 0; + margin: 0px 0px 0px 50px; +} +ul { + list-style-type: square; + list-style-position: inside; + +} + +li { + line-height:150% +} +li ul, li ul { + margin-left: 24px; +} + +pre { + padding: 0px 24px; + max-width: 800px; + white-space: pre-wrap; +} +code { + font-family: Consolas, Monaco, Andale Mono, monospace; + line-height: 1.5; + font-size: 15px; +} +aside { + display: block; + float: right; + width: 390px; +} +blockquote { + border-left:.5em solid #eee; + padding: 0 1em; + margin-left:0; + max-width: 476px; +} +blockquote cite { + / font-size:14px; + line-height:20px; + color:#bfbfbf; +} +blockquote cite:before { + content: '\2014 \00A0'; +} + +blockquote p { + color: #666; + max-width: 460px; +} +hr { +/ width: 540px; + text-align: left; + margin: 0 auto 0 0; + color: #999; +} + + +/* table */ + +table { + width: 100%; + border-top: 1px solid #919699; + border-left: 1px solid #919699; + border-spacing: 0; +} + +table th { + padding: 4px 8px 4px 8px; + text-align: center; + color: white; + background: #606AAA; + border-bottom: 1px solid #919699; + border-right: 1px solid #919699; +} +table th p { + font-weight: bold; + margin-bottom: 0px; +} + +table td { + padding: 8px; + vertical-align: top; + border-bottom: 1px solid #919699; + border-right: 1px solid #919699; +} + +table td:last-child { + /background: lightgray; + text-align: right; +} + +table td p { + margin-bottom: 0px; +} +table td p + p { + margin-top: 5px; +} +table td p + p + p { + margin-top: 5px; +} From dc3003cefd8b63762b39dc91ab2123ca7272b000 Mon Sep 17 00:00:00 2001 From: tqchen Date: Sun, 1 Feb 2015 21:17:37 -0800 Subject: [PATCH 57/94] add saveload to raw --- R-package/NAMESPACE | 1 + R-package/R/utils.R | 9 ++++++--- R-package/R/xgb.save.raw.R | 27 +++++++++++++++++++++++++++ R-package/demo/basic_walkthrough.R | 8 ++++++++ R-package/src/xgboost_R.cpp | 17 +++++++++++++++++ R-package/src/xgboost_R.h | 11 +++++++++++ src/learner/learner-inl.hpp | 6 ++++-- wrapper/xgboost_wrapper.cpp | 24 ++++++++++++++++++++++++ wrapper/xgboost_wrapper.h | 15 +++++++++++++++ 9 files changed, 113 insertions(+), 5 deletions(-) create mode 100644 R-package/R/xgb.save.raw.R diff --git a/R-package/NAMESPACE b/R-package/NAMESPACE index fab1546a2..ee8496d83 100644 --- a/R-package/NAMESPACE +++ b/R-package/NAMESPACE @@ -13,6 +13,7 @@ export(xgb.model.dt.tree) export(xgb.plot.importance) export(xgb.plot.tree) export(xgb.save) +export(xgb.save.raw) export(xgb.train) export(xgboost) exportMethods(predict) diff --git a/R-package/R/utils.R b/R-package/R/utils.R index 412132891..b0c7f15ac 100644 --- a/R-package/R/utils.R +++ b/R-package/R/utils.R @@ -57,10 +57,13 @@ xgb.Booster <- function(params = list(), cachelist = list(), modelfile = NULL) { } } if (!is.null(modelfile)) { - if (typeof(modelfile) != "character") { - stop("xgb.Booster: modelfile must be character") + if (typeof(modelfile) == "character") { + .Call("XGBoosterLoadModel_R", handle, modelfile, PACKAGE = "xgboost") + } else if (typeof(modelfile) == "raw") { + .Call("XGBoosterLoadModelFromRaw_R", handle, modelfile, PACKAGE = "xgboost") + } else { + stop("xgb.Booster: modelfile must be character or raw vector") } - .Call("XGBoosterLoadModel_R", handle, modelfile, PACKAGE = "xgboost") } return(structure(handle, class = "xgb.Booster")) } diff --git a/R-package/R/xgb.save.raw.R b/R-package/R/xgb.save.raw.R new file mode 100644 index 000000000..11fc44470 --- /dev/null +++ b/R-package/R/xgb.save.raw.R @@ -0,0 +1,27 @@ +#' Save xgboost model to R's raw vector, +#' user can call xgb.load to load the model back from raw vector +#' +#' Save xgboost model from xgboost or xgb.train +#' +#' @param model the model object. +#' +#' @examples +#' data(agaricus.train, package='xgboost') +#' data(agaricus.test, package='xgboost') +#' train <- agaricus.train +#' test <- agaricus.test +#' bst <- xgboost(data = train$data, label = train$label, max.depth = 2, +#' eta = 1, nround = 2,objective = "binary:logistic") +#' raw <- xgb.save(bst) +#' bst <- xgb.load(raw) +#' pred <- predict(bst, test$data) +#' @export +#' +xgb.save.raw <- function(model) { + if (class(model) == "xgb.Booster") { + raw <- .Call("XGBoosterModelToRaw_R", model, PACKAGE = "xgboost") + return(raw) + } + stop("xgb.raw: the input must be xgb.Booster. Use xgb.DMatrix.save to save + xgb.DMatrix object.") +} diff --git a/R-package/demo/basic_walkthrough.R b/R-package/demo/basic_walkthrough.R index 7e6914b31..25dd56612 100644 --- a/R-package/demo/basic_walkthrough.R +++ b/R-package/demo/basic_walkthrough.R @@ -58,6 +58,14 @@ pred2 <- predict(bst2, test$data) # pred2 should be identical to pred print(paste("sum(abs(pred2-pred))=", sum(abs(pred2-pred)))) +# save model to R's raw vector +raw = xgb.save.raw(bst) +# load binary model to R +bst3 <- xgb.load(raw) +pred3 <- predict(bst2, test$data) +# pred2 should be identical to pred +print(paste("sum(abs(pred3-pred))=", sum(abs(pred2-pred)))) + #----------------Advanced features -------------- # to use advanced features, we need to put data in xgb.DMatrix dtrain <- xgb.DMatrix(data = train$data, label=train$label) diff --git a/R-package/src/xgboost_R.cpp b/R-package/src/xgboost_R.cpp index aa17b30cc..9ebcec167 100644 --- a/R-package/src/xgboost_R.cpp +++ b/R-package/src/xgboost_R.cpp @@ -274,6 +274,23 @@ extern "C" { XGBoosterSaveModel(R_ExternalPtrAddr(handle), CHAR(asChar(fname))); _WrapperEnd(); } + void XGBoosterLoadModelFromRaw_R(SEXP handle, SEXP raw) { + _WrapperBegin(); + XGBoosterLoadModelFromBuffer(R_ExternalPtrAddr(handle), + RAW(raw), + length(raw)); + _WrapperEnd(); + } + SEXP XGBoosterModelToRaw_R(SEXP handle) { + bst_ulong olen; + _WrapperBegin(); + const char *raw = XGBoosterGetModelRaw(R_ExternalPtrAddr(handle), &olen); + _WrapperEnd(); + SEXP ret = PROTECT(allocVector(RAWSXP, olen)); + memcpy(RAW(ret), raw, olen); + UNPROTECT(1); + return ret; + } SEXP XGBoosterDumpModel_R(SEXP handle, SEXP fmap, SEXP with_stats) { _WrapperBegin(); bst_ulong olen; diff --git a/R-package/src/xgboost_R.h b/R-package/src/xgboost_R.h index 1e7606dd7..a86e85ffa 100644 --- a/R-package/src/xgboost_R.h +++ b/R-package/src/xgboost_R.h @@ -127,6 +127,17 @@ extern "C" { * \param fname file name */ void XGBoosterSaveModel_R(SEXP handle, SEXP fname); + /*! + * \brief load model from raw array + * \param handle handle + */ + void XGBoosterLoadModelFromRaw_R(SEXP handle, SEXP raw); + /*! + * \brief save model into R's raw array + * \param handle handle + * \return raw array + */ + SEXP XGBoosterModelToRaw_R(SEXP handle); /*! * \brief dump model into a string * \param handle handle diff --git a/src/learner/learner-inl.hpp b/src/learner/learner-inl.hpp index f7af5bfff..ee9153896 100644 --- a/src/learner/learner-inl.hpp +++ b/src/learner/learner-inl.hpp @@ -159,7 +159,9 @@ class BoostLearner : public rabit::ISerializable { * \param with_pbuffer whether to load with predict buffer * \param calc_num_feature whether call InitTrainer with calc_num_feature */ - inline void LoadModel(utils::IStream &fi, bool with_pbuffer = true, bool calc_num_feature = true) { + inline void LoadModel(utils::IStream &fi, + bool with_pbuffer = true, + bool calc_num_feature = true) { utils::Check(fi.Read(&mparam, sizeof(ModelParam)) != 0, "BoostLearner: wrong model format"); utils::Check(fi.Read(&name_obj_), "BoostLearner: wrong model format"); @@ -192,8 +194,8 @@ class BoostLearner : public rabit::ISerializable { */ inline void LoadModel(const char *fname) { FILE *fp = utils::FopenCheck(fname, "rb"); - std::string header; header.resize(4); utils::FileStream fi(fp); + std::string header; header.resize(4); // check header for different binary encode // can be base64 or binary if (fi.Read(&header[0], 4) != 0) { diff --git a/wrapper/xgboost_wrapper.cpp b/wrapper/xgboost_wrapper.cpp index d744c3e22..2aa523494 100644 --- a/wrapper/xgboost_wrapper.cpp +++ b/wrapper/xgboost_wrapper.cpp @@ -57,6 +57,22 @@ class Booster: public learner::BoostLearner { learner::BoostLearner::LoadModel(fname); this->init_model = true; } + inline void LoadModelFromBuffer(const void *buf, size_t size) { + utils::MemoryFixSizeBuffer fs((void*)buf, size); + learner::BoostLearner::LoadModel(fs); + this->init_model = true; + } + inline const char *GetModelRaw(bst_ulong *out_len) { + model_str.resize(0); + utils::MemoryBufferStream fs(&model_str); + learner::BoostLearner::SaveModel(fs); + *out_len = static_cast(model_str.length()); + if (*out_len == 0) { + return NULL; + } else { + return &model_str[0]; + } + } inline const char** GetModelDump(const utils::FeatMap& fmap, bool with_stats, bst_ulong *len) { model_dump = this->DumpModel(fmap, with_stats); model_dump_cptr.resize(model_dump.size()); @@ -69,6 +85,8 @@ class Booster: public learner::BoostLearner { // temporal fields // temporal data to save evaluation dump std::string eval_str; + // temporal data to save model dump + std::string model_str; // temporal space to save model dump std::vector model_dump; std::vector model_dump_cptr; @@ -295,6 +313,12 @@ extern "C"{ void XGBoosterSaveModel(const void *handle, const char *fname) { static_cast(handle)->SaveModel(fname); } + void XGBoosterLoadModelFromBuffer(void *handle, const void *buf, bst_ulong len) { + static_cast(handle)->LoadModelFromBuffer(buf, len); + } + const char *XGBoosterGetModelRaw(void *handle, bst_ulong *out_len) { + return static_cast(handle)->GetModelRaw(out_len); + } const char** XGBoosterDumpModel(void *handle, const char *fmap, int with_stats, bst_ulong *len){ utils::FeatMap featmap; if (strlen(fmap) != 0) { diff --git a/wrapper/xgboost_wrapper.h b/wrapper/xgboost_wrapper.h index 82fedb9d6..f236ee5da 100644 --- a/wrapper/xgboost_wrapper.h +++ b/wrapper/xgboost_wrapper.h @@ -224,6 +224,21 @@ extern "C" { * \param fname file name */ XGB_DLL void XGBoosterSaveModel(const void *handle, const char *fname); + /*! + * \brief load model from in memory buffer + * \param handle handle + * \param buf pointer to the buffer + * \param len the length of the buffer + */ + XGB_DLL void XGBoosterLoadModelFromBuffer(void *handle, const void *buf, bst_ulong len); + /*! + * \brief save model into binary raw bytes, return header of the array + * user must copy the result out, before next xgboost call + * \param handle handle + * \param out_len the argument to hold the output length + * \return the pointer to the beginning of binary buffer + */ + XGB_DLL const char *XGBoosterGetModelRaw(void *handle, bst_ulong *out_len); /*! * \brief dump model, return array of strings representing model dump * \param handle handle From 1d21ff87ff439e6d44f4a71d44c9db8428d2634d Mon Sep 17 00:00:00 2001 From: tqchen Date: Sun, 1 Feb 2015 21:19:24 -0800 Subject: [PATCH 58/94] add saveload to raw --- R-package/demo/basic_walkthrough.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R-package/demo/basic_walkthrough.R b/R-package/demo/basic_walkthrough.R index 25dd56612..c00765f58 100644 --- a/R-package/demo/basic_walkthrough.R +++ b/R-package/demo/basic_walkthrough.R @@ -62,7 +62,7 @@ print(paste("sum(abs(pred2-pred))=", sum(abs(pred2-pred)))) raw = xgb.save.raw(bst) # load binary model to R bst3 <- xgb.load(raw) -pred3 <- predict(bst2, test$data) +pred3 <- predict(bst3, test$data) # pred2 should be identical to pred print(paste("sum(abs(pred3-pred))=", sum(abs(pred2-pred)))) From 5d135858f7eefa7d5af54dced27e9e1d03d73865 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Benesty?= Date: Mon, 2 Feb 2015 13:21:13 +0100 Subject: [PATCH 59/94] Spell --- R-package/demo/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R-package/demo/README.md b/R-package/demo/README.md index e646cf0c9..be08ee54f 100644 --- a/R-package/demo/README.md +++ b/R-package/demo/README.md @@ -14,5 +14,5 @@ Benchmarks Notes ==== -* Contribution of exampls, benchmarks is more than welcomed! +* Contribution of examples, benchmarks is more than welcomed! * If you like to share how you use xgboost to solve your problem, send a pull request:) From b34a56b1f9cd247026aa10d7572a92a26279e761 Mon Sep 17 00:00:00 2001 From: tqchen Date: Wed, 4 Feb 2015 11:18:56 -0800 Subject: [PATCH 60/94] fix for ulong --- R-package/src/xgboost_R.cpp | 6 +++--- wrapper/xgboost_wrapper.cpp | 5 +++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/R-package/src/xgboost_R.cpp b/R-package/src/xgboost_R.cpp index 9ebcec167..39e6a1b66 100644 --- a/R-package/src/xgboost_R.cpp +++ b/R-package/src/xgboost_R.cpp @@ -71,13 +71,13 @@ extern "C" { SEXP missing) { _WrapperBegin(); SEXP dim = getAttrib(mat, R_DimSymbol); - bst_ulong nrow = static_cast(INTEGER(dim)[0]); - bst_ulong ncol = static_cast(INTEGER(dim)[1]); + size_t nrow = static_cast(INTEGER(dim)[0]); + size_t ncol = static_cast(INTEGER(dim)[1]); double *din = REAL(mat); std::vector data(nrow * ncol); #pragma omp parallel for schedule(static) for (bst_omp_uint i = 0; i < nrow; ++i) { - for (bst_ulong j = 0; j < ncol; ++j) { + for (size_t j = 0; j < ncol; ++j) { data[i * ncol +j] = din[i + nrow * j]; } } diff --git a/wrapper/xgboost_wrapper.cpp b/wrapper/xgboost_wrapper.cpp index 2aa523494..6c8f250c1 100644 --- a/wrapper/xgboost_wrapper.cpp +++ b/wrapper/xgboost_wrapper.cpp @@ -151,7 +151,7 @@ extern "C"{ void* XGDMatrixCreateFromMat(const float *data, bst_ulong nrow, bst_ulong ncol, - float missing) { + float missing) { bool nan_missing = isnan(missing); DMatrixSimple *p_mat = new DMatrixSimple(); DMatrixSimple &mat = *p_mat; @@ -161,7 +161,8 @@ extern "C"{ bst_ulong nelem = 0; for (bst_ulong j = 0; j < ncol; ++j) { if (isnan(data[j])) { - utils::Check(nan_missing, "There are NAN in the matrix, however, you did not set missing=NAN"); + utils::Check(nan_missing, + "There are NAN in the matrix, however, you did not set missing=NAN"); } else { if (nan_missing || data[j] != missing) { mat.row_data_.push_back(RowBatch::Entry(j, data[j])); From 9f5889f1e385623d473fa9b7cd588fd69cb9c584 Mon Sep 17 00:00:00 2001 From: El Potaeto Date: Wed, 4 Feb 2015 23:59:53 +0100 Subject: [PATCH 61/94] new included feature in dt.tree function --- R-package/NAMESPACE | 1 + R-package/R/xgb.model.dt.tree.R | 7 ++++++- R-package/man/xgb.model.dt.tree.Rd | 2 ++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/R-package/NAMESPACE b/R-package/NAMESPACE index fab1546a2..7d9c64563 100644 --- a/R-package/NAMESPACE +++ b/R-package/NAMESPACE @@ -41,6 +41,7 @@ importFrom(ggplot2,ylab) importFrom(magrittr,"%>%") importFrom(magrittr,add) importFrom(magrittr,not) +importFrom(stringr,str_detect) importFrom(stringr,str_extract) importFrom(stringr,str_extract_all) importFrom(stringr,str_match) diff --git a/R-package/R/xgb.model.dt.tree.R b/R-package/R/xgb.model.dt.tree.R index 373f29403..42ca8237b 100644 --- a/R-package/R/xgb.model.dt.tree.R +++ b/R-package/R/xgb.model.dt.tree.R @@ -14,6 +14,7 @@ #' @importFrom stringr str_split #' @importFrom stringr str_extract #' @importFrom stringr str_trim +#' @importFrom stringr str_detect #' @param feature_names names of each feature as a character vector. Can be extracted from a sparse matrix (see example). If model dump already contains feature names, this argument should be \code{NULL}. #' @param filename_dump the path to the text file storing the model. Model dump must include the gain per feature and per tree (parameter \code{with.stats = T} in function \code{xgb.dump}). #' @param model dump generated by the \code{xgb.train} function. Avoid the creation of a dump file. @@ -37,6 +38,8 @@ #' \item \code{Quality}: it's the gain related to the split in this specific node ; #' \item \code{Cover}: metric to measure the number of observation affected by the split ; #' \item \code{Tree}: ID of the tree. It is included in the main ID ; +#' \item \code{Yes.X} or \code{No.X}: data related to the pointer in \code{Yes} or \code{No} column ; +#' \item \code{Included}: \code{boolean} value which indicates if this value has been pointed by a Yes branch (\code{True}) or a No branch (\code{False}) ; #' } #' #' @examples @@ -158,6 +161,8 @@ xgb.model.dt.tree <- function(feature_names = NULL, filename_dump = NULL, model set(allTrees, i = which(allTrees[,Feature]!= "Leaf"), j = "No.Quality", value = allTrees[ID == no,Quality]) + + allTrees[,"Included":=F][ID == allTrees[!is.na(Yes), Yes], Included:=T][str_detect(ID, "-0$"), Included:=T] allTrees } @@ -165,4 +170,4 @@ xgb.model.dt.tree <- function(feature_names = NULL, filename_dump = NULL, model # Avoid error messages during CRAN check. # The reason is that these variables are never declared # They are mainly column names inferred by Data.table... -globalVariables(c("ID", "Tree", "Yes", ".", ".N", "Feature", "Cover", "Quality", "No", "Gain", "Frequence")) \ No newline at end of file +globalVariables(c("ID", "Tree", "Yes", ".", ".N", "Feature", "Cover", "Quality", "No", "Gain", "Frequence", "Included")) \ No newline at end of file diff --git a/R-package/man/xgb.model.dt.tree.Rd b/R-package/man/xgb.model.dt.tree.Rd index 069e7ad77..31910cc49 100644 --- a/R-package/man/xgb.model.dt.tree.Rd +++ b/R-package/man/xgb.model.dt.tree.Rd @@ -39,6 +39,8 @@ The content of the \code{data.table} is organised that way: \item \code{Quality}: it's the gain related to the split in this specific node ; \item \code{Cover}: metric to measure the number of observation affected by the split ; \item \code{Tree}: ID of the tree. It is included in the main ID ; + \item \code{Yes.X} or \code{No.X}: data related to the pointer in \code{Yes} or \code{No} column ; + \item \code{Included}: \code{boolean} value which indicates if this value has been pointed by a Yes branch (\code{True}) or a No branch (\code{False}) ; } } \examples{ From 92652bffa1b42f21764c3eb19b82fa229aa409a3 Mon Sep 17 00:00:00 2001 From: El Potaeto Date: Thu, 5 Feb 2015 00:01:13 +0100 Subject: [PATCH 62/94] wording --- R-package/R/xgb.model.dt.tree.R | 2 +- R-package/man/xgb.model.dt.tree.Rd | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/R-package/R/xgb.model.dt.tree.R b/R-package/R/xgb.model.dt.tree.R index 42ca8237b..39d51d942 100644 --- a/R-package/R/xgb.model.dt.tree.R +++ b/R-package/R/xgb.model.dt.tree.R @@ -39,7 +39,7 @@ #' \item \code{Cover}: metric to measure the number of observation affected by the split ; #' \item \code{Tree}: ID of the tree. It is included in the main ID ; #' \item \code{Yes.X} or \code{No.X}: data related to the pointer in \code{Yes} or \code{No} column ; -#' \item \code{Included}: \code{boolean} value which indicates if this value has been pointed by a Yes branch (\code{True}) or a No branch (\code{False}) ; +#' \item \code{Included}: \code{boolean} value which indicates if this feature has been pointed by a Yes branch (\code{True}) or a No branch (\code{False}) ; #' } #' #' @examples diff --git a/R-package/man/xgb.model.dt.tree.Rd b/R-package/man/xgb.model.dt.tree.Rd index 31910cc49..105724471 100644 --- a/R-package/man/xgb.model.dt.tree.Rd +++ b/R-package/man/xgb.model.dt.tree.Rd @@ -40,7 +40,7 @@ The content of the \code{data.table} is organised that way: \item \code{Cover}: metric to measure the number of observation affected by the split ; \item \code{Tree}: ID of the tree. It is included in the main ID ; \item \code{Yes.X} or \code{No.X}: data related to the pointer in \code{Yes} or \code{No} column ; - \item \code{Included}: \code{boolean} value which indicates if this value has been pointed by a Yes branch (\code{True}) or a No branch (\code{False}) ; + \item \code{Included}: \code{boolean} value which indicates if this feature has been pointed by a Yes branch (\code{True}) or a No branch (\code{False}) ; } } \examples{ From b7526671ba43ec327447817e1e037861fed3b54a Mon Sep 17 00:00:00 2001 From: El Potaeto Date: Thu, 5 Feb 2015 00:03:39 +0100 Subject: [PATCH 63/94] wording --- R-package/R/xgb.model.dt.tree.R | 2 +- R-package/man/xgb.model.dt.tree.Rd | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/R-package/R/xgb.model.dt.tree.R b/R-package/R/xgb.model.dt.tree.R index 39d51d942..acdcdbdfa 100644 --- a/R-package/R/xgb.model.dt.tree.R +++ b/R-package/R/xgb.model.dt.tree.R @@ -39,7 +39,7 @@ #' \item \code{Cover}: metric to measure the number of observation affected by the split ; #' \item \code{Tree}: ID of the tree. It is included in the main ID ; #' \item \code{Yes.X} or \code{No.X}: data related to the pointer in \code{Yes} or \code{No} column ; -#' \item \code{Included}: \code{boolean} value which indicates if this feature has been pointed by a Yes branch (\code{True}) or a No branch (\code{False}) ; +#' \item \code{Included}: \code{boolean} value which indicates if this feature has been pointed by a Yes branch (\code{True}) or a No branch (\code{False}). By convention stem feature is always included ; #' } #' #' @examples diff --git a/R-package/man/xgb.model.dt.tree.Rd b/R-package/man/xgb.model.dt.tree.Rd index 105724471..8b9eb6a13 100644 --- a/R-package/man/xgb.model.dt.tree.Rd +++ b/R-package/man/xgb.model.dt.tree.Rd @@ -40,7 +40,7 @@ The content of the \code{data.table} is organised that way: \item \code{Cover}: metric to measure the number of observation affected by the split ; \item \code{Tree}: ID of the tree. It is included in the main ID ; \item \code{Yes.X} or \code{No.X}: data related to the pointer in \code{Yes} or \code{No} column ; - \item \code{Included}: \code{boolean} value which indicates if this feature has been pointed by a Yes branch (\code{True}) or a No branch (\code{False}) ; + \item \code{Included}: \code{boolean} value which indicates if this feature has been pointed by a Yes branch (\code{True}) or a No branch (\code{False}). By convention stem feature is always included ; } } \examples{ From 68290546ca4fdda3f240ebb6529805924a64ec03 Mon Sep 17 00:00:00 2001 From: El Potaeto Date: Thu, 5 Feb 2015 09:53:21 +0100 Subject: [PATCH 64/94] simplidied included column computation --- R-package/R/xgb.model.dt.tree.R | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/R-package/R/xgb.model.dt.tree.R b/R-package/R/xgb.model.dt.tree.R index acdcdbdfa..c88c16989 100644 --- a/R-package/R/xgb.model.dt.tree.R +++ b/R-package/R/xgb.model.dt.tree.R @@ -129,8 +129,8 @@ xgb.model.dt.tree <- function(feature_names = NULL, filename_dump = NULL, model qualityLeaf <- extract(leaf, "leaf=\\-*\\d*\\.*\\d*") coverBranch <- extract(branch, "cover=\\d*\\.*\\d*") coverLeaf <- extract(leaf, "cover=\\d*\\.*\\d*") - dt <- data.table(ID = c(idBranch, idLeaf), Feature = c(featureBranch, featureLeaf), Split = c(splitBranch, splitLeaf), Yes = c(yesBranch, yesLeaf), No = c(noBranch, noLeaf), Missing = c(missingBranch, missingLeaf), Quality = c(qualityBranch, qualityLeaf), Cover = c(coverBranch, coverLeaf))[order(ID)][,Tree:=treeID] - + dt <- data.table(ID = c(idBranch, idLeaf), Feature = c(featureBranch, featureLeaf), Split = c(splitBranch, splitLeaf), Yes = c(yesBranch, yesLeaf), No = c(noBranch, noLeaf), Missing = c(missingBranch, missingLeaf), Quality = c(qualityBranch, qualityLeaf), Cover = c(coverBranch, coverLeaf))[order(ID)][,Tree:=treeID][,"Included":=F][ID == yesBranch, Included:=T][1, Included:=T] + allTrees <- rbindlist(list(allTrees, dt), use.names = T, fill = F) } @@ -161,9 +161,7 @@ xgb.model.dt.tree <- function(feature_names = NULL, filename_dump = NULL, model set(allTrees, i = which(allTrees[,Feature]!= "Leaf"), j = "No.Quality", value = allTrees[ID == no,Quality]) - - allTrees[,"Included":=F][ID == allTrees[!is.na(Yes), Yes], Included:=T][str_detect(ID, "-0$"), Included:=T] - + allTrees } From a82a942cd60216559aabe3c950c5200bca21cf9e Mon Sep 17 00:00:00 2001 From: El Potaeto Date: Thu, 5 Feb 2015 17:25:37 +0100 Subject: [PATCH 65/94] add importance feature sign --- R-package/R/xgb.importance.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R-package/R/xgb.importance.R b/R-package/R/xgb.importance.R index 094171382..51cf2a258 100644 --- a/R-package/R/xgb.importance.R +++ b/R-package/R/xgb.importance.R @@ -73,7 +73,7 @@ xgb.importance <- function(feature_names = NULL, filename_dump = NULL, model = N } treeDump <- function(feature_names, text){ - result <- xgb.model.dt.tree(feature_names = feature_names, text = text)[Feature!="Leaf",.(Gain = sum(Quality), Cover = sum(Cover), Frequence = .N), by = Feature][,`:=`(Gain=Gain/sum(Gain),Cover=Cover/sum(Cover),Frequence=Frequence/sum(Frequence))][order(-Gain)] + result <- xgb.model.dt.tree(feature_names = feature_names, text = text)[Feature!="Leaf",.(Gain = sum(Quality), Cover = sum(Cover), Frequence = .N, Included = sum(Included)), by = Feature][,`:=`(Gain=Gain/sum(Gain),Cover=Cover/sum(Cover), Frequence = Frequence/sum(Frequence), Included = Included/Frequence)][,Gain:= ifelse(Included >= 0.5, Gain, -Gain)][order(-Gain)] result } From 8b4acef66222fe5eef168acb545b709e69200443 Mon Sep 17 00:00:00 2001 From: tqchen Date: Thu, 5 Feb 2015 21:03:06 -0800 Subject: [PATCH 66/94] remove sync from wrapper.h --- wrapper/xgboost_wrapper.h | 24 +----------------------- 1 file changed, 1 insertion(+), 23 deletions(-) diff --git a/wrapper/xgboost_wrapper.h b/wrapper/xgboost_wrapper.h index f236ee5da..78df68c71 100644 --- a/wrapper/xgboost_wrapper.h +++ b/wrapper/xgboost_wrapper.h @@ -1,7 +1,7 @@ #ifndef XGBOOST_WRAPPER_H_ #define XGBOOST_WRAPPER_H_ /*! - * \file xgboost_wrapperh + * \file xgboost_wrapper.h * \author Tianqi Chen * \brief a C style wrapper of xgboost * can be used to create wrapper of other languages @@ -17,28 +17,6 @@ typedef unsigned long bst_ulong; #ifdef __cplusplus extern "C" { #endif - /*! - * \brief initialize sync module, this is needed if used in distributed model - * normally, argv need to contain master_uri and master_port - * if start using submit_job_tcp script, then pass args to this will do - * \param argc number of arguments - * \param argv the arguments to be passed in sync module - */ - XGB_DLL void XGSyncInit(int argc, char *argv[]); - /*! - * \brief finalize sync module, call this when everything is done - */ - XGB_DLL void XGSyncFinalize(void); - /*! - * \brief get the rank - * \return return the rank of - */ - XGB_DLL int XGSyncGetRank(void); - /*! - * \brief get the world size from sync - * \return return the number of distributed job ran in the group - */ - XGB_DLL int XGSyncGetWorldSize(void); /*! * \brief load a data matrix * \return a loaded data matrix From 85186a2e55a91f08a49cee332a25968375cfb185 Mon Sep 17 00:00:00 2001 From: El Potaeto Date: Fri, 6 Feb 2015 11:44:09 +0100 Subject: [PATCH 67/94] remove buggy feature --- R-package/R/xgb.importance.R | 2 +- R-package/R/xgb.model.dt.tree.R | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/R-package/R/xgb.importance.R b/R-package/R/xgb.importance.R index 51cf2a258..710143fa1 100644 --- a/R-package/R/xgb.importance.R +++ b/R-package/R/xgb.importance.R @@ -73,7 +73,7 @@ xgb.importance <- function(feature_names = NULL, filename_dump = NULL, model = N } treeDump <- function(feature_names, text){ - result <- xgb.model.dt.tree(feature_names = feature_names, text = text)[Feature!="Leaf",.(Gain = sum(Quality), Cover = sum(Cover), Frequence = .N, Included = sum(Included)), by = Feature][,`:=`(Gain=Gain/sum(Gain),Cover=Cover/sum(Cover), Frequence = Frequence/sum(Frequence), Included = Included/Frequence)][,Gain:= ifelse(Included >= 0.5, Gain, -Gain)][order(-Gain)] + result <- xgb.model.dt.tree(feature_names = feature_names, text = text)[Feature!="Leaf",.(Gain = sum(Quality), Cover = sum(Cover), Frequence = .N), by = Feature][,`:=`(Gain = Gain/sum(Gain), Cover = Cover/sum(Cover), Frequence = Frequence/sum(Frequence))] result } diff --git a/R-package/R/xgb.model.dt.tree.R b/R-package/R/xgb.model.dt.tree.R index c88c16989..9c570c44b 100644 --- a/R-package/R/xgb.model.dt.tree.R +++ b/R-package/R/xgb.model.dt.tree.R @@ -39,7 +39,6 @@ #' \item \code{Cover}: metric to measure the number of observation affected by the split ; #' \item \code{Tree}: ID of the tree. It is included in the main ID ; #' \item \code{Yes.X} or \code{No.X}: data related to the pointer in \code{Yes} or \code{No} column ; -#' \item \code{Included}: \code{boolean} value which indicates if this feature has been pointed by a Yes branch (\code{True}) or a No branch (\code{False}). By convention stem feature is always included ; #' } #' #' @examples @@ -129,7 +128,7 @@ xgb.model.dt.tree <- function(feature_names = NULL, filename_dump = NULL, model qualityLeaf <- extract(leaf, "leaf=\\-*\\d*\\.*\\d*") coverBranch <- extract(branch, "cover=\\d*\\.*\\d*") coverLeaf <- extract(leaf, "cover=\\d*\\.*\\d*") - dt <- data.table(ID = c(idBranch, idLeaf), Feature = c(featureBranch, featureLeaf), Split = c(splitBranch, splitLeaf), Yes = c(yesBranch, yesLeaf), No = c(noBranch, noLeaf), Missing = c(missingBranch, missingLeaf), Quality = c(qualityBranch, qualityLeaf), Cover = c(coverBranch, coverLeaf))[order(ID)][,Tree:=treeID][,"Included":=F][ID == yesBranch, Included:=T][1, Included:=T] + dt <- data.table(ID = c(idBranch, idLeaf), Feature = c(featureBranch, featureLeaf), Split = c(splitBranch, splitLeaf), Yes = c(yesBranch, yesLeaf), No = c(noBranch, noLeaf), Missing = c(missingBranch, missingLeaf), Quality = c(qualityBranch, qualityLeaf), Cover = c(coverBranch, coverLeaf))[order(ID)][,Tree:=treeID] allTrees <- rbindlist(list(allTrees, dt), use.names = T, fill = F) } @@ -168,4 +167,4 @@ xgb.model.dt.tree <- function(feature_names = NULL, filename_dump = NULL, model # Avoid error messages during CRAN check. # The reason is that these variables are never declared # They are mainly column names inferred by Data.table... -globalVariables(c("ID", "Tree", "Yes", ".", ".N", "Feature", "Cover", "Quality", "No", "Gain", "Frequence", "Included")) \ No newline at end of file +globalVariables(c("ID", "Tree", "Yes", ".", ".N", "Feature", "Cover", "Quality", "No", "Gain", "Frequence")) \ No newline at end of file From 85739c537dbfdb516192fbb11990ce35523dbdd5 Mon Sep 17 00:00:00 2001 From: El Potaeto Date: Sat, 7 Feb 2015 23:40:49 +0100 Subject: [PATCH 68/94] new doc --- R-package/R/xgboost.R | 73 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 61 insertions(+), 12 deletions(-) diff --git a/R-package/R/xgboost.R b/R-package/R/xgboost.R index c72c4d5b0..b3c76af9d 100644 --- a/R-package/R/xgboost.R +++ b/R-package/R/xgboost.R @@ -6,20 +6,69 @@ #' \code{xgb.DMatrix}. #' @param label the response variable. User should not set this field, #' if data is local data file or \code{xgb.DMatrix}. -#' @param params the list of parameters. Commonly used ones are: +#' @param params the list of parameters. +#' +#' General Parameters +#' #' \itemize{ -#' \item \code{objective} objective function, common ones are -#' \itemize{ -#' \item \code{reg:linear} linear regression -#' \item \code{binary:logistic} logistic regression for classification -#' } -#' \item \code{eta} step size of each boosting step -#' \item \code{max.depth} maximum depth of the tree -#' \item \code{nthread} number of thread used in training, if not set, all threads are used +#' \item \code{booster} which booster to use, can be gbtree or gblinear. (default=gbtree) +#' \item \code{silent} 0 (default) means printing running messages, 1 means silent mode. +#' \item \code{nthread} number of parallel threads used to run xgboost. Default to maximum number of threads available if not set. +#' \item \code{num_pbuffer} size of prediction buffer, normally set to number of training instances. The buffers are used to save the prediction results of last boosting step. Default: set automatically by xgboost, no need to be set by user +#' \item \code{num_feature} feature dimension used in boosting, set to maximum dimension of the feature. Default: set automatically by xgboost, no need to be set by user. #' } -#' -#' See \url{https://github.com/tqchen/xgboost/wiki/Parameters} for -#' further details. See also demo/ for walkthrough example in R. +#' +#' Booster Parameters +#' +#' 1. Parameter for Tree Booster +#' +#' \itemize{ +#' \item \code{eta} step size shrinkage used in update to prevents overfitting. After each boosting step, we can directly get the weights of new features. and eta actually shrinkage the feature weights to make the boosting process more conservative. Default: 0.3 +#' \item \code{gamma} minimum loss reduction required to make a further partition on a leaf node of the tree. the larger, the more conservative the algorithm will be. +#' \item \code{max_depth} maximum depth of a tree. Default: 6 +#' \item \code{min_child_weight} minimum sum of instance weight(hessian) needed in a child. If the tree partition step results in a leaf node with the sum of instance weight less than min_child_weight, then the building process will give up further partitioning. In linear regression mode, this simply corresponds to minimum number of instances needed to be in each node. The larger, the more conservative the algorithm will be. Default: 1 +#' \item \code{subsample} subsample ratio of the training instance. Setting it to 0.5 means that XGBoost randomly collected half of the data instances to grow trees and this will prevent overfitting. Default: 1. +#' \item \code{colsample_bytree} subsample ratio of columns when constructing each tree. Default: 1. +#' } +#' +#' 2. Parameter for Linear Booster +#' +#' \itemize{ +#' \item \code{lambda} L2 regularization term on weights. Default: 0 +#' \item \code{lambda_bias} L2 regularization term on bias. Default: 0 +#' \item \code{alpha} L1 regularization term on weights. (there is no L1 reg on bias because it is not important). Default: 0 +#' } +#' +#' Task Parameters +#' +#' +#' \itemize{ +#' \item \code{objective} specify the learning task and the corresponding learning objective, and the objective options are below: +#' \itemize{ +#' \item \code{reg:linear} linear regression (Default). +#' \item \code{reg:logistic} logistic regression. +#' \item \code{binary:logistic} logistic regression for binary classification. Output probability. +#' \item \code{binary:logitraw} logistic regression for binary classification, output score before logistic transformation. +#' \item \code{multi:softmax} set XGBoost to do multiclass classification using the softmax objective, you also need to set num_class(number of classes). +#' \item \code{multi:softprob} same as softmax, but output a vector of ndata * nclass, which can be further reshaped to ndata, nclass matrix. The result contains predicted probability of each data point belonging to each class. +#' \item \code{rank:pairwise} set XGBoost to do ranking task by minimizing the pairwise loss +#' } +#' \item \code{base_score} the initial prediction score of all instances, global bias. Default: 0.5 +#' \item \code{eval_metric} evaluation metrics for validation data, a default metric will be assigned according to objective(rmse for regression, and error for classification, mean average precision for ranking). Default according to objective. The choices are listed below: +#' \itemize{ +#' \item \code{rmse} root mean square error. \url{http://en.wikipedia.org/wiki/Root_mean_square_error} +#' \item \code{logloss} negative log-likelihood. \url{http://en.wikipedia.org/wiki/Log-likelihood} +#' \item \code{error} Binary classification error rate. It is calculated as (wrong cases)/(all cases). For the predictions, the evaluation will regard the instances with prediction value larger than 0.5 as positive instances, and the others as negative instances. +#' \item \code{merror} Multiclass classification error rate. It is calculated as (wrong cases)/(all cases). +#' \item \code{auc} Area under the curve. \url{http://en.wikipedia.org/wiki/Receiver_operating_characteristic#'Area_under_curve} for ranking evaluation. +#' \item \code{ndcg} Normalized Discounted Cumulative Gain. \url{http://en.wikipedia.org/wiki/NDCG} +#' } +#' \item \code{map} Mean average precision. \url{http://en.wikipedia.org/wiki/Mean_average_precision#'Mean_average_precision} +#' \item \code{ndcg@n} and \code{map@n} n can be assigned as an integer to cut off the top positions in the lists for evaluation. +#' \item \code{ndcg-}, \code{map-}, \code{ndcg@n-}, \code{map@n-} In XGBoost, NDCG and MAP will evaluate the score of a list without any positive samples as 1. By adding "-" in the evaluation metric XGBoost will evaluate these score as 0 to be consistent under some conditions. Training repeatively. +#' \item \code{seed} random number seed. Default: 0 +#' } +#' #' @param nrounds the max number of iterations #' @param verbose If 0, xgboost will stay silent. If 1, xgboost will print #' information of performance. If 2, xgboost will print information of both From 75f205b0b1f88db58168a3658e83cde617711f36 Mon Sep 17 00:00:00 2001 From: El Potaeto Date: Sat, 7 Feb 2015 23:53:55 +0100 Subject: [PATCH 69/94] fix documentation --- R-package/R/xgboost.R | 29 ++++++------- R-package/man/xgb.model.dt.tree.Rd | 1 - R-package/man/xgboost.Rd | 69 +++++++++++++++++++++++++----- 3 files changed, 72 insertions(+), 27 deletions(-) diff --git a/R-package/R/xgboost.R b/R-package/R/xgboost.R index b3c76af9d..4e1861ae4 100644 --- a/R-package/R/xgboost.R +++ b/R-package/R/xgboost.R @@ -8,30 +8,30 @@ #' if data is local data file or \code{xgb.DMatrix}. #' @param params the list of parameters. #' -#' General Parameters +#' 1. General Parameters #' #' \itemize{ -#' \item \code{booster} which booster to use, can be gbtree or gblinear. (default=gbtree) -#' \item \code{silent} 0 (default) means printing running messages, 1 means silent mode. +#' \item \code{booster} which booster to use, can be gbtree or gblinear. Default: gbtree +#' \item \code{silent} 0 means printing running messages, 1 means silent mode. Default: 0 #' \item \code{nthread} number of parallel threads used to run xgboost. Default to maximum number of threads available if not set. #' \item \code{num_pbuffer} size of prediction buffer, normally set to number of training instances. The buffers are used to save the prediction results of last boosting step. Default: set automatically by xgboost, no need to be set by user #' \item \code{num_feature} feature dimension used in boosting, set to maximum dimension of the feature. Default: set automatically by xgboost, no need to be set by user. #' } #' -#' Booster Parameters +#' 2. Booster Parameters #' -#' 1. Parameter for Tree Booster +#' 2.1. Parameter for Tree Booster #' #' \itemize{ #' \item \code{eta} step size shrinkage used in update to prevents overfitting. After each boosting step, we can directly get the weights of new features. and eta actually shrinkage the feature weights to make the boosting process more conservative. Default: 0.3 #' \item \code{gamma} minimum loss reduction required to make a further partition on a leaf node of the tree. the larger, the more conservative the algorithm will be. #' \item \code{max_depth} maximum depth of a tree. Default: 6 #' \item \code{min_child_weight} minimum sum of instance weight(hessian) needed in a child. If the tree partition step results in a leaf node with the sum of instance weight less than min_child_weight, then the building process will give up further partitioning. In linear regression mode, this simply corresponds to minimum number of instances needed to be in each node. The larger, the more conservative the algorithm will be. Default: 1 -#' \item \code{subsample} subsample ratio of the training instance. Setting it to 0.5 means that XGBoost randomly collected half of the data instances to grow trees and this will prevent overfitting. Default: 1. -#' \item \code{colsample_bytree} subsample ratio of columns when constructing each tree. Default: 1. +#' \item \code{subsample} subsample ratio of the training instance. Setting it to 0.5 means that XGBoost randomly collected half of the data instances to grow trees and this will prevent overfitting. Default: 1 +#' \item \code{colsample_bytree} subsample ratio of columns when constructing each tree. Default: 1 #' } #' -#' 2. Parameter for Linear Booster +#' 2.2. Parameter for Linear Booster #' #' \itemize{ #' \item \code{lambda} L2 regularization term on weights. Default: 0 @@ -39,8 +39,7 @@ #' \item \code{alpha} L1 regularization term on weights. (there is no L1 reg on bias because it is not important). Default: 0 #' } #' -#' Task Parameters -#' +#' 3. Task Parameters #' #' \itemize{ #' \item \code{objective} specify the learning task and the corresponding learning objective, and the objective options are below: @@ -51,21 +50,21 @@ #' \item \code{binary:logitraw} logistic regression for binary classification, output score before logistic transformation. #' \item \code{multi:softmax} set XGBoost to do multiclass classification using the softmax objective, you also need to set num_class(number of classes). #' \item \code{multi:softprob} same as softmax, but output a vector of ndata * nclass, which can be further reshaped to ndata, nclass matrix. The result contains predicted probability of each data point belonging to each class. -#' \item \code{rank:pairwise} set XGBoost to do ranking task by minimizing the pairwise loss +#' \item \code{rank:pairwise} set XGBoost to do ranking task by minimizing the pairwise loss. #' } #' \item \code{base_score} the initial prediction score of all instances, global bias. Default: 0.5 #' \item \code{eval_metric} evaluation metrics for validation data, a default metric will be assigned according to objective(rmse for regression, and error for classification, mean average precision for ranking). Default according to objective. The choices are listed below: #' \itemize{ #' \item \code{rmse} root mean square error. \url{http://en.wikipedia.org/wiki/Root_mean_square_error} #' \item \code{logloss} negative log-likelihood. \url{http://en.wikipedia.org/wiki/Log-likelihood} -#' \item \code{error} Binary classification error rate. It is calculated as (wrong cases)/(all cases). For the predictions, the evaluation will regard the instances with prediction value larger than 0.5 as positive instances, and the others as negative instances. -#' \item \code{merror} Multiclass classification error rate. It is calculated as (wrong cases)/(all cases). +#' \item \code{error} Binary classification error rate. It is calculated as \code{(wrong cases) / (all cases)}. For the predictions, the evaluation will regard the instances with prediction value larger than 0.5 as positive instances, and the others as negative instances. +#' \item \code{merror} Multiclass classification error rate. It is calculated as \code{(wrong cases) / (all cases)}. #' \item \code{auc} Area under the curve. \url{http://en.wikipedia.org/wiki/Receiver_operating_characteristic#'Area_under_curve} for ranking evaluation. #' \item \code{ndcg} Normalized Discounted Cumulative Gain. \url{http://en.wikipedia.org/wiki/NDCG} #' } #' \item \code{map} Mean average precision. \url{http://en.wikipedia.org/wiki/Mean_average_precision#'Mean_average_precision} -#' \item \code{ndcg@n} and \code{map@n} n can be assigned as an integer to cut off the top positions in the lists for evaluation. -#' \item \code{ndcg-}, \code{map-}, \code{ndcg@n-}, \code{map@n-} In XGBoost, NDCG and MAP will evaluate the score of a list without any positive samples as 1. By adding "-" in the evaluation metric XGBoost will evaluate these score as 0 to be consistent under some conditions. Training repeatively. +#' \item \code{ndcg@@n} and \code{map@@n} n can be assigned as an integer to cut off the top positions in the lists for evaluation. +#' \item \code{ndcg-}, \code{map-}, \code{ndcg@@n-}, \code{map@@n-} In XGBoost, NDCG and MAP will evaluate the score of a list without any positive samples as 1. By adding "-" in the evaluation metric XGBoost will evaluate these score as 0 to be consistent under some conditions. Training repeatively. #' \item \code{seed} random number seed. Default: 0 #' } #' diff --git a/R-package/man/xgb.model.dt.tree.Rd b/R-package/man/xgb.model.dt.tree.Rd index 8b9eb6a13..604607209 100644 --- a/R-package/man/xgb.model.dt.tree.Rd +++ b/R-package/man/xgb.model.dt.tree.Rd @@ -40,7 +40,6 @@ The content of the \code{data.table} is organised that way: \item \code{Cover}: metric to measure the number of observation affected by the split ; \item \code{Tree}: ID of the tree. It is included in the main ID ; \item \code{Yes.X} or \code{No.X}: data related to the pointer in \code{Yes} or \code{No} column ; - \item \code{Included}: \code{boolean} value which indicates if this feature has been pointed by a Yes branch (\code{True}) or a No branch (\code{False}). By convention stem feature is always included ; } } \examples{ diff --git a/R-package/man/xgboost.Rd b/R-package/man/xgboost.Rd index 39364c64e..57ca5e469 100644 --- a/R-package/man/xgboost.Rd +++ b/R-package/man/xgboost.Rd @@ -17,20 +17,67 @@ if data is local data file or \code{xgb.DMatrix}.} \item{missing}{Missing is only used when input is dense matrix, pick a float value that represents missing value. Sometime a data use 0 or other extreme value to represents missing values.} -\item{params}{the list of parameters. Commonly used ones are: +\item{params}{the list of parameters. + +1. General Parameters + \itemize{ - \item \code{objective} objective function, common ones are - \itemize{ - \item \code{reg:linear} linear regression - \item \code{binary:logistic} logistic regression for classification - } - \item \code{eta} step size of each boosting step - \item \code{max.depth} maximum depth of the tree - \item \code{nthread} number of thread used in training, if not set, all threads are used + \item \code{booster} which booster to use, can be gbtree or gblinear. Default: gbtree + \item \code{silent} 0 means printing running messages, 1 means silent mode. Default: 0 + \item \code{nthread} number of parallel threads used to run xgboost. Default to maximum number of threads available if not set. + \item \code{num_pbuffer} size of prediction buffer, normally set to number of training instances. The buffers are used to save the prediction results of last boosting step. Default: set automatically by xgboost, no need to be set by user + \item \code{num_feature} feature dimension used in boosting, set to maximum dimension of the feature. Default: set automatically by xgboost, no need to be set by user. } - See \url{https://github.com/tqchen/xgboost/wiki/Parameters} for - further details. See also demo/ for walkthrough example in R.} +2. Booster Parameters + +2.1. Parameter for Tree Booster + +\itemize{ + \item \code{eta} step size shrinkage used in update to prevents overfitting. After each boosting step, we can directly get the weights of new features. and eta actually shrinkage the feature weights to make the boosting process more conservative. Default: 0.3 + \item \code{gamma} minimum loss reduction required to make a further partition on a leaf node of the tree. the larger, the more conservative the algorithm will be. + \item \code{max_depth} maximum depth of a tree. Default: 6 + \item \code{min_child_weight} minimum sum of instance weight(hessian) needed in a child. If the tree partition step results in a leaf node with the sum of instance weight less than min_child_weight, then the building process will give up further partitioning. In linear regression mode, this simply corresponds to minimum number of instances needed to be in each node. The larger, the more conservative the algorithm will be. Default: 1 + \item \code{subsample} subsample ratio of the training instance. Setting it to 0.5 means that XGBoost randomly collected half of the data instances to grow trees and this will prevent overfitting. Default: 1 + \item \code{colsample_bytree} subsample ratio of columns when constructing each tree. Default: 1 +} + +2.2. Parameter for Linear Booster + +\itemize{ + \item \code{lambda} L2 regularization term on weights. Default: 0 + \item \code{lambda_bias} L2 regularization term on bias. Default: 0 + \item \code{alpha} L1 regularization term on weights. (there is no L1 reg on bias because it is not important). Default: 0 +} + +3. Task Parameters + +\itemize{ +\item \code{objective} specify the learning task and the corresponding learning objective, and the objective options are below: + \itemize{ + \item \code{reg:linear} linear regression (Default). + \item \code{reg:logistic} logistic regression. + \item \code{binary:logistic} logistic regression for binary classification. Output probability. + \item \code{binary:logitraw} logistic regression for binary classification, output score before logistic transformation. + \item \code{multi:softmax} set XGBoost to do multiclass classification using the softmax objective, you also need to set num_class(number of classes). + \item \code{multi:softprob} same as softmax, but output a vector of ndata * nclass, which can be further reshaped to ndata, nclass matrix. The result contains predicted probability of each data point belonging to each class. + \item \code{rank:pairwise} set XGBoost to do ranking task by minimizing the pairwise loss. + } + \item \code{base_score} the initial prediction score of all instances, global bias. Default: 0.5 + \item \code{eval_metric} evaluation metrics for validation data, a default metric will be assigned according to objective(rmse for regression, and error for classification, mean average precision for ranking). Default according to objective. The choices are listed below: + \itemize{ + \item \code{rmse} root mean square error. \url{http://en.wikipedia.org/wiki/Root_mean_square_error} + \item \code{logloss} negative log-likelihood. \url{http://en.wikipedia.org/wiki/Log-likelihood} + \item \code{error} Binary classification error rate. It is calculated as \code{(wrong cases) / (all cases)}. For the predictions, the evaluation will regard the instances with prediction value larger than 0.5 as positive instances, and the others as negative instances. + \item \code{merror} Multiclass classification error rate. It is calculated as \code{(wrong cases) / (all cases)}. + \item \code{auc} Area under the curve. \url{http://en.wikipedia.org/wiki/Receiver_operating_characteristic#'Area_under_curve} for ranking evaluation. + \item \code{ndcg} Normalized Discounted Cumulative Gain. \url{http://en.wikipedia.org/wiki/NDCG} + } + \item \code{map} Mean average precision. \url{http://en.wikipedia.org/wiki/Mean_average_precision#'Mean_average_precision} + \item \code{ndcg@n} and \code{map@n} n can be assigned as an integer to cut off the top positions in the lists for evaluation. + \item \code{ndcg-}, \code{map-}, \code{ndcg@n-}, \code{map@n-} In XGBoost, NDCG and MAP will evaluate the score of a list without any positive samples as 1. By adding "-" in the evaluation metric XGBoost will evaluate these score as 0 to be consistent under some conditions. Training repeatively. + \item \code{seed} random number seed. Default: 0 +}} \item{nrounds}{the max number of iterations} From 12b0e8e6d583b1e2d19b9e903a146a1cc19e09d4 Mon Sep 17 00:00:00 2001 From: El Potaeto Date: Sat, 7 Feb 2015 23:57:48 +0100 Subject: [PATCH 70/94] small doc fix --- R-package/man/xgboost.Rd | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/R-package/man/xgboost.Rd b/R-package/man/xgboost.Rd index 57ca5e469..6a5e72264 100644 --- a/R-package/man/xgboost.Rd +++ b/R-package/man/xgboost.Rd @@ -22,7 +22,7 @@ value that represents missing value. Sometime a data use 0 or other extreme valu 1. General Parameters \itemize{ - \item \code{booster} which booster to use, can be gbtree or gblinear. Default: gbtree + \item \code{booster} which booster to use, can be \code{gbtree} or \code{gblinear}. Default: \code{gbtree} \item \code{silent} 0 means printing running messages, 1 means silent mode. Default: 0 \item \code{nthread} number of parallel threads used to run xgboost. Default to maximum number of threads available if not set. \item \code{num_pbuffer} size of prediction buffer, normally set to number of training instances. The buffers are used to save the prediction results of last boosting step. Default: set automatically by xgboost, no need to be set by user @@ -38,7 +38,7 @@ value that represents missing value. Sometime a data use 0 or other extreme valu \item \code{gamma} minimum loss reduction required to make a further partition on a leaf node of the tree. the larger, the more conservative the algorithm will be. \item \code{max_depth} maximum depth of a tree. Default: 6 \item \code{min_child_weight} minimum sum of instance weight(hessian) needed in a child. If the tree partition step results in a leaf node with the sum of instance weight less than min_child_weight, then the building process will give up further partitioning. In linear regression mode, this simply corresponds to minimum number of instances needed to be in each node. The larger, the more conservative the algorithm will be. Default: 1 - \item \code{subsample} subsample ratio of the training instance. Setting it to 0.5 means that XGBoost randomly collected half of the data instances to grow trees and this will prevent overfitting. Default: 1 + \item \code{subsample} subsample ratio of the training instance. Setting it to 0.5 means that xgboost randomly collected half of the data instances to grow trees and this will prevent overfitting. Default: 1 \item \code{colsample_bytree} subsample ratio of columns when constructing each tree. Default: 1 } @@ -59,9 +59,9 @@ value that represents missing value. Sometime a data use 0 or other extreme valu \item \code{reg:logistic} logistic regression. \item \code{binary:logistic} logistic regression for binary classification. Output probability. \item \code{binary:logitraw} logistic regression for binary classification, output score before logistic transformation. - \item \code{multi:softmax} set XGBoost to do multiclass classification using the softmax objective, you also need to set num_class(number of classes). + \item \code{multi:softmax} set xgboost to do multiclass classification using the softmax objective, you also need to set num_class(number of classes). \item \code{multi:softprob} same as softmax, but output a vector of ndata * nclass, which can be further reshaped to ndata, nclass matrix. The result contains predicted probability of each data point belonging to each class. - \item \code{rank:pairwise} set XGBoost to do ranking task by minimizing the pairwise loss. + \item \code{rank:pairwise} set xgboost to do ranking task by minimizing the pairwise loss. } \item \code{base_score} the initial prediction score of all instances, global bias. Default: 0.5 \item \code{eval_metric} evaluation metrics for validation data, a default metric will be assigned according to objective(rmse for regression, and error for classification, mean average precision for ranking). Default according to objective. The choices are listed below: @@ -75,7 +75,7 @@ value that represents missing value. Sometime a data use 0 or other extreme valu } \item \code{map} Mean average precision. \url{http://en.wikipedia.org/wiki/Mean_average_precision#'Mean_average_precision} \item \code{ndcg@n} and \code{map@n} n can be assigned as an integer to cut off the top positions in the lists for evaluation. - \item \code{ndcg-}, \code{map-}, \code{ndcg@n-}, \code{map@n-} In XGBoost, NDCG and MAP will evaluate the score of a list without any positive samples as 1. By adding "-" in the evaluation metric XGBoost will evaluate these score as 0 to be consistent under some conditions. Training repeatively. + \item \code{ndcg-}, \code{map-}, \code{ndcg@n-}, \code{map@n-} In xgboost, NDCG and MAP will evaluate the score of a list without any positive samples as 1. By adding "-" in the evaluation metric xgboost will evaluate these score as 0 to be consistent under some conditions. Training repeatively. \item \code{seed} random number seed. Default: 0 }} From 9d89441e38a23f02429fe388229e737a5661dde4 Mon Sep 17 00:00:00 2001 From: El Potaeto Date: Sat, 7 Feb 2015 23:58:09 +0100 Subject: [PATCH 71/94] small doc fix --- R-package/R/xgboost.R | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/R-package/R/xgboost.R b/R-package/R/xgboost.R index 4e1861ae4..d60c58d5f 100644 --- a/R-package/R/xgboost.R +++ b/R-package/R/xgboost.R @@ -11,7 +11,7 @@ #' 1. General Parameters #' #' \itemize{ -#' \item \code{booster} which booster to use, can be gbtree or gblinear. Default: gbtree +#' \item \code{booster} which booster to use, can be \code{gbtree} or \code{gblinear}. Default: \code{gbtree} #' \item \code{silent} 0 means printing running messages, 1 means silent mode. Default: 0 #' \item \code{nthread} number of parallel threads used to run xgboost. Default to maximum number of threads available if not set. #' \item \code{num_pbuffer} size of prediction buffer, normally set to number of training instances. The buffers are used to save the prediction results of last boosting step. Default: set automatically by xgboost, no need to be set by user @@ -27,7 +27,7 @@ #' \item \code{gamma} minimum loss reduction required to make a further partition on a leaf node of the tree. the larger, the more conservative the algorithm will be. #' \item \code{max_depth} maximum depth of a tree. Default: 6 #' \item \code{min_child_weight} minimum sum of instance weight(hessian) needed in a child. If the tree partition step results in a leaf node with the sum of instance weight less than min_child_weight, then the building process will give up further partitioning. In linear regression mode, this simply corresponds to minimum number of instances needed to be in each node. The larger, the more conservative the algorithm will be. Default: 1 -#' \item \code{subsample} subsample ratio of the training instance. Setting it to 0.5 means that XGBoost randomly collected half of the data instances to grow trees and this will prevent overfitting. Default: 1 +#' \item \code{subsample} subsample ratio of the training instance. Setting it to 0.5 means that xgboost randomly collected half of the data instances to grow trees and this will prevent overfitting. Default: 1 #' \item \code{colsample_bytree} subsample ratio of columns when constructing each tree. Default: 1 #' } #' @@ -48,9 +48,9 @@ #' \item \code{reg:logistic} logistic regression. #' \item \code{binary:logistic} logistic regression for binary classification. Output probability. #' \item \code{binary:logitraw} logistic regression for binary classification, output score before logistic transformation. -#' \item \code{multi:softmax} set XGBoost to do multiclass classification using the softmax objective, you also need to set num_class(number of classes). +#' \item \code{multi:softmax} set xgboost to do multiclass classification using the softmax objective, you also need to set num_class(number of classes). #' \item \code{multi:softprob} same as softmax, but output a vector of ndata * nclass, which can be further reshaped to ndata, nclass matrix. The result contains predicted probability of each data point belonging to each class. -#' \item \code{rank:pairwise} set XGBoost to do ranking task by minimizing the pairwise loss. +#' \item \code{rank:pairwise} set xgboost to do ranking task by minimizing the pairwise loss. #' } #' \item \code{base_score} the initial prediction score of all instances, global bias. Default: 0.5 #' \item \code{eval_metric} evaluation metrics for validation data, a default metric will be assigned according to objective(rmse for regression, and error for classification, mean average precision for ranking). Default according to objective. The choices are listed below: @@ -64,7 +64,7 @@ #' } #' \item \code{map} Mean average precision. \url{http://en.wikipedia.org/wiki/Mean_average_precision#'Mean_average_precision} #' \item \code{ndcg@@n} and \code{map@@n} n can be assigned as an integer to cut off the top positions in the lists for evaluation. -#' \item \code{ndcg-}, \code{map-}, \code{ndcg@@n-}, \code{map@@n-} In XGBoost, NDCG and MAP will evaluate the score of a list without any positive samples as 1. By adding "-" in the evaluation metric XGBoost will evaluate these score as 0 to be consistent under some conditions. Training repeatively. +#' \item \code{ndcg-}, \code{map-}, \code{ndcg@@n-}, \code{map@@n-} In xgboost, NDCG and MAP will evaluate the score of a list without any positive samples as 1. By adding "-" in the evaluation metric xgboost will evaluate these score as 0 to be consistent under some conditions. Training repeatively. #' \item \code{seed} random number seed. Default: 0 #' } #' From 29b5312428dbdb3a4deddec2852d84115de9f4c7 Mon Sep 17 00:00:00 2001 From: El Potaeto Date: Sun, 8 Feb 2015 00:02:53 +0100 Subject: [PATCH 72/94] remove not required dependency --- R-package/R/xgb.model.dt.tree.R | 1 - 1 file changed, 1 deletion(-) diff --git a/R-package/R/xgb.model.dt.tree.R b/R-package/R/xgb.model.dt.tree.R index 9c570c44b..42b3657b0 100644 --- a/R-package/R/xgb.model.dt.tree.R +++ b/R-package/R/xgb.model.dt.tree.R @@ -14,7 +14,6 @@ #' @importFrom stringr str_split #' @importFrom stringr str_extract #' @importFrom stringr str_trim -#' @importFrom stringr str_detect #' @param feature_names names of each feature as a character vector. Can be extracted from a sparse matrix (see example). If model dump already contains feature names, this argument should be \code{NULL}. #' @param filename_dump the path to the text file storing the model. Model dump must include the gain per feature and per tree (parameter \code{with.stats = T} in function \code{xgb.dump}). #' @param model dump generated by the \code{xgb.train} function. Avoid the creation of a dump file. From 76e24fdd36648da21c46176c214df5481e40c52b Mon Sep 17 00:00:00 2001 From: El Potaeto Date: Sun, 8 Feb 2015 22:46:29 +0100 Subject: [PATCH 73/94] documentation simplification --- R-package/NAMESPACE | 1 - R-package/R/xgboost.R | 43 +++++++++++++++++++--------------------- R-package/man/xgboost.Rd | 33 ++++++++++++++---------------- 3 files changed, 35 insertions(+), 42 deletions(-) diff --git a/R-package/NAMESPACE b/R-package/NAMESPACE index 7d9c64563..fab1546a2 100644 --- a/R-package/NAMESPACE +++ b/R-package/NAMESPACE @@ -41,7 +41,6 @@ importFrom(ggplot2,ylab) importFrom(magrittr,"%>%") importFrom(magrittr,add) importFrom(magrittr,not) -importFrom(stringr,str_detect) importFrom(stringr,str_extract) importFrom(stringr,str_extract_all) importFrom(stringr,str_match) diff --git a/R-package/R/xgboost.R b/R-package/R/xgboost.R index d60c58d5f..3d5ad1d89 100644 --- a/R-package/R/xgboost.R +++ b/R-package/R/xgboost.R @@ -13,9 +13,6 @@ #' \itemize{ #' \item \code{booster} which booster to use, can be \code{gbtree} or \code{gblinear}. Default: \code{gbtree} #' \item \code{silent} 0 means printing running messages, 1 means silent mode. Default: 0 -#' \item \code{nthread} number of parallel threads used to run xgboost. Default to maximum number of threads available if not set. -#' \item \code{num_pbuffer} size of prediction buffer, normally set to number of training instances. The buffers are used to save the prediction results of last boosting step. Default: set automatically by xgboost, no need to be set by user -#' \item \code{num_feature} feature dimension used in boosting, set to maximum dimension of the feature. Default: set automatically by xgboost, no need to be set by user. #' } #' #' 2. Booster Parameters @@ -53,7 +50,24 @@ #' \item \code{rank:pairwise} set xgboost to do ranking task by minimizing the pairwise loss. #' } #' \item \code{base_score} the initial prediction score of all instances, global bias. Default: 0.5 -#' \item \code{eval_metric} evaluation metrics for validation data, a default metric will be assigned according to objective(rmse for regression, and error for classification, mean average precision for ranking). Default according to objective. The choices are listed below: +#' \item \code{eval_metric} evaluation metrics for validation data. Default: metric will be assigned according to objective(rmse for regression, and error for classification, mean average precision for ranking). List is provided in detail section. +#' } +#' +#' @param nrounds the max number of iterations +#' @param verbose If 0, xgboost will stay silent. If 1, xgboost will print +#' information of performance. If 2, xgboost will print information of both +#' performance and construction progress information +#' @param missing Missing is only used when input is dense matrix, pick a float +#' value that represents missing value. Sometimes a data use 0 or other extreme value to represents missing values. +#' @param ... other parameters to pass to \code{params}. +#' +#' @details +#' This is the modeling function for xgboost. +#' +#' Parallelization is automatically enabled if OpenMP is present. +#' Number of threads can also be manually specified via "nthread" parameter. +#' +#' \code{eval_metric} is set automatically by xgboost but can be overriden by parameter. Below is provided the list of different metric optimized by xgboost to help you to understand how it works inside. It should not be overriden until you have a real reason to do so. #' \itemize{ #' \item \code{rmse} root mean square error. \url{http://en.wikipedia.org/wiki/Root_mean_square_error} #' \item \code{logloss} negative log-likelihood. \url{http://en.wikipedia.org/wiki/Log-likelihood} @@ -62,25 +76,8 @@ #' \item \code{auc} Area under the curve. \url{http://en.wikipedia.org/wiki/Receiver_operating_characteristic#'Area_under_curve} for ranking evaluation. #' \item \code{ndcg} Normalized Discounted Cumulative Gain. \url{http://en.wikipedia.org/wiki/NDCG} #' } -#' \item \code{map} Mean average precision. \url{http://en.wikipedia.org/wiki/Mean_average_precision#'Mean_average_precision} -#' \item \code{ndcg@@n} and \code{map@@n} n can be assigned as an integer to cut off the top positions in the lists for evaluation. -#' \item \code{ndcg-}, \code{map-}, \code{ndcg@@n-}, \code{map@@n-} In xgboost, NDCG and MAP will evaluate the score of a list without any positive samples as 1. By adding "-" in the evaluation metric xgboost will evaluate these score as 0 to be consistent under some conditions. Training repeatively. -#' \item \code{seed} random number seed. Default: 0 -#' } -#' -#' @param nrounds the max number of iterations -#' @param verbose If 0, xgboost will stay silent. If 1, xgboost will print -#' information of performance. If 2, xgboost will print information of both -#' performance and construction progress information -#' @param missing Missing is only used when input is dense matrix, pick a float -#' value that represents missing value. Sometime a data use 0 or other extreme value to represents missing values. -#' @param ... other parameters to pass to \code{params}. -#' -#' @details -#' This is the modeling function for xgboost. -#' -#' Parallelization is automatically enabled if OpenMP is present. -#' Number of threads can also be manually specified via "nthread" parameter +#' +#' More parameters are available in the Wiki \url{https://github.com/tqchen/xgboost/wiki/Parameters} #' #' @examples #' data(agaricus.train, package='xgboost') diff --git a/R-package/man/xgboost.Rd b/R-package/man/xgboost.Rd index 6a5e72264..8c8a384f4 100644 --- a/R-package/man/xgboost.Rd +++ b/R-package/man/xgboost.Rd @@ -15,7 +15,7 @@ xgboost(data = NULL, label = NULL, missing = NULL, params = list(), if data is local data file or \code{xgb.DMatrix}.} \item{missing}{Missing is only used when input is dense matrix, pick a float -value that represents missing value. Sometime a data use 0 or other extreme value to represents missing values.} +value that represents missing value. Sometimes a data use 0 or other extreme value to represents missing values.} \item{params}{the list of parameters. @@ -24,9 +24,6 @@ value that represents missing value. Sometime a data use 0 or other extreme valu \itemize{ \item \code{booster} which booster to use, can be \code{gbtree} or \code{gblinear}. Default: \code{gbtree} \item \code{silent} 0 means printing running messages, 1 means silent mode. Default: 0 - \item \code{nthread} number of parallel threads used to run xgboost. Default to maximum number of threads available if not set. - \item \code{num_pbuffer} size of prediction buffer, normally set to number of training instances. The buffers are used to save the prediction results of last boosting step. Default: set automatically by xgboost, no need to be set by user - \item \code{num_feature} feature dimension used in boosting, set to maximum dimension of the feature. Default: set automatically by xgboost, no need to be set by user. } 2. Booster Parameters @@ -64,19 +61,7 @@ value that represents missing value. Sometime a data use 0 or other extreme valu \item \code{rank:pairwise} set xgboost to do ranking task by minimizing the pairwise loss. } \item \code{base_score} the initial prediction score of all instances, global bias. Default: 0.5 - \item \code{eval_metric} evaluation metrics for validation data, a default metric will be assigned according to objective(rmse for regression, and error for classification, mean average precision for ranking). Default according to objective. The choices are listed below: - \itemize{ - \item \code{rmse} root mean square error. \url{http://en.wikipedia.org/wiki/Root_mean_square_error} - \item \code{logloss} negative log-likelihood. \url{http://en.wikipedia.org/wiki/Log-likelihood} - \item \code{error} Binary classification error rate. It is calculated as \code{(wrong cases) / (all cases)}. For the predictions, the evaluation will regard the instances with prediction value larger than 0.5 as positive instances, and the others as negative instances. - \item \code{merror} Multiclass classification error rate. It is calculated as \code{(wrong cases) / (all cases)}. - \item \code{auc} Area under the curve. \url{http://en.wikipedia.org/wiki/Receiver_operating_characteristic#'Area_under_curve} for ranking evaluation. - \item \code{ndcg} Normalized Discounted Cumulative Gain. \url{http://en.wikipedia.org/wiki/NDCG} - } - \item \code{map} Mean average precision. \url{http://en.wikipedia.org/wiki/Mean_average_precision#'Mean_average_precision} - \item \code{ndcg@n} and \code{map@n} n can be assigned as an integer to cut off the top positions in the lists for evaluation. - \item \code{ndcg-}, \code{map-}, \code{ndcg@n-}, \code{map@n-} In xgboost, NDCG and MAP will evaluate the score of a list without any positive samples as 1. By adding "-" in the evaluation metric xgboost will evaluate these score as 0 to be consistent under some conditions. Training repeatively. - \item \code{seed} random number seed. Default: 0 + \item \code{eval_metric} evaluation metrics for validation data. Default: metric will be assigned according to objective(rmse for regression, and error for classification, mean average precision for ranking). List is provided in detail section. }} \item{nrounds}{the max number of iterations} @@ -94,7 +79,19 @@ A simple interface for xgboost in R This is the modeling function for xgboost. Parallelization is automatically enabled if OpenMP is present. -Number of threads can also be manually specified via "nthread" parameter +Number of threads can also be manually specified via "nthread" parameter. + +\code{eval_metric} is set automatically by xgboost but can be overriden by parameter. Below is provided the list of different metric optimized by xgboost to help you to understand how it works inside. It should not be overriden until you have a real reason to do so. + \itemize{ + \item \code{rmse} root mean square error. \url{http://en.wikipedia.org/wiki/Root_mean_square_error} + \item \code{logloss} negative log-likelihood. \url{http://en.wikipedia.org/wiki/Log-likelihood} + \item \code{error} Binary classification error rate. It is calculated as \code{(wrong cases) / (all cases)}. For the predictions, the evaluation will regard the instances with prediction value larger than 0.5 as positive instances, and the others as negative instances. + \item \code{merror} Multiclass classification error rate. It is calculated as \code{(wrong cases) / (all cases)}. + \item \code{auc} Area under the curve. \url{http://en.wikipedia.org/wiki/Receiver_operating_characteristic#'Area_under_curve} for ranking evaluation. + \item \code{ndcg} Normalized Discounted Cumulative Gain. \url{http://en.wikipedia.org/wiki/NDCG} + } + +More parameters are available in the Wiki \url{https://github.com/tqchen/xgboost/wiki/Parameters} } \examples{ data(agaricus.train, package='xgboost') From a45497e6f337c4d99726193132c19a943a77def9 Mon Sep 17 00:00:00 2001 From: El Potaeto Date: Sun, 8 Feb 2015 22:46:59 +0100 Subject: [PATCH 74/94] add web address --- R-package/R/xgboost.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R-package/R/xgboost.R b/R-package/R/xgboost.R index 3d5ad1d89..3b01868f9 100644 --- a/R-package/R/xgboost.R +++ b/R-package/R/xgboost.R @@ -77,7 +77,7 @@ #' \item \code{ndcg} Normalized Discounted Cumulative Gain. \url{http://en.wikipedia.org/wiki/NDCG} #' } #' -#' More parameters are available in the Wiki \url{https://github.com/tqchen/xgboost/wiki/Parameters} +#' More parameters are available in the Wiki \url{https://github.com/tqchen/xgboost/wiki/Parameters}. #' #' @examples #' data(agaricus.train, package='xgboost') From 092288325090e9c97987a43ae6f390737aa37444 Mon Sep 17 00:00:00 2001 From: El Potaeto Date: Mon, 9 Feb 2015 17:20:21 +0100 Subject: [PATCH 75/94] Optimization in dump function (replaced some regular R function by data.table) --- R-package/R/xgb.dump.R | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/R-package/R/xgb.dump.R b/R-package/R/xgb.dump.R index a0938ded1..7cb1c524a 100644 --- a/R-package/R/xgb.dump.R +++ b/R-package/R/xgb.dump.R @@ -46,12 +46,15 @@ xgb.dump <- function(model = NULL, fname = NULL, fmap = "", with.stats=FALSE) { stop("fmap: argument must be type character (when provided)") } - result <- .Call("XGBoosterDumpModel_R", model, fmap, as.integer(with.stats), PACKAGE = "xgboost") + dt <- .Call("XGBoosterDumpModel_R", model, fmap, as.integer(with.stats), PACKAGE = "xgboost") %>% fread + + setnames(dt, "Content") if(is.null(fname)) { - return(str_split(result, "\n") %>% unlist %>% str_replace("^\t+","") %>% Filter(function(x) x != "", .)) + result <- dt[Content != "0"][,Content := str_replace(Content, "^\t+", "")][Content != ""][,paste(Content)] + return(result) } else { - result %>% str_split("\n") %>% unlist %>% Filter(function(x) x != "", .) %>% writeLines(fname) + result <- dt[Content != "0"][Content != ""][,paste(Content)] %>% writeLines(fname) return(TRUE) } } From 3971323203fc3e0c841b91d303a7bfdbb2a5d4fe Mon Sep 17 00:00:00 2001 From: El Potaeto Date: Mon, 9 Feb 2015 18:01:14 +0100 Subject: [PATCH 76/94] fix bug --- R-package/NAMESPACE | 1 + R-package/R/xgb.dump.R | 8 ++++++-- R-package/man/xgboost.Rd | 2 +- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/R-package/NAMESPACE b/R-package/NAMESPACE index fab1546a2..cce209811 100644 --- a/R-package/NAMESPACE +++ b/R-package/NAMESPACE @@ -25,6 +25,7 @@ importFrom(data.table,":=") importFrom(data.table,as.data.table) importFrom(data.table,copy) importFrom(data.table,data.table) +importFrom(data.table,fread) importFrom(data.table,rbindlist) importFrom(data.table,set) importFrom(data.table,setnames) diff --git a/R-package/R/xgb.dump.R b/R-package/R/xgb.dump.R index 7cb1c524a..cc22bb3a7 100644 --- a/R-package/R/xgb.dump.R +++ b/R-package/R/xgb.dump.R @@ -3,8 +3,10 @@ #' Save a xgboost model to text file. Could be parsed later. #' #' @importFrom magrittr %>% -#' @importFrom stringr str_split #' @importFrom stringr str_replace +#' @importFrom data.table fread +#' @importFrom data.table := +#' @importFrom data.table setnames #' @param model the model object. #' @param fname the name of the text file where to save the model text dump. If not provided or set to \code{NULL} the function will return the model as a \code{character} vector. #' @param fmap feature map file representing the type of feature. @@ -46,7 +48,9 @@ xgb.dump <- function(model = NULL, fname = NULL, fmap = "", with.stats=FALSE) { stop("fmap: argument must be type character (when provided)") } - dt <- .Call("XGBoosterDumpModel_R", model, fmap, as.integer(with.stats), PACKAGE = "xgboost") %>% fread + longString <- .Call("XGBoosterDumpModel_R", model, fmap, as.integer(with.stats), PACKAGE = "xgboost") + + dt <- fread(paste(longString, collapse = ""), sep = "\n", header = F) setnames(dt, "Content") diff --git a/R-package/man/xgboost.Rd b/R-package/man/xgboost.Rd index 8c8a384f4..59480e36c 100644 --- a/R-package/man/xgboost.Rd +++ b/R-package/man/xgboost.Rd @@ -91,7 +91,7 @@ Number of threads can also be manually specified via "nthread" parameter. \item \code{ndcg} Normalized Discounted Cumulative Gain. \url{http://en.wikipedia.org/wiki/NDCG} } -More parameters are available in the Wiki \url{https://github.com/tqchen/xgboost/wiki/Parameters} +More parameters are available in the Wiki \url{https://github.com/tqchen/xgboost/wiki/Parameters}. } \examples{ data(agaricus.train, package='xgboost') From f4b454d6ddbb7b8fa91266bd126140c044dab42f Mon Sep 17 00:00:00 2001 From: pommedeterresautee Date: Mon, 9 Feb 2015 21:34:53 +0100 Subject: [PATCH 77/94] fix some warning in Cran check --- R-package/R/xgb.dump.R | 8 ++++---- R-package/man/xgb.save.raw.Rd | 27 +++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 4 deletions(-) create mode 100644 R-package/man/xgb.save.raw.Rd diff --git a/R-package/R/xgb.dump.R b/R-package/R/xgb.dump.R index cc22bb3a7..edeb03b5f 100644 --- a/R-package/R/xgb.dump.R +++ b/R-package/R/xgb.dump.R @@ -52,13 +52,13 @@ xgb.dump <- function(model = NULL, fname = NULL, fmap = "", with.stats=FALSE) { dt <- fread(paste(longString, collapse = ""), sep = "\n", header = F) - setnames(dt, "Content") + setnames(dt, "Lines") if(is.null(fname)) { - result <- dt[Content != "0"][,Content := str_replace(Content, "^\t+", "")][Content != ""][,paste(Content)] + result <- dt[Lines != "0"][, Lines := str_replace(Lines, "^\t+", "")][Lines != ""][, paste(Lines)] return(result) } else { - result <- dt[Content != "0"][Content != ""][,paste(Content)] %>% writeLines(fname) + result <- dt[Lines != "0"][Lines != ""][, paste(Lines)] %>% writeLines(fname) return(TRUE) } } @@ -66,4 +66,4 @@ xgb.dump <- function(model = NULL, fname = NULL, fmap = "", with.stats=FALSE) { # Avoid error messages during CRAN check. # The reason is that these variables are never declared # They are mainly column names inferred by Data.table... -globalVariables(".") \ No newline at end of file +globalVariables(c("Lines", ".")) \ No newline at end of file diff --git a/R-package/man/xgb.save.raw.Rd b/R-package/man/xgb.save.raw.Rd new file mode 100644 index 000000000..15ec30636 --- /dev/null +++ b/R-package/man/xgb.save.raw.Rd @@ -0,0 +1,27 @@ +% Generated by roxygen2 (4.1.0): do not edit by hand +% Please edit documentation in R/xgb.save.raw.R +\name{xgb.save.raw} +\alias{xgb.save.raw} +\title{Save xgboost model to R's raw vector, +user can call xgb.load to load the model back from raw vector} +\usage{ +xgb.save.raw(model) +} +\arguments{ +\item{model}{the model object.} +} +\description{ +Save xgboost model from xgboost or xgb.train +} +\examples{ +data(agaricus.train, package='xgboost') +data(agaricus.test, package='xgboost') +train <- agaricus.train +test <- agaricus.test +bst <- xgboost(data = train$data, label = train$label, max.depth = 2, + eta = 1, nround = 2,objective = "binary:logistic") +raw <- xgb.save(bst) +bst <- xgb.load(raw) +pred <- predict(bst, test$data) +} + From eecfd015fac1f14a3aa4277448de1534df7403af Mon Sep 17 00:00:00 2001 From: pommedeterresautee Date: Mon, 9 Feb 2015 21:37:31 +0100 Subject: [PATCH 78/94] Update CK.means version --- R-package/DESCRIPTION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R-package/DESCRIPTION b/R-package/DESCRIPTION index 0e62ac423..de91c5edc 100644 --- a/R-package/DESCRIPTION +++ b/R-package/DESCRIPTION @@ -29,4 +29,4 @@ Imports: stringr (>= 0.6.2), DiagrammeR (>= 0.4), ggplot2 (>= 1.0.0), - Ckmeans.1d.dp (>= 3.02) + Ckmeans.1d.dp (>= 3.3.0) From 8c16491b4239476d6a4ad843d3c2f37eb942ada2 Mon Sep 17 00:00:00 2001 From: Tong He Date: Mon, 9 Feb 2015 13:31:21 -0800 Subject: [PATCH 79/94] Update xgb.save.raw.R --- R-package/R/xgb.save.raw.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R-package/R/xgb.save.raw.R b/R-package/R/xgb.save.raw.R index 11fc44470..d8ed6f526 100644 --- a/R-package/R/xgb.save.raw.R +++ b/R-package/R/xgb.save.raw.R @@ -12,7 +12,7 @@ #' test <- agaricus.test #' bst <- xgboost(data = train$data, label = train$label, max.depth = 2, #' eta = 1, nround = 2,objective = "binary:logistic") -#' raw <- xgb.save(bst) +#' raw <- xgb.save.raw(bst) #' bst <- xgb.load(raw) #' pred <- predict(bst, test$data) #' @export From ea5860d574126400933d0dd06f754932da99dd72 Mon Sep 17 00:00:00 2001 From: hetong007 Date: Mon, 9 Feb 2015 13:43:32 -0800 Subject: [PATCH 80/94] fix save.raw doc --- R-package/man/xgb.save.raw.Rd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R-package/man/xgb.save.raw.Rd b/R-package/man/xgb.save.raw.Rd index 15ec30636..f169a3d3d 100644 --- a/R-package/man/xgb.save.raw.Rd +++ b/R-package/man/xgb.save.raw.Rd @@ -20,7 +20,7 @@ train <- agaricus.train test <- agaricus.test bst <- xgboost(data = train$data, label = train$label, max.depth = 2, eta = 1, nround = 2,objective = "binary:logistic") -raw <- xgb.save(bst) +raw <- xgb.save.raw(bst) bst <- xgb.load(raw) pred <- predict(bst, test$data) } From 5b611c355e61546b7c70d4f6a86b387e50a2b17e Mon Sep 17 00:00:00 2001 From: hetong007 Date: Mon, 9 Feb 2015 15:51:24 -0800 Subject: [PATCH 81/94] add handle and raw structure to xgb.Booster --- R-package/R/predict.xgb.Booster.R | 17 +++++++++++++++-- R-package/R/utils.R | 8 ++++---- R-package/R/xgb.dump.R | 9 ++++++++- R-package/R/xgb.load.R | 6 +++++- R-package/R/xgb.save.R | 5 ++++- R-package/R/xgb.save.raw.R | 8 ++++---- R-package/R/xgb.train.R | 9 ++++++--- 7 files changed, 46 insertions(+), 16 deletions(-) diff --git a/R-package/R/predict.xgb.Booster.R b/R-package/R/predict.xgb.Booster.R index 1e458e708..033bfab84 100644 --- a/R-package/R/predict.xgb.Booster.R +++ b/R-package/R/predict.xgb.Booster.R @@ -1,4 +1,7 @@ -setClass("xgb.Booster") +setClass("xgb.Booster.handle") +setClass("xgb.Booster", + slots = c(handle = "xgb.Booster.handle", + raw = "raw")) #' Predict method for eXtreme Gradient Boosting model #' @@ -30,6 +33,16 @@ setClass("xgb.Booster") setMethod("predict", signature = "xgb.Booster", definition = function(object, newdata, missing = NULL, outputmargin = FALSE, ntreelimit = NULL, predleaf = FALSE) { + if (class(object) != "xgb.Booster"){ + stop("predict: model in prediction must be of class xgb.Booster") + } else { + if (is.null(object$handle)) { + object$handle <- xgb.load(object$raw) + } else { + if (is.null(object$raw)) + object$raw <- xgb.save.raw(object$handle) + } + } if (class(newdata) != "xgb.DMatrix") { if (is.null(missing)) { newdata <- xgb.DMatrix(newdata) @@ -51,7 +64,7 @@ setMethod("predict", signature = "xgb.Booster", if (predleaf) { option <- option + 2 } - ret <- .Call("XGBoosterPredict_R", object, newdata, as.integer(option), + ret <- .Call("XGBoosterPredict_R", object$handle, newdata, as.integer(option), as.integer(ntreelimit), PACKAGE = "xgboost") if (predleaf){ len <- getinfo(newdata, "nrow") diff --git a/R-package/R/utils.R b/R-package/R/utils.R index b0c7f15ac..fb3f59957 100644 --- a/R-package/R/utils.R +++ b/R-package/R/utils.R @@ -65,7 +65,7 @@ xgb.Booster <- function(params = list(), cachelist = list(), modelfile = NULL) { stop("xgb.Booster: modelfile must be character or raw vector") } } - return(structure(handle, class = "xgb.Booster")) + return(structure(handle, class = "xgb.Booster.handle")) } ## ----the following are low level iteratively function, not needed if @@ -102,7 +102,7 @@ xgb.numrow <- function(dmat) { } # iteratively update booster with customized statistics xgb.iter.boost <- function(booster, dtrain, gpair) { - if (class(booster) != "xgb.Booster") { + if (class(booster) != "xgb.Booster.handle") { stop("xgb.iter.update: first argument must be type xgb.Booster") } if (class(dtrain) != "xgb.DMatrix") { @@ -115,7 +115,7 @@ xgb.iter.boost <- function(booster, dtrain, gpair) { # iteratively update booster with dtrain xgb.iter.update <- function(booster, dtrain, iter, obj = NULL) { - if (class(booster) != "xgb.Booster") { + if (class(booster) != "xgb.Booster.handle") { stop("xgb.iter.update: first argument must be type xgb.Booster") } if (class(dtrain) != "xgb.DMatrix") { @@ -135,7 +135,7 @@ xgb.iter.update <- function(booster, dtrain, iter, obj = NULL) { # iteratively evaluate one iteration xgb.iter.eval <- function(booster, watchlist, iter, feval = NULL, prediction = FALSE) { - if (class(booster) != "xgb.Booster") { + if (class(booster) != "xgb.Booster.handle") { stop("xgb.eval: first argument must be type xgb.Booster") } if (typeof(watchlist) != "list") { diff --git a/R-package/R/xgb.dump.R b/R-package/R/xgb.dump.R index edeb03b5f..1f73eed2e 100644 --- a/R-package/R/xgb.dump.R +++ b/R-package/R/xgb.dump.R @@ -40,6 +40,13 @@ xgb.dump <- function(model = NULL, fname = NULL, fmap = "", with.stats=FALSE) { if (class(model) != "xgb.Booster") { stop("model: argument must be type xgb.Booster") + } else { + if (is.null(model$handle)) { + model$handle <- xgb.load(model$raw) + } else { + if (is.null(model$raw)) + model$raw <- xgb.save.raw(model$handle) + } } if (!(class(fname) %in% c("character", "NULL") && length(fname) <= 1)) { stop("fname: argument must be type character (when provided)") @@ -48,7 +55,7 @@ xgb.dump <- function(model = NULL, fname = NULL, fmap = "", with.stats=FALSE) { stop("fmap: argument must be type character (when provided)") } - longString <- .Call("XGBoosterDumpModel_R", model, fmap, as.integer(with.stats), PACKAGE = "xgboost") + longString <- .Call("XGBoosterDumpModel_R", model$handle, fmap, as.integer(with.stats), PACKAGE = "xgboost") dt <- fread(paste(longString, collapse = ""), sep = "\n", header = F) diff --git a/R-package/R/xgb.load.R b/R-package/R/xgb.load.R index af87e2b3c..87247b4a9 100644 --- a/R-package/R/xgb.load.R +++ b/R-package/R/xgb.load.R @@ -19,5 +19,9 @@ xgb.load <- function(modelfile) { if (is.null(modelfile)) stop("xgb.load: modelfile cannot be NULL") - xgb.Booster(modelfile = modelfile) + bst <- list(handle = NULL,raw = NULL) + class(bst) <- 'xgb.Booster' + bst$handle <- xgb.Booster(modelfile = modelfile) + bst$raw <- xgb.save.raw(bst$handle) + return(bst) } diff --git a/R-package/R/xgb.save.R b/R-package/R/xgb.save.R index 2a250a9af..0fecddfb5 100644 --- a/R-package/R/xgb.save.R +++ b/R-package/R/xgb.save.R @@ -22,7 +22,10 @@ xgb.save <- function(model, fname) { stop("xgb.save: fname must be character") } if (class(model) == "xgb.Booster") { - .Call("XGBoosterSaveModel_R", model, fname, PACKAGE = "xgboost") + if (is.null(model$handle)) { + model$handle <- xgb.load(model$raw) + } + .Call("XGBoosterSaveModel_R", model$handle, fname, PACKAGE = "xgboost") return(TRUE) } stop("xgb.save: the input must be xgb.Booster. Use xgb.DMatrix.save to save diff --git a/R-package/R/xgb.save.raw.R b/R-package/R/xgb.save.raw.R index d8ed6f526..91f7075bd 100644 --- a/R-package/R/xgb.save.raw.R +++ b/R-package/R/xgb.save.raw.R @@ -17,11 +17,11 @@ #' pred <- predict(bst, test$data) #' @export #' -xgb.save.raw <- function(model) { - if (class(model) == "xgb.Booster") { - raw <- .Call("XGBoosterModelToRaw_R", model, PACKAGE = "xgboost") +xgb.save.raw <- function(handle) { + if (class(handle) == "xgb.Booster.handle") { + raw <- .Call("XGBoosterModelToRaw_R", handle, PACKAGE = "xgboost") return(raw) } - stop("xgb.raw: the input must be xgb.Booster. Use xgb.DMatrix.save to save + stop("xgb.raw: the input must be xgb.Booster.handle. Use xgb.DMatrix.save to save xgb.DMatrix object.") } diff --git a/R-package/R/xgb.train.R b/R-package/R/xgb.train.R index 06c39d76c..c6d29e6e3 100644 --- a/R-package/R/xgb.train.R +++ b/R-package/R/xgb.train.R @@ -86,13 +86,16 @@ xgb.train <- function(params=list(), data, nrounds, watchlist = list(), } params = append(params, list(...)) - bst <- xgb.Booster(params, append(watchlist, dtrain)) + bst <- list(handle = NULL,raw = NULL) + class(bst) <- 'xgb.Booster' + bst$handle <- xgb.Booster(params, append(watchlist, dtrain)) for (i in 1:nrounds) { - succ <- xgb.iter.update(bst, dtrain, i - 1, obj) + succ <- xgb.iter.update(bst$handle, dtrain, i - 1, obj) if (length(watchlist) != 0) { - msg <- xgb.iter.eval(bst, watchlist, i - 1, feval) + msg <- xgb.iter.eval(bst$handle, watchlist, i - 1, feval) cat(paste(msg, "\n", sep="")) } } + bst$raw <- xgb.save.raw(bst$handle) return(bst) } From f7c838ffaa138aed9c87ca941e8582eef4a20b30 Mon Sep 17 00:00:00 2001 From: hetong007 Date: Mon, 9 Feb 2015 16:16:11 -0800 Subject: [PATCH 82/94] fix bugs --- R-package/R/utils.R | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/R-package/R/utils.R b/R-package/R/utils.R index fb3f59957..bcbde36d1 100644 --- a/R-package/R/utils.R +++ b/R-package/R/utils.R @@ -102,28 +102,42 @@ xgb.numrow <- function(dmat) { } # iteratively update booster with customized statistics xgb.iter.boost <- function(booster, dtrain, gpair) { - if (class(booster) != "xgb.Booster.handle") { + if (class(booster) != "xgb.Booster") { stop("xgb.iter.update: first argument must be type xgb.Booster") + } else { + if (is.null(booster$handle)) { + booster$handle <- xgb.load(booster$raw) + } else { + if (is.null(booster$raw)) + booster$raw <- xgb.save.raw(booster$handle) + } } if (class(dtrain) != "xgb.DMatrix") { stop("xgb.iter.update: second argument must be type xgb.DMatrix") } - .Call("XGBoosterBoostOneIter_R", booster, dtrain, gpair$grad, gpair$hess, + .Call("XGBoosterBoostOneIter_R", booster$handle, dtrain, gpair$grad, gpair$hess, PACKAGE = "xgboost") return(TRUE) } # iteratively update booster with dtrain xgb.iter.update <- function(booster, dtrain, iter, obj = NULL) { - if (class(booster) != "xgb.Booster.handle") { + if (class(booster) != "xgb.Booster") { stop("xgb.iter.update: first argument must be type xgb.Booster") + } else { + if (is.null(booster$handle)) { + booster$handle <- xgb.load(booster$raw) + } else { + if (is.null(booster$raw)) + booster$raw <- xgb.save.raw(booster$handle) + } } if (class(dtrain) != "xgb.DMatrix") { stop("xgb.iter.update: second argument must be type xgb.DMatrix") } if (is.null(obj)) { - .Call("XGBoosterUpdateOneIter_R", booster, as.integer(iter), dtrain, + .Call("XGBoosterUpdateOneIter_R", booster$handle, as.integer(iter), dtrain, PACKAGE = "xgboost") } else { pred <- predict(booster, dtrain) From 0aef62dabceba9b2b6442d86dd767dfc0b798442 Mon Sep 17 00:00:00 2001 From: hetong007 Date: Mon, 9 Feb 2015 16:25:00 -0800 Subject: [PATCH 83/94] fix with new predict --- R-package/R/predict.xgb.Booster.R | 2 +- R-package/R/predict.xgb.Booster.handle.R | 16 +++++++++++++++ R-package/R/utils.R | 26 ++++++------------------ R-package/R/xgb.load.R | 1 + 4 files changed, 24 insertions(+), 21 deletions(-) create mode 100644 R-package/R/predict.xgb.Booster.handle.R diff --git a/R-package/R/predict.xgb.Booster.R b/R-package/R/predict.xgb.Booster.R index 033bfab84..b1c3c10ca 100644 --- a/R-package/R/predict.xgb.Booster.R +++ b/R-package/R/predict.xgb.Booster.R @@ -77,4 +77,4 @@ setMethod("predict", signature = "xgb.Booster", } return(ret) }) - + diff --git a/R-package/R/predict.xgb.Booster.handle.R b/R-package/R/predict.xgb.Booster.handle.R new file mode 100644 index 000000000..05cbf891e --- /dev/null +++ b/R-package/R/predict.xgb.Booster.handle.R @@ -0,0 +1,16 @@ +setClass("xgb.Booster.handle") + +setMethod("predict", signature = "xgb.Booster.handle", + definition = function(object, ...) { + if (class(object) != "xgb.Booster.handle"){ + stop("predict: model in prediction must be of class xgb.Booster.handle") + } + + bst <- list(handle = object,raw = NULL) + class(bst) <- 'xgb.Booster' + bst$raw <- xgb.save.raw(bst$handle) + + ret = predict(bst, ...) + return(ret) +}) + diff --git a/R-package/R/utils.R b/R-package/R/utils.R index bcbde36d1..5093382d4 100644 --- a/R-package/R/utils.R +++ b/R-package/R/utils.R @@ -102,42 +102,28 @@ xgb.numrow <- function(dmat) { } # iteratively update booster with customized statistics xgb.iter.boost <- function(booster, dtrain, gpair) { - if (class(booster) != "xgb.Booster") { - stop("xgb.iter.update: first argument must be type xgb.Booster") - } else { - if (is.null(booster$handle)) { - booster$handle <- xgb.load(booster$raw) - } else { - if (is.null(booster$raw)) - booster$raw <- xgb.save.raw(booster$handle) - } + if (class(booster) != "xgb.Booster.handle") { + stop("xgb.iter.update: first argument must be type xgb.Booster.handle") } if (class(dtrain) != "xgb.DMatrix") { stop("xgb.iter.update: second argument must be type xgb.DMatrix") } - .Call("XGBoosterBoostOneIter_R", booster$handle, dtrain, gpair$grad, gpair$hess, + .Call("XGBoosterBoostOneIter_R", booster, dtrain, gpair$grad, gpair$hess, PACKAGE = "xgboost") return(TRUE) } # iteratively update booster with dtrain xgb.iter.update <- function(booster, dtrain, iter, obj = NULL) { - if (class(booster) != "xgb.Booster") { - stop("xgb.iter.update: first argument must be type xgb.Booster") - } else { - if (is.null(booster$handle)) { - booster$handle <- xgb.load(booster$raw) - } else { - if (is.null(booster$raw)) - booster$raw <- xgb.save.raw(booster$handle) - } + if (class(booster) != "xgb.Booster.handle") { + stop("xgb.iter.update: first argument must be type xgb.Booster.handle") } if (class(dtrain) != "xgb.DMatrix") { stop("xgb.iter.update: second argument must be type xgb.DMatrix") } if (is.null(obj)) { - .Call("XGBoosterUpdateOneIter_R", booster$handle, as.integer(iter), dtrain, + .Call("XGBoosterUpdateOneIter_R", booster, as.integer(iter), dtrain, PACKAGE = "xgboost") } else { pred <- predict(booster, dtrain) diff --git a/R-package/R/xgb.load.R b/R-package/R/xgb.load.R index 87247b4a9..264176952 100644 --- a/R-package/R/xgb.load.R +++ b/R-package/R/xgb.load.R @@ -19,6 +19,7 @@ xgb.load <- function(modelfile) { if (is.null(modelfile)) stop("xgb.load: modelfile cannot be NULL") + bst <- list(handle = NULL,raw = NULL) class(bst) <- 'xgb.Booster' bst$handle <- xgb.Booster(modelfile = modelfile) From 4c25600d2a8beb09e3a9b5f9f5e54c1e3df941c4 Mon Sep 17 00:00:00 2001 From: hetong007 Date: Mon, 9 Feb 2015 17:28:48 -0800 Subject: [PATCH 84/94] fix segfault and add two function for handle and booster --- R-package/R/predict.xgb.Booster.R | 7 +------ R-package/R/predict.xgb.Booster.handle.R | 6 +++--- R-package/R/utils.R | 20 ++++++++++++++++++++ R-package/R/xgb.dump.R | 7 +------ R-package/R/xgb.load.R | 7 +++---- R-package/R/xgb.save.R | 4 +--- R-package/R/xgb.train.R | 7 +++---- 7 files changed, 32 insertions(+), 26 deletions(-) diff --git a/R-package/R/predict.xgb.Booster.R b/R-package/R/predict.xgb.Booster.R index b1c3c10ca..52c40df9b 100644 --- a/R-package/R/predict.xgb.Booster.R +++ b/R-package/R/predict.xgb.Booster.R @@ -36,12 +36,7 @@ setMethod("predict", signature = "xgb.Booster", if (class(object) != "xgb.Booster"){ stop("predict: model in prediction must be of class xgb.Booster") } else { - if (is.null(object$handle)) { - object$handle <- xgb.load(object$raw) - } else { - if (is.null(object$raw)) - object$raw <- xgb.save.raw(object$handle) - } + object <- xgb.Booster.check(object, saveraw = FALSE) } if (class(newdata) != "xgb.DMatrix") { if (is.null(missing)) { diff --git a/R-package/R/predict.xgb.Booster.handle.R b/R-package/R/predict.xgb.Booster.handle.R index 05cbf891e..a38aeb64e 100644 --- a/R-package/R/predict.xgb.Booster.handle.R +++ b/R-package/R/predict.xgb.Booster.handle.R @@ -6,9 +6,9 @@ setMethod("predict", signature = "xgb.Booster.handle", stop("predict: model in prediction must be of class xgb.Booster.handle") } - bst <- list(handle = object,raw = NULL) - class(bst) <- 'xgb.Booster' - bst$raw <- xgb.save.raw(bst$handle) + bst <- xgb.handleToBooster(object) + # Avoid save a handle without update + # bst$raw <- xgb.save.raw(object) ret = predict(bst, ...) return(ret) diff --git a/R-package/R/utils.R b/R-package/R/utils.R index 5093382d4..bff6dd0e8 100644 --- a/R-package/R/utils.R +++ b/R-package/R/utils.R @@ -68,6 +68,26 @@ xgb.Booster <- function(params = list(), cachelist = list(), modelfile = NULL) { return(structure(handle, class = "xgb.Booster.handle")) } +# convert xgb.Booster.handle to xgb.Booster +xgb.handleToBooster <- function(handle) +{ + bst <- list(handle = handle, raw = NULL) + class(bst) <- "xgb.Booster" + return(bst) +} + +# Check whether an xgb.Booster object is complete +xgb.Booster.check <- function(bst, saveraw = TRUE) +{ + if (is.null(bst$handle)) { + bst$handle <- xgb.load(bst$raw) + } else { + if (is.null(bst$raw) && saveraw) + bst$raw <- xgb.save.raw(bst$handle) + } + return(bst) +} + ## ----the following are low level iteratively function, not needed if ## you do not want to use them --------------------------------------- # get dmatrix from data, label diff --git a/R-package/R/xgb.dump.R b/R-package/R/xgb.dump.R index 1f73eed2e..fa5fe4149 100644 --- a/R-package/R/xgb.dump.R +++ b/R-package/R/xgb.dump.R @@ -41,12 +41,7 @@ xgb.dump <- function(model = NULL, fname = NULL, fmap = "", with.stats=FALSE) { if (class(model) != "xgb.Booster") { stop("model: argument must be type xgb.Booster") } else { - if (is.null(model$handle)) { - model$handle <- xgb.load(model$raw) - } else { - if (is.null(model$raw)) - model$raw <- xgb.save.raw(model$handle) - } + model <- xgb.Booster.check(model) } if (!(class(fname) %in% c("character", "NULL") && length(fname) <= 1)) { stop("fname: argument must be type character (when provided)") diff --git a/R-package/R/xgb.load.R b/R-package/R/xgb.load.R index 264176952..33d440530 100644 --- a/R-package/R/xgb.load.R +++ b/R-package/R/xgb.load.R @@ -20,9 +20,8 @@ xgb.load <- function(modelfile) { if (is.null(modelfile)) stop("xgb.load: modelfile cannot be NULL") - bst <- list(handle = NULL,raw = NULL) - class(bst) <- 'xgb.Booster' - bst$handle <- xgb.Booster(modelfile = modelfile) - bst$raw <- xgb.save.raw(bst$handle) + handle <- xgb.Booster(modelfile = modelfile) + bst <- xgb.handleToBooster(handle) + bst <- xgb.Booster.check(bst) return(bst) } diff --git a/R-package/R/xgb.save.R b/R-package/R/xgb.save.R index 0fecddfb5..59c5d2ecd 100644 --- a/R-package/R/xgb.save.R +++ b/R-package/R/xgb.save.R @@ -22,9 +22,7 @@ xgb.save <- function(model, fname) { stop("xgb.save: fname must be character") } if (class(model) == "xgb.Booster") { - if (is.null(model$handle)) { - model$handle <- xgb.load(model$raw) - } + model <- xgb.Booster.check(model) .Call("XGBoosterSaveModel_R", model$handle, fname, PACKAGE = "xgboost") return(TRUE) } diff --git a/R-package/R/xgb.train.R b/R-package/R/xgb.train.R index c6d29e6e3..250ba2fbf 100644 --- a/R-package/R/xgb.train.R +++ b/R-package/R/xgb.train.R @@ -86,9 +86,8 @@ xgb.train <- function(params=list(), data, nrounds, watchlist = list(), } params = append(params, list(...)) - bst <- list(handle = NULL,raw = NULL) - class(bst) <- 'xgb.Booster' - bst$handle <- xgb.Booster(params, append(watchlist, dtrain)) + handle <- xgb.Booster(params, append(watchlist, dtrain)) + bst <- xgb.handleToBooster(handle) for (i in 1:nrounds) { succ <- xgb.iter.update(bst$handle, dtrain, i - 1, obj) if (length(watchlist) != 0) { @@ -96,6 +95,6 @@ xgb.train <- function(params=list(), data, nrounds, watchlist = list(), cat(paste(msg, "\n", sep="")) } } - bst$raw <- xgb.save.raw(bst$handle) + bst <- xgb.Booster.check(bst) return(bst) } From 47b5cf5148e5eb933e5a798a8a3dc20b87378844 Mon Sep 17 00:00:00 2001 From: hetong007 Date: Mon, 9 Feb 2015 17:35:50 -0800 Subject: [PATCH 85/94] fix save.raw --- R-package/R/xgb.save.raw.R | 3 +++ 1 file changed, 3 insertions(+) diff --git a/R-package/R/xgb.save.raw.R b/R-package/R/xgb.save.raw.R index 91f7075bd..ee217aa85 100644 --- a/R-package/R/xgb.save.raw.R +++ b/R-package/R/xgb.save.raw.R @@ -18,6 +18,9 @@ #' @export #' xgb.save.raw <- function(handle) { + if (class(handle) == "xgb.Booster"){ + handle <- handle$handle + } if (class(handle) == "xgb.Booster.handle") { raw <- .Call("XGBoosterModelToRaw_R", handle, PACKAGE = "xgboost") return(raw) From 25f508e43e4c2393fbdda38196886bd766df7d2f Mon Sep 17 00:00:00 2001 From: hetong007 Date: Mon, 9 Feb 2015 17:48:52 -0800 Subject: [PATCH 86/94] update doc, resolve warnings --- R-package/R/predict.xgb.Booster.R | 1 - R-package/R/predict.xgb.Booster.handle.R | 9 +++++++-- R-package/R/xgb.save.raw.R | 10 +++++----- .../man/predict-xgb.Booster.handle-method.Rd | 18 ++++++++++++++++++ 4 files changed, 30 insertions(+), 8 deletions(-) create mode 100644 R-package/man/predict-xgb.Booster.handle-method.Rd diff --git a/R-package/R/predict.xgb.Booster.R b/R-package/R/predict.xgb.Booster.R index 52c40df9b..c5e1046eb 100644 --- a/R-package/R/predict.xgb.Booster.R +++ b/R-package/R/predict.xgb.Booster.R @@ -1,4 +1,3 @@ -setClass("xgb.Booster.handle") setClass("xgb.Booster", slots = c(handle = "xgb.Booster.handle", raw = "raw")) diff --git a/R-package/R/predict.xgb.Booster.handle.R b/R-package/R/predict.xgb.Booster.handle.R index a38aeb64e..3a09e02de 100644 --- a/R-package/R/predict.xgb.Booster.handle.R +++ b/R-package/R/predict.xgb.Booster.handle.R @@ -1,5 +1,12 @@ setClass("xgb.Booster.handle") +#' Predict method for eXtreme Gradient Boosting model handle +#' +#' Predicted values based on xgb.Booster.handle object. +#' +#' @param object Object of class "xgb.Boost.handle" +#' @param ... Parameters pass to \code{predict.xgb.Booster} +#' setMethod("predict", signature = "xgb.Booster.handle", definition = function(object, ...) { if (class(object) != "xgb.Booster.handle"){ @@ -7,8 +14,6 @@ setMethod("predict", signature = "xgb.Booster.handle", } bst <- xgb.handleToBooster(object) - # Avoid save a handle without update - # bst$raw <- xgb.save.raw(object) ret = predict(bst, ...) return(ret) diff --git a/R-package/R/xgb.save.raw.R b/R-package/R/xgb.save.raw.R index ee217aa85..7f3a2df21 100644 --- a/R-package/R/xgb.save.raw.R +++ b/R-package/R/xgb.save.raw.R @@ -17,12 +17,12 @@ #' pred <- predict(bst, test$data) #' @export #' -xgb.save.raw <- function(handle) { - if (class(handle) == "xgb.Booster"){ - handle <- handle$handle +xgb.save.raw <- function(model) { + if (class(model) == "xgb.Booster"){ + model <- model$handle } - if (class(handle) == "xgb.Booster.handle") { - raw <- .Call("XGBoosterModelToRaw_R", handle, PACKAGE = "xgboost") + if (class(model) == "xgb.Booster.handle") { + raw <- .Call("XGBoosterModelToRaw_R", model, PACKAGE = "xgboost") return(raw) } stop("xgb.raw: the input must be xgb.Booster.handle. Use xgb.DMatrix.save to save diff --git a/R-package/man/predict-xgb.Booster.handle-method.Rd b/R-package/man/predict-xgb.Booster.handle-method.Rd new file mode 100644 index 000000000..cc9ba29f9 --- /dev/null +++ b/R-package/man/predict-xgb.Booster.handle-method.Rd @@ -0,0 +1,18 @@ +% Generated by roxygen2 (4.1.0): do not edit by hand +% Please edit documentation in R/predict.xgb.Booster.handle.R +\docType{methods} +\name{predict,xgb.Booster.handle-method} +\alias{predict,xgb.Booster.handle-method} +\title{Predict method for eXtreme Gradient Boosting model handle} +\usage{ +\S4method{predict}{xgb.Booster.handle}(object, ...) +} +\arguments{ +\item{object}{Object of class "xgb.Boost.handle"} + +\item{...}{Parameters pass to \code{predict.xgb.Booster}} +} +\description{ +Predicted values based on xgb.Booster.handle object. +} + From 7f3dc7cf7e779b70c788ade2e847c01fef3e12a1 Mon Sep 17 00:00:00 2001 From: hetong007 Date: Mon, 9 Feb 2015 18:38:23 -0800 Subject: [PATCH 87/94] fix warnings --- R-package/R/predict.xgb.Booster.R | 1 + R-package/R/predict.xgb.Booster.handle.R | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/R-package/R/predict.xgb.Booster.R b/R-package/R/predict.xgb.Booster.R index c5e1046eb..52c40df9b 100644 --- a/R-package/R/predict.xgb.Booster.R +++ b/R-package/R/predict.xgb.Booster.R @@ -1,3 +1,4 @@ +setClass("xgb.Booster.handle") setClass("xgb.Booster", slots = c(handle = "xgb.Booster.handle", raw = "raw")) diff --git a/R-package/R/predict.xgb.Booster.handle.R b/R-package/R/predict.xgb.Booster.handle.R index 3a09e02de..685318f12 100644 --- a/R-package/R/predict.xgb.Booster.handle.R +++ b/R-package/R/predict.xgb.Booster.handle.R @@ -1,5 +1,3 @@ -setClass("xgb.Booster.handle") - #' Predict method for eXtreme Gradient Boosting model handle #' #' Predicted values based on xgb.Booster.handle object. From e889da4cc1ceb34b69140ec4cb36f41c5959c399 Mon Sep 17 00:00:00 2001 From: tqchen Date: Mon, 9 Feb 2015 21:07:57 -0800 Subject: [PATCH 88/94] new Rpack --- Makefile | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index a4bbe876f..24f4063a1 100644 --- a/Makefile +++ b/Makefile @@ -75,15 +75,17 @@ Rpack: cd subtree/rabit;make clean;cd .. rm -rf xgboost xgboost*.tar.gz cp -r R-package xgboost - rm -rf xgboost/inst/examples/*.buffer - rm -rf xgboost/inst/examples/*.model - rm -rf xgboost/inst/examples/dump* rm -rf xgboost/src/*.o xgboost/src/*.so xgboost/src/*.dll + rm -rf xgboost/src/*/*.o rm -rf subtree/rabit/src/*.o rm -rf xgboost/demo/*.model xgboost/demo/*.buffer xgboost/demo/*.txt rm -rf xgboost/demo/runall.R cp -r src xgboost/src/src - cp -r subtree xgboost/src/subtree + mkdir xgboost/src/subtree + mkdir xgboost/src/subtree/rabit + cp -r subtree/rabit/include xgboost/src/subtree/rabit/include + cp -r subtree/rabit/src xgboost/src/subtree/rabit/src + rm -rf xgboost/src/subtree/rabit/src/*.o mkdir xgboost/src/wrapper cp wrapper/xgboost_wrapper.h xgboost/src/wrapper cp wrapper/xgboost_wrapper.cpp xgboost/src/wrapper @@ -95,5 +97,5 @@ Rpack: R CMD check --as-cran xgboost*.tar.gz clean: - $(RM) $(OBJ) $(BIN) $(MPIBIN) $(MPIOBJ) $(SLIB) *.o */*.o */*/*.o *~ */*~ */*/*~ + $(RM) -rf $(OBJ) $(BIN) $(MPIBIN) $(MPIOBJ) $(SLIB) *.o */*.o */*/*.o *~ */*~ */*/*~ cd subtree/rabit; make clean; cd .. From 423c3e6a8d8ed8a53595e33e84a792aad726f7a4 Mon Sep 17 00:00:00 2001 From: El Potaeto Date: Tue, 10 Feb 2015 13:54:30 +0100 Subject: [PATCH 89/94] improved vignette text --- R-package/vignettes/discoverYourData.Rmd | 43 +++++++++++++++++------- R-package/vignettes/vignette.css | 14 ++++---- 2 files changed, 38 insertions(+), 19 deletions(-) diff --git a/R-package/vignettes/discoverYourData.Rmd b/R-package/vignettes/discoverYourData.Rmd index 899efba11..210930dd6 100644 --- a/R-package/vignettes/discoverYourData.Rmd +++ b/R-package/vignettes/discoverYourData.Rmd @@ -36,8 +36,8 @@ Sometimes the dataset we have to work on have *categorical* data. A *categorical* variable is one which have a fixed number of different values. By exemple, if for each observation a variable called *Colour* can have only *red*, *blue* or *green* as value, it is a *categorical* variable. -In **R**, *categorical* variable is called `factor`. -Type `?factor` in console for more information. +> In **R**, *categorical* variable is called `factor`. +> Type `?factor` in console for more information. In this demo we will see how to transform a dense dataframe with *categorical* variables to a sparse matrix before analyzing it in **Xgboost**. @@ -62,18 +62,21 @@ Now we will check the format of each column. str(df) ``` -2 columns have `factor` type, one has `ordinal` type (`ordinal` variable is a categorical variable with values wich can be ordered, here: `None` > `Some` > `Marked`). +> 2 columns have `factor` type, one has `ordinal` type. +> `ordinal` variable is a categorical variable with values wich can be ordered +> Here: `None` > `Some` > `Marked`. Let's add some new categorical features to see if it helps. Of course these feature are highly correlated to the Age feature. Usually it's not a good thing in ML, but tree algorithms (including boosted trees) are able to select the best features, even in case of highly correlated features. -For the first feature we create groups of age by rounding the real age. Note that we transform it to `factor` so the algorithm treat them as independant values. - ```{r} df[,AgeDiscret:= as.factor(round(Age/10,0))][1:10] ``` +> For the first feature we create groups of age by rounding the real age. +> Note that we transform it to `factor` so the algorithm treat them as independant values. + Following is an even stronger simplification of the real age with an arbitrary split at 30 years old. I choose this value **based on nothing**. We will see later if simplifying the information based on arbitrary values is a good strategy (I am sure you already have an idea of how well it will work!). ```{r} @@ -99,7 +102,7 @@ The purpose is to transform each value of each *categorical* feature in a binary For example, the column Treatment will be replaced by two columns, Placebo, and Treated. Each of them will be *binary*. For example an observation which had the value Placebo in column Treatment before the transformation will have, after the transformation, the value 1 in the new column Placebo and the value 0 in the new column Treated. -Formulae `Improved~.-1` used below means transform all *categorical* features but column Improved to binary values. +> Formulae `Improved~.-1` used below means transform all *categorical* features but column Improved to binary values. Column Improved is excluded because it will be our output column, the one we want to predict. @@ -133,8 +136,9 @@ You can see plenty of `train-error: 0.XXXXX` lines followed by a number. It decr A model which fits too well may [overfit](http://en.wikipedia.org/wiki/Overfitting) (meaning it copy paste too much the past, and is not that good to predict the future). -Here you can see the numbers decrease until line 7 and then increase. It probably means I am overfitting. To fix that I may reduce the number of rounds to `nround = 4`. I will let things like that because I don't really care for the purpose of this example :-) - +> Here you can see the numbers decrease until line 7 and then increase. +> It probably means I am overfitting. To fix that I may reduce the number of rounds to `nround = 4`. +> I will let things like that because I don't really care for the purpose of this example :-) Feature importance ================== @@ -149,7 +153,8 @@ importance <- xgb.importance(sparse_matrix@Dimnames[[2]], model = bst) print(importance) ``` -The column `Gain` provide the information we are looking for. +> The column `Gain` provide the information we are looking for. +> As you can see, features are classified by `Gain`. `Gain` is the improvement in accuracy brought by a feature to the branches it is on. The idea is that before adding a new split on a feature X to the branch there was some wrongly classified elements, after adding the split on this feature, there are two new branches, and each of these branch is more accurate (one branch saying if your observation is on this branch then it should be classified as 1, and the other branch saying the exact opposite, both new branch being more accurate than the one before the insertion of the feature). @@ -157,8 +162,6 @@ The column `Gain` provide the information we are looking for. `Frequence` is a simpler way to measure the `Gain`. It just counts the number of times a feature is used in all generated trees. You should not use it (unless you know why you want to use it). -As you can see, features are classified by `Gain`. - Plotting the feature importance ------------------------------- @@ -170,6 +173,9 @@ xgb.plot.importance(importance_matrix = importance) Feature have been automatically divided in 2 clusters: the interesting features... and the others. +> Depending of the case you may have more than two clusters. +> Default value is to limit them to 10, but you can increase this limit. Look at the function documentation for more information. + According to the plot above, the most important feature in this dataset to predict if the treatment will work is : * the Age; @@ -177,8 +183,6 @@ According to the plot above, the most important feature in this dataset to predi * the sex is third but already included in the not interesting feature ; * then we see our generated features (AgeDiscret). We can see that their contribution is very low. -*Note: Depending of the case you may have more than two clusters. Default value is to limit them to 10, but you can increase this limit. Look at the function documentation for more information.* - Does these results make sense? ------------------------------ @@ -224,4 +228,17 @@ Linear model may not be that strong in these scenario. #xgb.plot.tree(sparse_matrix@Dimnames[[2]], model = bst, n_first_tree = 1, width = 1200, height = 800) ``` +Special Note: What about Random forest? +======================================= +As you may know, [Random Forest](http://en.wikipedia.org/wiki/Random_forest) algorithm is cousin with boosting and both are part of the [ensemble leanrning](http://en.wikipedia.org/wiki/Ensemble_learning) family. + +Both trains several decision trees for one dataset. The *main* difference is that in Random Forest, trees are independant and in boosting tree N+1 focus its learning on what has no been well modeled by tree N (and so on...). + +This difference have an impact on a corner case in feature importance analysis: the *correlated features*. + +Imagine two features perfectly correlated, feature `A` and feature `B`. For one specific tree, if the algorithm needs one of them, it will choose randomly (true in both boosting and random forest). + +However, in Random Forest this choice will be done plenty of times, because trees are independant. So the **importance** of a specific feature is diluted among features `A` and `B`. So you won't easily know they are important to predict what you want to predict. + +In boosting, when as aspect of your dataset have been learned by the algorithm, there is no more need to refocus on it. Therefore, all the importace will be on `A` or `B`. You will know that one of them is important, it is up to you to search for correlated features. diff --git a/R-package/vignettes/vignette.css b/R-package/vignettes/vignette.css index c09bf1813..678accf77 100644 --- a/R-package/vignettes/vignette.css +++ b/R-package/vignettes/vignette.css @@ -130,13 +130,16 @@ aside { width: 390px; } blockquote { - border-left:.5em solid #eee; - padding: 0 1em; - margin-left:0; - max-width: 476px; + font-size:14px; + border-left:.5em solid #606AAA; + background: #f5f5f5; + color:#bfbfbf; + padding: 5px; + margin-left:25px; + max-width: 500px; } blockquote cite { - / font-size:14px; + font-size:14px; line-height:20px; color:#bfbfbf; } @@ -146,7 +149,6 @@ blockquote cite:before { blockquote p { color: #666; - max-width: 460px; } hr { / width: 540px; From c0d8ae3781bf02c61662d09e5a5c564ae947adf4 Mon Sep 17 00:00:00 2001 From: El Potaeto Date: Tue, 10 Feb 2015 13:59:13 +0100 Subject: [PATCH 90/94] text change --- R-package/vignettes/discoverYourData.Rmd | 1 - R-package/vignettes/xgboostPresentation.Rmd | 113 ++++++++++++++++++++ 2 files changed, 113 insertions(+), 1 deletion(-) create mode 100644 R-package/vignettes/xgboostPresentation.Rmd diff --git a/R-package/vignettes/discoverYourData.Rmd b/R-package/vignettes/discoverYourData.Rmd index 210930dd6..e6a99bd56 100644 --- a/R-package/vignettes/discoverYourData.Rmd +++ b/R-package/vignettes/discoverYourData.Rmd @@ -5,7 +5,6 @@ output: css: vignette.css number_sections: yes toc: yes -date: "Wednesday, January 28, 2015" --- Introduction diff --git a/R-package/vignettes/xgboostPresentation.Rmd b/R-package/vignettes/xgboostPresentation.Rmd new file mode 100644 index 000000000..9dcd12f39 --- /dev/null +++ b/R-package/vignettes/xgboostPresentation.Rmd @@ -0,0 +1,113 @@ +--- +title: "Xgboost presentation" +output: + html_document: + css: vignette.css + number_sections: yes + toc: yes +--- + +require(xgboost) +require(methods) +# we load in the agaricus dataset +# In this example, we are aiming to predict whether a mushroom can be eated +data(agaricus.train, package='xgboost') +data(agaricus.test, package='xgboost') +train <- agaricus.train +test <- agaricus.test +# the loaded data is stored in sparseMatrix, and label is a numeric vector in {0,1} +class(train$label) +class(train$data) + +#-------------Basic Training using XGBoost----------------- +# this is the basic usage of xgboost you can put matrix in data field +# note: we are puting in sparse matrix here, xgboost naturally handles sparse input +# use sparse matrix when your feature is sparse(e.g. when you using one-hot encoding vector) +print("training xgboost with sparseMatrix") +bst <- xgboost(data = train$data, label = train$label, max.depth = 2, eta = 1, nround = 2, + objective = "binary:logistic") +# alternatively, you can put in dense matrix, i.e. basic R-matrix +print("training xgboost with Matrix") +bst <- xgboost(data = as.matrix(train$data), label = train$label, max.depth = 2, eta = 1, nround = 2, + objective = "binary:logistic") + +# you can also put in xgb.DMatrix object, stores label, data and other meta datas needed for advanced features +print("training xgboost with xgb.DMatrix") +dtrain <- xgb.DMatrix(data = train$data, label = train$label) +bst <- xgboost(data = dtrain, max.depth = 2, eta = 1, nround = 2, objective = "binary:logistic") + +# Verbose = 0,1,2 +print ('train xgboost with verbose 0, no message') +bst <- xgboost(data = dtrain, max.depth = 2, eta = 1, nround = 2, + objective = "binary:logistic", verbose = 0) +print ('train xgboost with verbose 1, print evaluation metric') +bst <- xgboost(data = dtrain, max.depth = 2, eta = 1, nround = 2, + objective = "binary:logistic", verbose = 1) +print ('train xgboost with verbose 2, also print information about tree') +bst <- xgboost(data = dtrain, max.depth = 2, eta = 1, nround = 2, + objective = "binary:logistic", verbose = 2) + +# you can also specify data as file path to a LibSVM format input +# since we do not have this file with us, the following line is just for illustration +# bst <- xgboost(data = 'agaricus.train.svm', max.depth = 2, eta = 1, nround = 2,objective = "binary:logistic") + +#--------------------basic prediction using xgboost-------------- +# you can do prediction using the following line +# you can put in Matrix, sparseMatrix, or xgb.DMatrix +pred <- predict(bst, test$data) +err <- mean(as.numeric(pred > 0.5) != test$label) +print(paste("test-error=", err)) + +#-------------------save and load models------------------------- +# save model to binary local file +xgb.save(bst, "xgboost.model") +# load binary model to R +bst2 <- xgb.load("xgboost.model") +pred2 <- predict(bst2, test$data) +# pred2 should be identical to pred +print(paste("sum(abs(pred2-pred))=", sum(abs(pred2-pred)))) + +# save model to R's raw vector +raw = xgb.save.raw(bst) +# load binary model to R +bst3 <- xgb.load(raw) +pred3 <- predict(bst3, test$data) +# pred2 should be identical to pred +print(paste("sum(abs(pred3-pred))=", sum(abs(pred2-pred)))) + +#----------------Advanced features -------------- +# to use advanced features, we need to put data in xgb.DMatrix +dtrain <- xgb.DMatrix(data = train$data, label=train$label) +dtest <- xgb.DMatrix(data = test$data, label=test$label) +#---------------Using watchlist---------------- +# watchlist is a list of xgb.DMatrix, each of them tagged with name +watchlist <- list(train=dtrain, test=dtest) +# to train with watchlist, use xgb.train, which contains more advanced features +# watchlist allows us to monitor the evaluation result on all data in the list +print ('train xgboost using xgb.train with watchlist') +bst <- xgb.train(data=dtrain, max.depth=2, eta=1, nround=2, watchlist=watchlist, + objective = "binary:logistic") +# we can change evaluation metrics, or use multiple evaluation metrics +print ('train xgboost using xgb.train with watchlist, watch logloss and error') +bst <- xgb.train(data=dtrain, max.depth=2, eta=1, nround=2, watchlist=watchlist, + eval.metric = "error", eval.metric = "logloss", + objective = "binary:logistic") + +# xgb.DMatrix can also be saved using xgb.DMatrix.save +xgb.DMatrix.save(dtrain, "dtrain.buffer") +# to load it in, simply call xgb.DMatrix +dtrain2 <- xgb.DMatrix("dtrain.buffer") +bst <- xgb.train(data=dtrain2, max.depth=2, eta=1, nround=2, watchlist=watchlist, + objective = "binary:logistic") +# information can be extracted from xgb.DMatrix using getinfo +label = getinfo(dtest, "label") +pred <- predict(bst, dtest) +err <- as.numeric(sum(as.integer(pred > 0.5) != label))/length(label) +print(paste("test-error=", err)) + +# You can dump the tree you learned using xgb.dump into a text file +xgb.dump(bst, "dump.raw.txt", with.stats = T) + +# Finally, you can check which features are the most important. +print("Most important features (look at column Gain):") +print(xgb.importance(feature_names = train$data@Dimnames[[2]], filename_dump = "dump.raw.txt")) From cefd55ef00f17f301e05ae3af713f2ed4ff650ce Mon Sep 17 00:00:00 2001 From: El Potaeto Date: Tue, 10 Feb 2015 17:09:21 +0100 Subject: [PATCH 91/94] Vignettes improvement --- R-package/vignettes/discoverYourData.Rmd | 4 +- R-package/vignettes/xgboostPresentation.Rmd | 84 ++++++++++++++++----- 2 files changed, 69 insertions(+), 19 deletions(-) diff --git a/R-package/vignettes/discoverYourData.Rmd b/R-package/vignettes/discoverYourData.Rmd index e6a99bd56..d8f0e62e6 100644 --- a/R-package/vignettes/discoverYourData.Rmd +++ b/R-package/vignettes/discoverYourData.Rmd @@ -12,7 +12,7 @@ Introduction The purpose of this Vignette is to show you how to use **Xgboost** to discover and better understand your own dataset. -You may know **Xgboost** as a state of the art tool to build some kind of Machine learning models. It has been used to win several [Kaggle](http://www.kaggle.com/) competition ([more information](https://github.com/tqchen/xgboost)). +You may know **Xgboost** as a state of the art tool to build some kind of Machine learning models. It has been [used](https://github.com/tqchen/xgboost) to win several [Kaggle](http://www.kaggle.com) competition. During these competition, the purpose is to make prediction. This Vignette is not about showing you how to predict anything. The purpose of this document is to explain *how to use **Xgboost** to understand the link between the features of your data and an outcome*. @@ -24,7 +24,7 @@ require(Matrix) require(data.table) if (!require(vcd)) install.packages('vcd') ``` -*Note that **VCD** is used for one of its embedded dataset only (and not for its own functions).* +> **VCD** is used for one of its embedded dataset only (and not for its own functions). Preparation of the dataset ========================== diff --git a/R-package/vignettes/xgboostPresentation.Rmd b/R-package/vignettes/xgboostPresentation.Rmd index 9dcd12f39..77a250a2c 100644 --- a/R-package/vignettes/xgboostPresentation.Rmd +++ b/R-package/vignettes/xgboostPresentation.Rmd @@ -7,34 +7,84 @@ output: toc: yes --- +Introduction +============ + +The purpose of this Vignette is to show you how to use **Xgboost** to make prediction from a model based on your own dataset. + +You may know **Xgboost** as a state of the art tool to build some kind of Machine learning models. It has been [used](https://github.com/tqchen/xgboost) to win several [Kaggle](http://www.kaggle.com) competition. + +For the purpose of this tutorial we will first load the required packages. + +```{r libLoading, results='hold', message=F, warning=F} require(xgboost) require(methods) -# we load in the agaricus dataset -# In this example, we are aiming to predict whether a mushroom can be eated +``` + +In this example, we are aiming to predict whether a mushroom can be eated. + +Learning +======== + +Dataset loading +--------------- + +We load the `agaricus` datasets and link it to variables. + +The dataset is already separated in `train` and `test` data. + +As their names imply, the train part will be used to build the model and the test part to check how well our model works. Without separation we would test the model on data the algorithm have already seen, as you may imagine, it's not the best methodology to check the performance of a prediction (would it even be a prediction?). + +```{r datasetLoading, results='hold', message=F, warning=F} data(agaricus.train, package='xgboost') data(agaricus.test, package='xgboost') train <- agaricus.train test <- agaricus.test -# the loaded data is stored in sparseMatrix, and label is a numeric vector in {0,1} +``` + +> In the reality, it would be up to you to make this division between `train` and `test` data. + +> Each variable is a S3 object containing both label and data. + +The loaded data is stored in `dgCMatrix` which is a **sparse matrix** type. + +Label is a `numeric` vector in `{0,1}`. + +```{r dataClass, message=F, warning=F} +class(train$data)[1] class(train$label) -class(train$data) +``` -#-------------Basic Training using XGBoost----------------- -# this is the basic usage of xgboost you can put matrix in data field -# note: we are puting in sparse matrix here, xgboost naturally handles sparse input -# use sparse matrix when your feature is sparse(e.g. when you using one-hot encoding vector) -print("training xgboost with sparseMatrix") -bst <- xgboost(data = train$data, label = train$label, max.depth = 2, eta = 1, nround = 2, - objective = "binary:logistic") -# alternatively, you can put in dense matrix, i.e. basic R-matrix -print("training xgboost with Matrix") -bst <- xgboost(data = as.matrix(train$data), label = train$label, max.depth = 2, eta = 1, nround = 2, - objective = "binary:logistic") +Basic Training using XGBoost +---------------------------- -# you can also put in xgb.DMatrix object, stores label, data and other meta datas needed for advanced features -print("training xgboost with xgb.DMatrix") +The most critical part of the process is the training. + +We are using the train data. Both `data` and `label` are in each data (explained above). To access to the field of a `S3` object we use the `$` character in **R**. + +> label is the outcome of our dataset. It is the classification we want to predict. For these data we already have it, but when our model is built, that is this column we want to guess. + +In sparse matrix, cells which contains `0` are not encoded. Therefore, in a dataset where there are plenty of `0`, dataset size is optimized. It is very usual to have such dataset. Xgboost can manage both dense and sparse matrix. + +```{r trainingSparse, message=F, warning=F} +bstSparse <- xgboost(data = train$data, label = train$label, max.depth = 2, eta = 1, nround = 2, objective = "binary:logistic") +``` + +Alternatively, you can put your dataset in a dense matrix, i.e. a basic R-matrix. + +```{r trainingDense, message=F, warning=F} +bstDense <- xgboost(data = as.matrix(train$data), label = train$label, max.depth = 2, eta = 1, nround = 2, + objective = "binary:logistic") +``` + +Above, data and label are not stored together. + +Xgboost offer a way to group them in a `xgb.DMatrix`. You can even add other meta data. It will be usefull for the most advanced features. + +```{r trainingDense, message=F, warning=F} dtrain <- xgb.DMatrix(data = train$data, label = train$label) bst <- xgboost(data = dtrain, max.depth = 2, eta = 1, nround = 2, objective = "binary:logistic") +``` # Verbose = 0,1,2 print ('train xgboost with verbose 0, no message') From d7ba5c15119c9119323c57b4045a6ef0692c12a7 Mon Sep 17 00:00:00 2001 From: El Potaeto Date: Tue, 10 Feb 2015 19:46:39 +0100 Subject: [PATCH 92/94] text vignette --- R-package/vignettes/xgboostPresentation.Rmd | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/R-package/vignettes/xgboostPresentation.Rmd b/R-package/vignettes/xgboostPresentation.Rmd index 77a250a2c..29b8e40f1 100644 --- a/R-package/vignettes/xgboostPresentation.Rmd +++ b/R-package/vignettes/xgboostPresentation.Rmd @@ -83,23 +83,24 @@ Xgboost offer a way to group them in a `xgb.DMatrix`. You can even add other met ```{r trainingDense, message=F, warning=F} dtrain <- xgb.DMatrix(data = train$data, label = train$label) -bst <- xgboost(data = dtrain, max.depth = 2, eta = 1, nround = 2, objective = "binary:logistic") +bstDMatrix <- xgboost(data = dtrain, max.depth = 2, eta = 1, nround = 2, objective = "binary:logistic") ``` -# Verbose = 0,1,2 +Below is a demonstration of the effect of verbose parameter. + +```{r trainingVerbose, message=T, warning=F} print ('train xgboost with verbose 0, no message') bst <- xgboost(data = dtrain, max.depth = 2, eta = 1, nround = 2, objective = "binary:logistic", verbose = 0) + print ('train xgboost with verbose 1, print evaluation metric') bst <- xgboost(data = dtrain, max.depth = 2, eta = 1, nround = 2, objective = "binary:logistic", verbose = 1) + print ('train xgboost with verbose 2, also print information about tree') bst <- xgboost(data = dtrain, max.depth = 2, eta = 1, nround = 2, objective = "binary:logistic", verbose = 2) - -# you can also specify data as file path to a LibSVM format input -# since we do not have this file with us, the following line is just for illustration -# bst <- xgboost(data = 'agaricus.train.svm', max.depth = 2, eta = 1, nround = 2,objective = "binary:logistic") +``` #--------------------basic prediction using xgboost-------------- # you can do prediction using the following line From dc9e4905e4a402cf86a4995d0bb00e9fb82a0f4b Mon Sep 17 00:00:00 2001 From: pommedeterresautee Date: Tue, 10 Feb 2015 22:48:16 +0100 Subject: [PATCH 93/94] Vignette text --- R-package/vignettes/xgboostPresentation.Rmd | 133 +++++++++++++++----- 1 file changed, 105 insertions(+), 28 deletions(-) diff --git a/R-package/vignettes/xgboostPresentation.Rmd b/R-package/vignettes/xgboostPresentation.Rmd index 29b8e40f1..90bb49d9c 100644 --- a/R-package/vignettes/xgboostPresentation.Rmd +++ b/R-package/vignettes/xgboostPresentation.Rmd @@ -42,10 +42,10 @@ train <- agaricus.train test <- agaricus.test ``` -> In the reality, it would be up to you to make this division between `train` and `test` data. - > Each variable is a S3 object containing both label and data. +> In the real world, it would be up to you to make this division between `train` and `test` data. + The loaded data is stored in `dgCMatrix` which is a **sparse matrix** type. Label is a `numeric` vector in `{0,1}`. @@ -64,7 +64,7 @@ We are using the train data. Both `data` and `label` are in each data (explained > label is the outcome of our dataset. It is the classification we want to predict. For these data we already have it, but when our model is built, that is this column we want to guess. -In sparse matrix, cells which contains `0` are not encoded. Therefore, in a dataset where there are plenty of `0`, dataset size is optimized. It is very usual to have such dataset. Xgboost can manage both dense and sparse matrix. +In sparse matrix, cells which contains `0` are not encoded. Therefore, in a dataset where there are plenty of `0`, dataset size is optimized. It is very usual to have such dataset. **Xgboost** can manage both dense and sparse matrix. ```{r trainingSparse, message=F, warning=F} bstSparse <- xgboost(data = train$data, label = train$label, max.depth = 2, eta = 1, nround = 2, objective = "binary:logistic") @@ -79,9 +79,9 @@ bstDense <- xgboost(data = as.matrix(train$data), label = train$label, max.depth Above, data and label are not stored together. -Xgboost offer a way to group them in a `xgb.DMatrix`. You can even add other meta data. It will be usefull for the most advanced features. +**Xgboost** offer a way to group them in a `xgb.DMatrix`. You can even add other meta data. It will be usefull for the most advanced features. -```{r trainingDense, message=F, warning=F} +```{r trainingDmatrix, message=F, warning=F} dtrain <- xgb.DMatrix(data = train$data, label = train$label) bstDMatrix <- xgboost(data = dtrain, max.depth = 2, eta = 1, nround = 2, objective = "binary:logistic") ``` @@ -89,76 +89,153 @@ bstDMatrix <- xgboost(data = dtrain, max.depth = 2, eta = 1, nround = 2, objecti Below is a demonstration of the effect of verbose parameter. ```{r trainingVerbose, message=T, warning=F} -print ('train xgboost with verbose 0, no message') +# verbose 0, no message bst <- xgboost(data = dtrain, max.depth = 2, eta = 1, nround = 2, objective = "binary:logistic", verbose = 0) -print ('train xgboost with verbose 1, print evaluation metric') +# verbose 1, print evaluation metric bst <- xgboost(data = dtrain, max.depth = 2, eta = 1, nround = 2, objective = "binary:logistic", verbose = 1) -print ('train xgboost with verbose 2, also print information about tree') +# verbose 2, also print information about tree bst <- xgboost(data = dtrain, max.depth = 2, eta = 1, nround = 2, objective = "binary:logistic", verbose = 2) ``` -#--------------------basic prediction using xgboost-------------- -# you can do prediction using the following line -# you can put in Matrix, sparseMatrix, or xgb.DMatrix +Basic prediction using Xgboost +------------------------------ + +The main use of **Xgboost** is to predict data. For that purpose we will use the test dataset. We remind you that the algorithm has never seen these data. + +```{r predicting, message=F, warning=F} pred <- predict(bst, test$data) err <- mean(as.numeric(pred > 0.5) != test$label) print(paste("test-error=", err)) +``` -#-------------------save and load models------------------------- +> You can put data in Matrix, sparseMatrix, or xgb.DMatrix + +Save and load models +-------------------- + +When your dataset is big, it may takes time to build a model. Or may be you are not a big fan of loosing time in redoing the same thing again and again. In these cases, you will want to save your model and load it when required. + +Hopefully for you, **Xgboost** implement such functions. + +```{r saveLoadModel, message=F, warning=F} # save model to binary local file xgb.save(bst, "xgboost.model") + # load binary model to R bst2 <- xgb.load("xgboost.model") pred2 <- predict(bst2, test$data) + # pred2 should be identical to pred print(paste("sum(abs(pred2-pred))=", sum(abs(pred2-pred)))) +``` +In some very specific cases, like when you want to pilot **Xgboost** from `caret`, you will want to save the model as a **R** binary vector. See below how to do it. + +```{r saveLoadRBinVectorModel, message=F, warning=F} # save model to R's raw vector raw = xgb.save.raw(bst) + # load binary model to R bst3 <- xgb.load(raw) pred3 <- predict(bst3, test$data) + # pred2 should be identical to pred print(paste("sum(abs(pred3-pred))=", sum(abs(pred2-pred)))) +``` -#----------------Advanced features -------------- -# to use advanced features, we need to put data in xgb.DMatrix + + +Advanced features +================= + +Most of the features below have been created to help you to improve your model by offering a better understanding of its content. + + +Dataset preparation +------------------- + +For the following advanced features, we need to put data in `xgb.DMatrix` as explained above. + +```{r DMatrix, message=F, warning=F} dtrain <- xgb.DMatrix(data = train$data, label=train$label) dtest <- xgb.DMatrix(data = test$data, label=test$label) -#---------------Using watchlist---------------- -# watchlist is a list of xgb.DMatrix, each of them tagged with name +``` + +Using xgb.train +--------------- + +`xgb.train` is a powerfull way to follow progress in learning of one or more dataset. + +One way to measure progress in learning of a model is to provide to the **Xgboost** a second dataset already classified. Therefore it can learn on the real dataset and test its model on the second one. Some metrics are measured after each round during the learning. + +For that purpose, you will use `watchlist` parameter. It is a list of `xgb.DMatrix`, each of them tagged with a name. + +```{r watchlist, message=F, warning=F} watchlist <- list(train=dtrain, test=dtest) -# to train with watchlist, use xgb.train, which contains more advanced features -# watchlist allows us to monitor the evaluation result on all data in the list -print ('train xgboost using xgb.train with watchlist') + bst <- xgb.train(data=dtrain, max.depth=2, eta=1, nround=2, watchlist=watchlist, objective = "binary:logistic") -# we can change evaluation metrics, or use multiple evaluation metrics -print ('train xgboost using xgb.train with watchlist, watch logloss and error') +``` + +> To train with watchlist, we use `xgb.train`, which contains more advanced features than `xgboost` function. + +For a better understanding, you may want to have some specific metric or even use multiple evaluation metrics. + +`eval.metric` allows us to monitor the evaluation of several metrics at a time. Hereafter we will watch two new metrics, logloss and error. + +```{r watchlist2, message=F, warning=F} bst <- xgb.train(data=dtrain, max.depth=2, eta=1, nround=2, watchlist=watchlist, eval.metric = "error", eval.metric = "logloss", objective = "binary:logistic") +``` -# xgb.DMatrix can also be saved using xgb.DMatrix.save +Manipulating xgb.DMatrix +------------------------ + +### Save / Load + +Like saving models, `xgb.DMatrix` object (which groups both dataset and outcome) can also be saved using `xgb.DMatrix.save` function. + +```{r DMatrixSave, message=F, warning=F} xgb.DMatrix.save(dtrain, "dtrain.buffer") # to load it in, simply call xgb.DMatrix dtrain2 <- xgb.DMatrix("dtrain.buffer") bst <- xgb.train(data=dtrain2, max.depth=2, eta=1, nround=2, watchlist=watchlist, objective = "binary:logistic") -# information can be extracted from xgb.DMatrix using getinfo +``` + +### Information extraction + +Information can be extracted from `xgb.DMatrix` using `getinfo` function. Hereafter we will extract `label` data. + +```{r getinfo, message=F, warning=F} label = getinfo(dtest, "label") pred <- predict(bst, dtest) err <- as.numeric(sum(as.integer(pred > 0.5) != label))/length(label) print(paste("test-error=", err)) +``` -# You can dump the tree you learned using xgb.dump into a text file -xgb.dump(bst, "dump.raw.txt", with.stats = T) +View the trees from a model +--------------------------- -# Finally, you can check which features are the most important. -print("Most important features (look at column Gain):") -print(xgb.importance(feature_names = train$data@Dimnames[[2]], filename_dump = "dump.raw.txt")) +You can dump the tree you learned using `xgb.dump` into a text file. + +```{r dump, message=T, warning=F} +xgb.dump(bst, with.stats = T) +``` + +Feature importance +------------------ + +Finally, you can check which features are the most important. + +```{r featureImportance, message=T, warning=F} +importance_matrix <- xgb.importance(feature_names = train$data@Dimnames[[2]], model = bst) +print(importance_matrix) +xgb.plot.importance(importance_matrix) +``` \ No newline at end of file From a16cbedfabff6efc40704cc20692598cfecc075f Mon Sep 17 00:00:00 2001 From: tqchen Date: Tue, 10 Feb 2015 21:49:29 -0800 Subject: [PATCH 94/94] try fix memleak when test data have more features than training --- src/gbm/gblinear-inl.hpp | 7 ++++--- src/tree/model.h | 2 ++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/gbm/gblinear-inl.hpp b/src/gbm/gblinear-inl.hpp index de9ee6173..3d2f36f5f 100644 --- a/src/gbm/gblinear-inl.hpp +++ b/src/gbm/gblinear-inl.hpp @@ -159,7 +159,7 @@ class GBLinear : public IGradBooster { } fo << "weight:\n"; for (int i = 0; i < model.param.num_output_group; ++i) { - for (int j = 0; j = model.param.num_feature) continue; psum += inst[i].fvalue * model[inst[i].index][gid]; } preds[gid] = psum; @@ -229,7 +230,7 @@ class GBLinear : public IGradBooster { // model parameter struct Param { // number of feature dimension - int num_feature; + unsigned num_feature; // number of output group int num_output_group; // reserved field @@ -242,7 +243,7 @@ class GBLinear : public IGradBooster { } inline void SetParam(const char *name, const char *val) { using namespace std; - if (!strcmp(name, "bst:num_feature")) num_feature = atoi(val); + if (!strcmp(name, "bst:num_feature")) num_feature = static_cast(atoi(val)); if (!strcmp(name, "num_output_group")) num_output_group = atoi(val); } }; diff --git a/src/tree/model.h b/src/tree/model.h index f3575488a..4d325ea8c 100644 --- a/src/tree/model.h +++ b/src/tree/model.h @@ -503,12 +503,14 @@ class RegTree: public TreeModel{ /*! \brief fill the vector with sparse vector */ inline void Fill(const RowBatch::Inst &inst) { for (bst_uint i = 0; i < inst.length; ++i) { + if (inst[i].index >= data.size()) continue; data[inst[i].index].fvalue = inst[i].fvalue; } } /*! \brief drop the trace after fill, must be called after fill */ inline void Drop(const RowBatch::Inst &inst) { for (bst_uint i = 0; i < inst.length; ++i) { + if (inst[i].index >= data.size()) continue; data[inst[i].index].flag = -1; } }