From b1b149282188ca6e1c9eaaf123feafc483aa9b72 Mon Sep 17 00:00:00 2001 From: theJonan Date: Fri, 3 Feb 2017 16:16:06 +0200 Subject: [PATCH 01/22] - First version of path printing. --- src/mlpack/methods/det/det_main.cpp | 173 +++++++++++++++++++++-- src/mlpack/methods/det/dt_utils.hpp | 4 +- src/mlpack/methods/det/dt_utils_impl.hpp | 12 +- src/mlpack/methods/det/dtree.hpp | 14 ++ src/mlpack/methods/det/dtree_impl.hpp | 36 ++++- 5 files changed, 221 insertions(+), 18 deletions(-) diff --git a/src/mlpack/methods/det/det_main.cpp b/src/mlpack/methods/det/det_main.cpp index 7323d9bb01..900e1c779d 100644 --- a/src/mlpack/methods/det/det_main.cpp +++ b/src/mlpack/methods/det/det_main.cpp @@ -57,6 +57,15 @@ PARAM_STRING_OUT("test_set_estimates_file", "The file in which to output the " PARAM_STRING_OUT("vi_file", "The file to output the variable importance values " "for each feature.", "i"); +// Tagging and path printing options +PARAM_STRING_IN("path_format", "The format of path printing - lr|idlr|ldid", + "p", "lr"); + +PARAM_STRING_IN("tag_counters", "The file to output tag counters.", "c", ""); + +PARAM_STRING_OUT("tag_file", "The file to output the tags (and possibly paths) " + " for each sample in the test set.", "g"); + // Parameters for the training algorithm. PARAM_INT_IN("folds", "The number of folds of cross-validation to perform for the " "estimation (0 is LOOCV)", "f", 10); @@ -72,6 +81,45 @@ PARAM_FLAG("volume_regularization", "This flag gives the used the option to use" "penalize low volume leaves.", "R"); */ + +class PathCacher +{ +public: + enum PathFormat + { + FormatLR, + FormatLR_ID, + FormatID_LR + }; + + template + PathCacher(PathFormat fmt, DTree* tree); + + ~PathCacher(); + + template + void Enter(const DTree* node, const DTree* parent); + + template + void Leave(const DTree* node, const DTree* parent); + + const std::string& PathFor(int tag) const; + + size_t NumLeaves() const { return numLeaves; } + +private: + typedef std::list > PathType; + + PathType path; + PathFormat format; + + int numLeaves; + std::string* pathCache; + + std::string BuildString(); +}; + + int main(int argc, char *argv[]) { CLI::ParseCommandLine(argc, argv); @@ -162,17 +210,67 @@ int main(int argc, char *argv[]) { arma::mat testData; data::Load(testFile, testData, true); + if (CLI::HasParam("test_set_estimates_file")) + { + // Compute test set densities. + Timer::Start("det_test_set_estimation"); + arma::rowvec testDensities(testData.n_cols); + for (size_t i = 0; i < testData.n_cols; i++) + testDensities[i] = tree->ComputeValue(testData.unsafe_col(i)); + Timer::Stop("det_test_set_estimation"); - // Compute test set densities. - Timer::Start("det_test_set_estimation"); - arma::rowvec testDensities(testData.n_cols); - for (size_t i = 0; i < testData.n_cols; i++) - testDensities[i] = tree->ComputeValue(testData.unsafe_col(i)); - Timer::Stop("det_test_set_estimation"); + if (CLI::GetParam("test_set_estimates_file") != "") + data::Save(CLI::GetParam("test_set_estimates_file"), + testDensities); + } + + if (CLI::HasParam("tag_file")) + { + const string tagFile = CLI::GetParam("tag_file"); + std::ofstream ofs; + ofs.open(tagFile, std::ofstream::out); + + arma::Row counters; - if (CLI::GetParam("test_set_estimates_file") != "") - data::Save(CLI::GetParam("test_set_estimates_file"), - testDensities); + Timer::Start("det_test_set_tagging"); + if (!ofs.is_open()) + { + Log::Warn << "Unable to open file '" << tagFile + << "' to save tag membership info." + << std::endl; + } + else if (CLI::HasParam("path_format")) + { + PathCacher path(PathCacher::FormatLR, tree); + counters.zeros(path.NumLeaves()); + + for (size_t i = 0; i < testData.n_cols; ++i) + { + int tag = tree->FindBucket(testData.unsafe_col(i)); + counters(tag) += 1; + + ofs << tag << " " << path.PathFor(tag) << std::endl; + } + } + else + { + int numLeaves = tree->TagTree(); + counters.zeros(numLeaves); + + for (size_t i = 0; i < testData.n_cols; ++i) + { + int tag = tree->FindBucket(testData.unsafe_col(i)); + counters(tag) += 1; + ofs << tag << std::endl; + } + } + + if (CLI::GetParam("tag_counters") != "") + data::Save(CLI::GetParam("tag_counters"), counters); + + Timer::Stop("det_test_set_tagging"); + ofs.close(); + } } // Print variable importance. @@ -186,3 +284,60 @@ int main(int argc, char *argv[]) delete tree; } + + +template +PathCacher::PathCacher(PathCacher::PathFormat fmt, DTree* dtree) : format(fmt) +{ + numLeaves = dtree->TagTree(); + pathCache = new std::string [numLeaves]; + assert(!!pathCache); + dtree->EnumerateTree(*this); +} + +PathCacher::~PathCacher() +{ + delete[] pathCache; +} + +template +void PathCacher::Enter(const DTree* node, const DTree* parent) +{ + if (parent == nullptr) + return; + + int tag = node->BucketTag(); + + path.push_back(PathType::value_type(parent->Left() == node, tag)); + if (tag >= 0) + pathCache[tag] = BuildString(); +} + +template +void PathCacher::Leave(const DTree* , const DTree* parent) +{ + if (parent != nullptr) + path.pop_back(); +} + +std::string PathCacher::BuildString() +{ + std::string str(""); + for (PathType::iterator it = path.begin(); it != path.end(); it++) + { + switch (format) + { + case FormatLR: str += it->first ? "L" : "R"; break; + default: + assert(0); + } + } + + return str; +} + +const std::string& PathCacher::PathFor(int tag) const +{ + assert(tag >= 0 && tag < numLeaves ); + return pathCache[tag]; +} diff --git a/src/mlpack/methods/det/dt_utils.hpp b/src/mlpack/methods/det/dt_utils.hpp index e8456006d6..ff1cb10471 100644 --- a/src/mlpack/methods/det/dt_utils.hpp +++ b/src/mlpack/methods/det/dt_utils.hpp @@ -35,8 +35,8 @@ void PrintLeafMembership(DTree* dtree, const MatType& data, const arma::Mat& labels, const size_t numClasses, - const std::string leafClassMembershipFile = ""); - + const std::string& leafClassMembershipFile = ""); + /** * Print the variable importance of each dimension of a density estimation tree. * Optionally, pass the name of a file to print this information to (otherwise diff --git a/src/mlpack/methods/det/dt_utils_impl.hpp b/src/mlpack/methods/det/dt_utils_impl.hpp index e9e1c61401..a9286b58bc 100644 --- a/src/mlpack/methods/det/dt_utils_impl.hpp +++ b/src/mlpack/methods/det/dt_utils_impl.hpp @@ -18,15 +18,15 @@ namespace mlpack { namespace det { -template -void PrintLeafMembership(DTree* dtree, +template +void PrintLeafMembership(DTree* dtree, const MatType& data, const arma::Mat& labels, const size_t numClasses, - const std::string leafClassMembershipFile) + const std::string& leafClassMembershipFile) { // Tag the leaves with numbers. - TagType numLeaves = dtree->TagTree(); + int numLeaves = dtree->TagTree(); arma::Mat table(numLeaves, (numClasses + 1)); table.zeros(); @@ -34,7 +34,7 @@ void PrintLeafMembership(DTree* dtree, for (size_t i = 0; i < data.n_cols; i++) { const typename MatType::vec_type testPoint = data.unsafe_col(i); - const TagType leafTag = dtree->FindBucket(testPoint); + const int leafTag = dtree->FindBucket(testPoint); const size_t label = labels[i]; table(leafTag, label) += 1; } @@ -65,7 +65,7 @@ void PrintLeafMembership(DTree* dtree, return; } - + template void PrintVariableImportance(const DTree* dtree, const std::string viFile) diff --git a/src/mlpack/methods/det/dtree.hpp b/src/mlpack/methods/det/dtree.hpp index d910af7167..7631bee162 100644 --- a/src/mlpack/methods/det/dtree.hpp +++ b/src/mlpack/methods/det/dtree.hpp @@ -161,6 +161,20 @@ class DTree * @param tag Tag for the next leaf; leave at 0 for the initial call. */ TagType TagTree(const TagType& tag = 0); + + + /** + * Traverses all nodes of the tree, including the inner ones. On each node + * two methods of the `enumer` are called: + * + * Enter(DTree* node, DTree* parent); + * Leave(Dtree* node, DTree* parent); + * + * @param walker An instance of custom class, receiver of the enumeration. + */ + template + void EnumerateTree(Walker& walker) const; + /** * Return the tag of the leaf containing the query. This is useful for diff --git a/src/mlpack/methods/det/dtree_impl.hpp b/src/mlpack/methods/det/dtree_impl.hpp index 5625006504..06abf07e6c 100644 --- a/src/mlpack/methods/det/dtree_impl.hpp +++ b/src/mlpack/methods/det/dtree_impl.hpp @@ -760,13 +760,47 @@ TagType DTree::TagTree(const TagType& tag) } } +// Enumerate the nodes of the tree. +template +template +void DTree::EnumerateTree(Walker& walker) const +{ + if (root == 1) + walker.Enter(this, (const DTree*)nullptr); + + if (subtreeLeaves > 1) + { + // walk the left ... + walker.Enter(left, this); + left->EnumerateTree(walker); + walker.Leave(left, this); + + // ... and the right. + walker.Enter(right, this); + right->EnumerateTree(walker); + walker.Leave(right, this); + } + + if (root == 1) + walker.Leave(this, (const DTree*)nullptr); + +} + template TagType DTree::FindBucket(const VecType& query) const { Log::Assert(query.n_elem == maxVals.n_elem); - if (subtreeLeaves == 1) // If we are a leaf... + if (root == 1) // If we are the root... + { + // Check if the query is within range. + if (!WithinRange(query)) + return bucketTag; + } + + // If we are a leaf... + if (subtreeLeaves == 1) { return bucketTag; } From c102205e0db3bed3b625ade008dfa94ad64c84ca Mon Sep 17 00:00:00 2001 From: theJonan Date: Fri, 3 Feb 2017 17:44:58 +0200 Subject: [PATCH 02/22] - Small fix. --- src/mlpack/methods/det/det_main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mlpack/methods/det/det_main.cpp b/src/mlpack/methods/det/det_main.cpp index 900e1c779d..d214895a02 100644 --- a/src/mlpack/methods/det/det_main.cpp +++ b/src/mlpack/methods/det/det_main.cpp @@ -61,7 +61,7 @@ PARAM_STRING_OUT("vi_file", "The file to output the variable importance values " PARAM_STRING_IN("path_format", "The format of path printing - lr|idlr|ldid", "p", "lr"); -PARAM_STRING_IN("tag_counters", "The file to output tag counters.", "c", ""); +PARAM_STRING_OUT("tag_counters", "The file to output tag counters.", "c"); PARAM_STRING_OUT("tag_file", "The file to output the tags (and possibly paths) " " for each sample in the test set.", "g"); From 94b32766e06427bea96a10580ef93f3b99b467a8 Mon Sep 17 00:00:00 2001 From: theJonan Date: Fri, 3 Feb 2017 18:06:02 +0200 Subject: [PATCH 03/22] - Parallelism added to tag/path computation. --- src/mlpack/methods/det/det_main.cpp | 38 ++++++++++++++++++++++++----- 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/src/mlpack/methods/det/det_main.cpp b/src/mlpack/methods/det/det_main.cpp index d214895a02..70afb966cd 100644 --- a/src/mlpack/methods/det/det_main.cpp +++ b/src/mlpack/methods/det/det_main.cpp @@ -215,8 +215,18 @@ int main(int argc, char *argv[]) // Compute test set densities. Timer::Start("det_test_set_estimation"); arma::rowvec testDensities(testData.n_cols); + +#ifdef _WIN32 + #pragma omp parallel for default(shared) + for (intmax_t i = 0; i < testData.n_cols; i++) +#else + #pragma omp parallel for default(shared) for (size_t i = 0; i < testData.n_cols; i++) +#endif + { testDensities[i] = tree->ComputeValue(testData.unsafe_col(i)); + } + Timer::Stop("det_test_set_estimation"); if (CLI::GetParam("test_set_estimates_file") != "") @@ -244,12 +254,20 @@ int main(int argc, char *argv[]) PathCacher path(PathCacher::FormatLR, tree); counters.zeros(path.NumLeaves()); - for (size_t i = 0; i < testData.n_cols; ++i) +#ifdef _WIN32 + #pragma omp parallel for default(shared) + for (intmax_t i = 0; i < testData.n_cols; i++) +#else + #pragma omp parallel for default(shared) + for (size_t i = 0; i < testData.n_cols; i++) +#endif { - int tag = tree->FindBucket(testData.unsafe_col(i)); - counters(tag) += 1; + const int tag = tree->FindBucket(testData.unsafe_col(i)); ofs << tag << " " << path.PathFor(tag) << std::endl; + + #pragma omp critical (DTreeCounterUpdate) + counters(tag) += 1; } } else @@ -257,11 +275,19 @@ int main(int argc, char *argv[]) int numLeaves = tree->TagTree(); counters.zeros(numLeaves); - for (size_t i = 0; i < testData.n_cols; ++i) +#ifdef _WIN32 + #pragma omp parallel for default(shared) + for (intmax_t i = 0; i < testData.n_cols; i++) +#else + #pragma omp parallel for default(shared) + for (size_t i = 0; i < testData.n_cols; i++) +#endif { - int tag = tree->FindBucket(testData.unsafe_col(i)); - counters(tag) += 1; + const int tag = tree->FindBucket(testData.unsafe_col(i)); ofs << tag << std::endl; + + #pragma omp critical (DTreeCounterUpdate) + counters(tag) += 1; } } From 0f0546ce002b37be67c9a36e9bcccef8e4edbf28 Mon Sep 17 00:00:00 2001 From: theJonan Date: Fri, 3 Feb 2017 20:04:03 +0200 Subject: [PATCH 04/22] - Parallelism removed on estimations (logN) --- src/mlpack/methods/det/det_main.cpp | 27 ++------------------------- 1 file changed, 2 insertions(+), 25 deletions(-) diff --git a/src/mlpack/methods/det/det_main.cpp b/src/mlpack/methods/det/det_main.cpp index 70afb966cd..c045ae9cbd 100644 --- a/src/mlpack/methods/det/det_main.cpp +++ b/src/mlpack/methods/det/det_main.cpp @@ -216,16 +216,8 @@ int main(int argc, char *argv[]) Timer::Start("det_test_set_estimation"); arma::rowvec testDensities(testData.n_cols); -#ifdef _WIN32 - #pragma omp parallel for default(shared) - for (intmax_t i = 0; i < testData.n_cols; i++) -#else - #pragma omp parallel for default(shared) for (size_t i = 0; i < testData.n_cols; i++) -#endif - { testDensities[i] = tree->ComputeValue(testData.unsafe_col(i)); - } Timer::Stop("det_test_set_estimation"); @@ -254,20 +246,12 @@ int main(int argc, char *argv[]) PathCacher path(PathCacher::FormatLR, tree); counters.zeros(path.NumLeaves()); -#ifdef _WIN32 - #pragma omp parallel for default(shared) - for (intmax_t i = 0; i < testData.n_cols; i++) -#else - #pragma omp parallel for default(shared) for (size_t i = 0; i < testData.n_cols; i++) -#endif { const int tag = tree->FindBucket(testData.unsafe_col(i)); - ofs << tag << " " << path.PathFor(tag) << std::endl; - - #pragma omp critical (DTreeCounterUpdate) counters(tag) += 1; + ofs << tag << " " << path.PathFor(tag) << std::endl; } } else @@ -275,18 +259,11 @@ int main(int argc, char *argv[]) int numLeaves = tree->TagTree(); counters.zeros(numLeaves); -#ifdef _WIN32 - #pragma omp parallel for default(shared) - for (intmax_t i = 0; i < testData.n_cols; i++) -#else - #pragma omp parallel for default(shared) for (size_t i = 0; i < testData.n_cols; i++) -#endif { const int tag = tree->FindBucket(testData.unsafe_col(i)); - ofs << tag << std::endl; - #pragma omp critical (DTreeCounterUpdate) + ofs << tag << std::endl; counters(tag) += 1; } } From c41aa8878b7dbfc3f6160a129dcc6b5866034b12 Mon Sep 17 00:00:00 2001 From: theJonan Date: Tue, 7 Feb 2017 00:03:45 +0200 Subject: [PATCH 05/22] - Fixes and small improvements. --- src/mlpack/methods/det/dt_utils_impl.hpp | 5 +++++ src/mlpack/methods/det/dtree.hpp | 5 +++++ src/mlpack/methods/det/dtree_impl.hpp | 23 ++++++++++++++++++++++- 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/src/mlpack/methods/det/dt_utils_impl.hpp b/src/mlpack/methods/det/dt_utils_impl.hpp index a9286b58bc..18df1a5312 100644 --- a/src/mlpack/methods/det/dt_utils_impl.hpp +++ b/src/mlpack/methods/det/dt_utils_impl.hpp @@ -116,6 +116,7 @@ DTree* Trainer(MatType& dataset, // Initialize the tree. DTree dtree(dataset); + Timer::Start("tree_growing"); // Prepare to grow the tree... arma::Col oldFromNew(dataset.n_cols); for (size_t i = 0; i < oldFromNew.n_elem; i++) @@ -129,6 +130,7 @@ DTree* Trainer(MatType& dataset, double alpha = dtree.Grow(newDataset, oldFromNew, useVolumeReg, maxLeafSize, minLeafSize); + Timer::Stop("tree_growing"); Log::Info << dtree.SubtreeLeaves() << " leaf nodes in the tree using full " << "dataset; minimum alpha: " << alpha << "." << std::endl; @@ -154,6 +156,8 @@ DTree* Trainer(MatType& dataset, outfile.close(); } + Timer::Start("prunning_sequence"); + // Sequentially prune and save the alpha values and the values of c_t^2 * r_t. std::vector > prunedSequence; while (dtree.SubtreeLeaves() > 1) @@ -175,6 +179,7 @@ DTree* Trainer(MatType& dataset, std::pair treeSeq(oldAlpha, dtree.SubtreeLeavesLogNegError()); prunedSequence.push_back(treeSeq); + Timer::Stop("prunning_sequence"); Log::Info << prunedSequence.size() << " trees in the sequence; maximum alpha:" << " " << oldAlpha << "." << std::endl; diff --git a/src/mlpack/methods/det/dtree.hpp b/src/mlpack/methods/det/dtree.hpp index 7631bee162..a2a2233e1f 100644 --- a/src/mlpack/methods/det/dtree.hpp +++ b/src/mlpack/methods/det/dtree.hpp @@ -57,6 +57,11 @@ class DTree * Create an empty density estimation tree. */ DTree(); + + /** + * Create a copy of an existing tree. + */ + DTree(const DTree& tree); /** * Create a density estimation tree with the given bounds and the given number diff --git a/src/mlpack/methods/det/dtree_impl.hpp b/src/mlpack/methods/det/dtree_impl.hpp index 06abf07e6c..39a8b1e112 100644 --- a/src/mlpack/methods/det/dtree_impl.hpp +++ b/src/mlpack/methods/det/dtree_impl.hpp @@ -67,7 +67,7 @@ namespace details const size_t minLeafSize) { typedef std::pair SplitItem; - arma::vec dimVec = data(dim, arma::span(start, end - 1)); + arma::rowvec dimVec = data(dim, arma::span(start, end - 1)); // We sort these, in-place (it's a copy of the data, anyways). std::sort(dimVec.begin(), dimVec.end()); @@ -165,6 +165,27 @@ DTree::DTree() : right(NULL) { /* Nothing to do. */ } +template +DTree::DTree(const DTree& src) : + start(src.start), + end(src.end), + splitDim(src.splitDim), + splitValue(src.splitValue), + logNegError(src.logNegError), + subtreeLeavesLogNegError(src.subtreeLeavesLogNegError), + subtreeLeaves(src.subtreeLeaves), + root(src.root), + ratio(src.ratio), + logVolume(src.logVolume), + bucketTag(src.bucketTag), + alphaUpper(src.alphaUpper) +{ + if (!!src.left) + left = new DTree(*src.left); + if (!!src.right) + right = new DTree(*src.right); +} + // Root node initializers From 8e1892a5277a17cf8d9b4a0df858bf82ed58eb5f Mon Sep 17 00:00:00 2001 From: theJonan Date: Tue, 7 Feb 2017 02:13:32 +0200 Subject: [PATCH 06/22] - More small fixes. - Optimized the DET model saving. - Added unpruned tree saving. --- src/mlpack/methods/det/det_main.cpp | 9 +++- src/mlpack/methods/det/dt_utils_impl.hpp | 9 +++- src/mlpack/methods/det/dtree.hpp | 8 ++-- src/mlpack/methods/det/dtree_impl.hpp | 55 ++++++++++++++---------- 4 files changed, 50 insertions(+), 31 deletions(-) diff --git a/src/mlpack/methods/det/det_main.cpp b/src/mlpack/methods/det/det_main.cpp index c045ae9cbd..e07af1468b 100644 --- a/src/mlpack/methods/det/det_main.cpp +++ b/src/mlpack/methods/det/det_main.cpp @@ -66,6 +66,9 @@ PARAM_STRING_OUT("tag_counters", "The file to output tag counters.", "c"); PARAM_STRING_OUT("tag_file", "The file to output the tags (and possibly paths) " " for each sample in the test set.", "g"); +PARAM_STRING_OUT("unpruned_tree", "The file to output the unpruned model to.", + "u"); + // Parameters for the training algorithm. PARAM_INT_IN("folds", "The number of folds of cross-validation to perform for the " "estimation (0 is LOOCV)", "f", 10); @@ -180,9 +183,11 @@ int main(int argc, char *argv[]) // Obtain the optimal tree. Timer::Start("det_training"); - tree = Trainer(trainingData, folds, regularization, maxLeafSize, minLeafSize, ""); + tree = Trainer(trainingData, folds, regularization, + maxLeafSize, minLeafSize, "", + CLI::GetParam("unpruned_tree")); Timer::Stop("det_training"); - + // Compute training set estimates, if desired. if (CLI::HasParam("training_set_estimates_file")) { diff --git a/src/mlpack/methods/det/dt_utils_impl.hpp b/src/mlpack/methods/det/dt_utils_impl.hpp index 18df1a5312..24ab262b95 100644 --- a/src/mlpack/methods/det/dt_utils_impl.hpp +++ b/src/mlpack/methods/det/dt_utils_impl.hpp @@ -111,7 +111,8 @@ DTree* Trainer(MatType& dataset, const bool useVolumeReg, const size_t maxLeafSize, const size_t minLeafSize, - const std::string unprunedTreeOutput) + const std::string unprunedTreeOutput, + const std::string unprunedModel) { // Initialize the tree. DTree dtree(dataset); @@ -134,6 +135,12 @@ DTree* Trainer(MatType& dataset, Log::Info << dtree.SubtreeLeaves() << " leaf nodes in the tree using full " << "dataset; minimum alpha: " << alpha << "." << std::endl; + if (unprunedModel != "") + { + Log::Info << "Saving unprunned tree in: " << unprunedModel << std::endl; + data::Save(unprunedModel, "det_model", dtree, false); + } + // Compute densities for the training points in the full tree, if we were // asked for this. if (unprunedTreeOutput != "") diff --git a/src/mlpack/methods/det/dtree.hpp b/src/mlpack/methods/det/dtree.hpp index a2a2233e1f..15012cc1ac 100644 --- a/src/mlpack/methods/det/dtree.hpp +++ b/src/mlpack/methods/det/dtree.hpp @@ -57,11 +57,6 @@ class DTree * Create an empty density estimation tree. */ DTree(); - - /** - * Create a copy of an existing tree. - */ - DTree(const DTree& tree); /** * Create a density estimation tree with the given bounds and the given number @@ -327,6 +322,9 @@ class DTree const size_t splitDim, const ElemType splitValue, arma::Col& oldFromNew) const; + + void FillMinMax(const StatType& mins, + const StatType& maxs); }; diff --git a/src/mlpack/methods/det/dtree_impl.hpp b/src/mlpack/methods/det/dtree_impl.hpp index 39a8b1e112..dc5f8082ec 100644 --- a/src/mlpack/methods/det/dtree_impl.hpp +++ b/src/mlpack/methods/det/dtree_impl.hpp @@ -165,27 +165,6 @@ DTree::DTree() : right(NULL) { /* Nothing to do. */ } -template -DTree::DTree(const DTree& src) : - start(src.start), - end(src.end), - splitDim(src.splitDim), - splitValue(src.splitValue), - logNegError(src.logNegError), - subtreeLeavesLogNegError(src.subtreeLeavesLogNegError), - subtreeLeaves(src.subtreeLeaves), - root(src.root), - ratio(src.ratio), - logVolume(src.logVolume), - bucketTag(src.bucketTag), - alphaUpper(src.alphaUpper) -{ - if (!!src.left) - left = new DTree(*src.left); - if (!!src.right) - right = new DTree(*src.right); -} - // Root node initializers @@ -863,6 +842,29 @@ DTree::ComputeVariableImportance(arma::vec& importances) const } } +template +void DTree::FillMinMax(const StatType& mins, + const StatType& maxs) +{ + if (!root) + { + minVals = mins; + maxVals = maxs; + } + + if (left && right) + { + StatType maxValsL(maxs); + StatType maxValsR(maxs); + StatType minValsL(mins); + StatType minValsR(mins); + + maxValsL[splitDim] = minValsR[splitDim] = splitValue; + left->FillMinMax(minValsL, maxValsL); + right->FillMinMax(minValsR, maxValsR); + } +} + template template void DTree::Serialize(Archive& ar, const unsigned int /* version */) @@ -871,8 +873,6 @@ void DTree::Serialize(Archive& ar, const unsigned int /* versi ar & CreateNVP(start, "start"); ar & CreateNVP(end, "end"); - ar & CreateNVP(maxVals, "maxVals"); - ar & CreateNVP(minVals, "minVals"); ar & CreateNVP(splitDim, "splitDim"); ar & CreateNVP(splitValue, "splitValue"); ar & CreateNVP(logNegError, "logNegError"); @@ -894,5 +894,14 @@ void DTree::Serialize(Archive& ar, const unsigned int /* versi ar & CreateNVP(left, "left"); ar & CreateNVP(right, "right"); + + if (root) + { + ar & CreateNVP(maxVals, "maxVals"); + ar & CreateNVP(minVals, "minVals"); + + if (Archive::is_loading::value && left && right) + FillMinMax(minVals, maxVals); + } } From 4041631b516253dbddd186c30ab30a7ac9f35a01 Mon Sep 17 00:00:00 2001 From: theJonan Date: Tue, 7 Feb 2017 02:17:17 +0200 Subject: [PATCH 07/22] - Minor logging fix. --- src/mlpack/methods/det/dt_utils_impl.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mlpack/methods/det/dt_utils_impl.hpp b/src/mlpack/methods/det/dt_utils_impl.hpp index 24ab262b95..fced4490c1 100644 --- a/src/mlpack/methods/det/dt_utils_impl.hpp +++ b/src/mlpack/methods/det/dt_utils_impl.hpp @@ -137,8 +137,8 @@ DTree* Trainer(MatType& dataset, if (unprunedModel != "") { - Log::Info << "Saving unprunned tree in: " << unprunedModel << std::endl; data::Save(unprunedModel, "det_model", dtree, false); + Log::Info << "Saved unprunned tree in: " << unprunedModel << std::endl; } // Compute densities for the training points in the full tree, if we were From 8010a98b39378e9d6913e236a1634fb2e560ca54 Mon Sep 17 00:00:00 2001 From: theJonan Date: Tue, 7 Feb 2017 11:24:21 +0200 Subject: [PATCH 08/22] - Internal nodes printing enabled. --- src/mlpack/methods/det/det_main.cpp | 31 ++++++++++++++++++++---- src/mlpack/methods/det/dt_utils_impl.hpp | 2 +- src/mlpack/methods/det/dtree.hpp | 4 +-- src/mlpack/methods/det/dtree_impl.hpp | 15 +++++++++--- 4 files changed, 40 insertions(+), 12 deletions(-) diff --git a/src/mlpack/methods/det/det_main.cpp b/src/mlpack/methods/det/det_main.cpp index e07af1468b..e22c35d489 100644 --- a/src/mlpack/methods/det/det_main.cpp +++ b/src/mlpack/methods/det/det_main.cpp @@ -248,7 +248,22 @@ int main(int argc, char *argv[]) } else if (CLI::HasParam("path_format")) { - PathCacher path(PathCacher::FormatLR, tree); + const string pathFormat = CLI::GetParam("path_format"); + PathCacher::PathFormat theFormat; + if (pathFormat == "lr" || pathFormat == "LR") + theFormat = PathCacher::FormatLR; + else if (pathFormat == "lr-id" || pathFormat == "LR-ID") + theFormat = PathCacher::FormatLR_ID; + else if (pathFormat == "id-lr" || pathFormat == "ID-LR") + theFormat = PathCacher::FormatID_LR; + else + { + Log::Warn << "Unknown path format specified: '" << pathFormat + << "'. Valid are: lr | lr-id | id-lr. Defaults to 'lr'" << endl; + theFormat = PathCacher::FormatLR; + } + + PathCacher path(theFormat, tree); counters.zeros(path.NumLeaves()); for (size_t i = 0; i < testData.n_cols; i++) @@ -297,7 +312,7 @@ int main(int argc, char *argv[]) template PathCacher::PathCacher(PathCacher::PathFormat fmt, DTree* dtree) : format(fmt) { - numLeaves = dtree->TagTree(); + numLeaves = dtree->TagTree(0, true); pathCache = new std::string [numLeaves]; assert(!!pathCache); dtree->EnumerateTree(*this); @@ -335,9 +350,15 @@ std::string PathCacher::BuildString() { switch (format) { - case FormatLR: str += it->first ? "L" : "R"; break; - default: - assert(0); + case FormatLR: + str += it->first ? "L" : "R"; + break; + case FormatLR_ID: + str += (it->first ? "L" : "R") + std::to_string(it->second); + break; + case FormatID_LR: + str += std::to_string(it->second) + (it->first ? "L" : "R"); + break; } } diff --git a/src/mlpack/methods/det/dt_utils_impl.hpp b/src/mlpack/methods/det/dt_utils_impl.hpp index fced4490c1..5e5b1e10c9 100644 --- a/src/mlpack/methods/det/dt_utils_impl.hpp +++ b/src/mlpack/methods/det/dt_utils_impl.hpp @@ -190,7 +190,7 @@ DTree* Trainer(MatType& dataset, Log::Info << prunedSequence.size() << " trees in the sequence; maximum alpha:" << " " << oldAlpha << "." << std::endl; - MatType cvData(dataset); + const MatType cvData(dataset); const size_t testSize = dataset.n_cols / folds; arma::vec regularizationConstants(prunedSequence.size()); diff --git a/src/mlpack/methods/det/dtree.hpp b/src/mlpack/methods/det/dtree.hpp index 15012cc1ac..12a724d38d 100644 --- a/src/mlpack/methods/det/dtree.hpp +++ b/src/mlpack/methods/det/dtree.hpp @@ -160,7 +160,7 @@ class DTree * * @param tag Tag for the next leaf; leave at 0 for the initial call. */ - TagType TagTree(const TagType& tag = 0); + TagType TagTree(const TagType& tag = 0, bool internal = false); /** @@ -287,7 +287,7 @@ class DTree //! Return the upper part of the alpha sum. double AlphaUpper() const { return alphaUpper; } //! Return the current bucket's ID, if leaf, or -1 otherwise - TagType BucketTag() const { return subtreeLeaves == 1 ? bucketTag : -1; } + TagType BucketTag() const { return bucketTag; } //! Return the maximum values. const StatType& MaxVals() const { return maxVals; } diff --git a/src/mlpack/methods/det/dtree_impl.hpp b/src/mlpack/methods/det/dtree_impl.hpp index dc5f8082ec..487bee927c 100644 --- a/src/mlpack/methods/det/dtree_impl.hpp +++ b/src/mlpack/methods/det/dtree_impl.hpp @@ -746,7 +746,7 @@ double DTree::ComputeValue(const VecType& query) const // Index the buckets for possible usage later. template -TagType DTree::TagTree(const TagType& tag) +TagType DTree::TagTree(const TagType& tag, bool internal) { if (subtreeLeaves == 1) { @@ -754,10 +754,17 @@ TagType DTree::TagTree(const TagType& tag) bucketTag = tag; return (tag + 1); } - else + + TagType nextTag; + if (internal) { - return right->TagTree(left->TagTree(tag)); + bucketTag = tag; + nextTag = tag + 1; } + else + nextTag = tag; + + return right->TagTree(left->TagTree(nextTag, internal), internal); } // Enumerate the nodes of the tree. @@ -796,7 +803,7 @@ TagType DTree::FindBucket(const VecType& query) const { // Check if the query is within range. if (!WithinRange(query)) - return bucketTag; + return -1; } // If we are a leaf... From 6e9ab983bfd0d87d9192fd1e0e03eb23ea331582 Mon Sep 17 00:00:00 2001 From: theJonan Date: Tue, 7 Feb 2017 15:20:24 +0200 Subject: [PATCH 09/22] Omp shared fix --- src/mlpack/methods/det/dt_utils_impl.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mlpack/methods/det/dt_utils_impl.hpp b/src/mlpack/methods/det/dt_utils_impl.hpp index 5e5b1e10c9..6de4016070 100644 --- a/src/mlpack/methods/det/dt_utils_impl.hpp +++ b/src/mlpack/methods/det/dt_utils_impl.hpp @@ -202,11 +202,11 @@ DTree* Trainer(MatType& dataset, // implementation. #ifdef _WIN32 #pragma omp parallel for default(none) \ - shared(cvData, prunedSequence, regularizationConstants) + shared(prunedSequence, regularizationConstants) for (intmax_t fold = 0; fold < (intmax_t) folds; fold++) #else #pragma omp parallel for default(none) \ - shared(cvData, prunedSequence, regularizationConstants) + shared(prunedSequence, regularizationConstants) for (size_t fold = 0; fold < folds; fold++) #endif { From 52a626aec5ad79633f1c3a3693dab09b86e5c0bd Mon Sep 17 00:00:00 2001 From: theJonan Date: Tue, 7 Feb 2017 18:10:13 +0200 Subject: [PATCH 10/22] - More improvements. --- src/mlpack/methods/det/det_main.cpp | 29 +++++------- src/mlpack/methods/det/dt_utils.hpp | 3 +- src/mlpack/methods/det/dt_utils_impl.hpp | 60 ++++++++++++++---------- 3 files changed, 47 insertions(+), 45 deletions(-) diff --git a/src/mlpack/methods/det/det_main.cpp b/src/mlpack/methods/det/det_main.cpp index e22c35d489..3e5847a1d5 100644 --- a/src/mlpack/methods/det/det_main.cpp +++ b/src/mlpack/methods/det/det_main.cpp @@ -58,7 +58,7 @@ PARAM_STRING_OUT("vi_file", "The file to output the variable importance values " "for each feature.", "i"); // Tagging and path printing options -PARAM_STRING_IN("path_format", "The format of path printing - lr|idlr|ldid", +PARAM_STRING_IN("path_format", "The format of path printing - lr|id-lr|lr-id", "p", "lr"); PARAM_STRING_OUT("tag_counters", "The file to output tag counters.", "c"); @@ -66,8 +66,11 @@ PARAM_STRING_OUT("tag_counters", "The file to output tag counters.", "c"); PARAM_STRING_OUT("tag_file", "The file to output the tags (and possibly paths) " " for each sample in the test set.", "g"); -PARAM_STRING_OUT("unpruned_tree", "The file to output the unpruned model to.", - "u"); +PARAM_STRING_OUT("unpruned_estimates", "The file to output the estimations from " + "the unpruned tree.", "u"); + +PARAM_FLAG("skip_pruning", "Whether to bypass the pruning process and output " + "the unpruned tree only", "s"); // Parameters for the training algorithm. PARAM_INT_IN("folds", "The number of folds of cross-validation to perform for the " @@ -164,28 +167,18 @@ int main(int argc, char *argv[]) arma::mat trainingData; data::Load(trainSetFile, trainingData, true); - // Cross-validation here. - size_t folds = CLI::GetParam("folds"); - if (folds == 0) - { - folds = trainingData.n_cols; - Log::Info << "Performing leave-one-out cross validation." << endl; - } - else - { - Log::Info << "Performing " << folds << "-fold cross validation." << endl; - } - const bool regularization = false; // const bool regularization = CLI::HasParam("volume_regularization"); const int maxLeafSize = CLI::GetParam("max_leaf_size"); const int minLeafSize = CLI::GetParam("min_leaf_size"); + const bool skipPruning = CLI::HasParam("skip_pruning"); // Obtain the optimal tree. Timer::Start("det_training"); - tree = Trainer(trainingData, folds, regularization, - maxLeafSize, minLeafSize, "", - CLI::GetParam("unpruned_tree")); + tree = Trainer(trainingData, CLI::GetParam("folds"), + regularization, maxLeafSize, minLeafSize, + CLI::GetParam("unpruned_estimates"), + skipPruning); Timer::Stop("det_training"); // Compute training set estimates, if desired. diff --git a/src/mlpack/methods/det/dt_utils.hpp b/src/mlpack/methods/det/dt_utils.hpp index ff1cb10471..c5605190e5 100644 --- a/src/mlpack/methods/det/dt_utils.hpp +++ b/src/mlpack/methods/det/dt_utils.hpp @@ -67,7 +67,8 @@ DTree* Trainer(MatType& dataset, const bool useVolumeReg = false, const size_t maxLeafSize = 10, const size_t minLeafSize = 5, - const std::string unprunedTreeOutput = ""); + const std::string unprunedTreeOutput = "", + const bool skipPruning = false); } // namespace det } // namespace mlpack diff --git a/src/mlpack/methods/det/dt_utils_impl.hpp b/src/mlpack/methods/det/dt_utils_impl.hpp index 6de4016070..2966574c43 100644 --- a/src/mlpack/methods/det/dt_utils_impl.hpp +++ b/src/mlpack/methods/det/dt_utils_impl.hpp @@ -107,15 +107,15 @@ void PrintVariableImportance(const DTree* dtree, // folds. template DTree* Trainer(MatType& dataset, - const size_t folds, + size_t folds, const bool useVolumeReg, const size_t maxLeafSize, const size_t minLeafSize, const std::string unprunedTreeOutput, - const std::string unprunedModel) + const bool skipPruning) { // Initialize the tree. - DTree dtree(dataset); + DTree* dtree = new DTree(dataset); Timer::Start("tree_growing"); // Prepare to grow the tree... @@ -128,19 +128,13 @@ DTree* Trainer(MatType& dataset, // Growing the tree double oldAlpha = 0.0; - double alpha = dtree.Grow(newDataset, oldFromNew, useVolumeReg, maxLeafSize, + double alpha = dtree->Grow(newDataset, oldFromNew, useVolumeReg, maxLeafSize, minLeafSize); Timer::Stop("tree_growing"); - Log::Info << dtree.SubtreeLeaves() << " leaf nodes in the tree using full " + Log::Info << dtree->SubtreeLeaves() << " leaf nodes in the tree using full " << "dataset; minimum alpha: " << alpha << "." << std::endl; - if (unprunedModel != "") - { - data::Save(unprunedModel, "det_model", dtree, false); - Log::Info << "Saved unprunned tree in: " << unprunedModel << std::endl; - } - // Compute densities for the training points in the full tree, if we were // asked for this. if (unprunedTreeOutput != "") @@ -151,7 +145,7 @@ DTree* Trainer(MatType& dataset, for (size_t i = 0; i < dataset.n_cols; ++i) { arma::vec testPoint = dataset.unsafe_col(i); - outfile << dtree.ComputeValue(testPoint) << std::endl; + outfile << dtree->ComputeValue(testPoint) << std::endl; } } else @@ -162,28 +156,41 @@ DTree* Trainer(MatType& dataset, outfile.close(); } + + if (skipPruning) + return dtree; + if (folds == 0) + { + folds = dataset.n_cols; + Log::Info << "Performing leave-one-out cross validation." << std::endl; + } + else + { + Log::Info << "Performing " << folds << "-fold cross validation." << std::endl; + } + Timer::Start("prunning_sequence"); // Sequentially prune and save the alpha values and the values of c_t^2 * r_t. std::vector > prunedSequence; - while (dtree.SubtreeLeaves() > 1) + while (dtree->SubtreeLeaves() > 1) { - std::pair treeSeq(oldAlpha, dtree.SubtreeLeavesLogNegError()); + std::pair treeSeq(oldAlpha, dtree->SubtreeLeavesLogNegError()); prunedSequence.push_back(treeSeq); oldAlpha = alpha; - alpha = dtree.PruneAndUpdate(oldAlpha, dataset.n_cols, useVolumeReg); + alpha = dtree->PruneAndUpdate(oldAlpha, dataset.n_cols, useVolumeReg); // Some sanity checks. It seems that on some datasets, the error does not // increase as the tree is pruned but instead stays the same---hence the // "<=" in the final assert. Log::Assert((alpha < std::numeric_limits::max()) - || (dtree.SubtreeLeaves() == 1)); + || (dtree->SubtreeLeaves() == 1)); Log::Assert(alpha > oldAlpha); - Log::Assert(dtree.SubtreeLeavesLogNegError() <= treeSeq.second); + Log::Assert(dtree->SubtreeLeavesLogNegError() <= treeSeq.second); } - std::pair treeSeq(oldAlpha, dtree.SubtreeLeavesLogNegError()); + std::pair treeSeq(oldAlpha, dtree->SubtreeLeavesLogNegError()); prunedSequence.push_back(treeSeq); Timer::Stop("prunning_sequence"); @@ -305,8 +312,9 @@ DTree* Trainer(MatType& dataset, Log::Info << "Optimal alpha: " << optimalAlpha << "." << std::endl; - // Initialize the tree. - DTree* dtreeOpt = new DTree(dataset); + // Re-Initialize the tree. + delete dtree; + dtree = new DTree(dataset); // Getting ready to grow the tree... for (size_t i = 0; i < oldFromNew.n_elem; i++) @@ -317,28 +325,28 @@ DTree* Trainer(MatType& dataset, // Grow the tree. oldAlpha = -DBL_MAX; - alpha = dtreeOpt->Grow(newDataset, + alpha = dtree->Grow(newDataset, oldFromNew, useVolumeReg, maxLeafSize, minLeafSize); // Prune with optimal alpha. - while ((oldAlpha < optimalAlpha) && (dtreeOpt->SubtreeLeaves() > 1)) + while ((oldAlpha < optimalAlpha) && (dtree->SubtreeLeaves() > 1)) { oldAlpha = alpha; - alpha = dtreeOpt->PruneAndUpdate(oldAlpha, newDataset.n_cols, useVolumeReg); + alpha = dtree->PruneAndUpdate(oldAlpha, newDataset.n_cols, useVolumeReg); // Some sanity checks. Log::Assert((alpha < std::numeric_limits::max()) || - (dtreeOpt->SubtreeLeaves() == 1)); + (dtree->SubtreeLeaves() == 1)); Log::Assert(alpha > oldAlpha); } - Log::Info << dtreeOpt->SubtreeLeaves() << " leaf nodes in the optimally " + Log::Info << dtree->SubtreeLeaves() << " leaf nodes in the optimally " << "pruned tree; optimal alpha: " << oldAlpha << "." << std::endl; - return dtreeOpt; + return dtree; } } // namespace det From ae228e2a2579872f96bafe4c7d39039d7021a017 Mon Sep 17 00:00:00 2001 From: theJonan Date: Tue, 7 Feb 2017 18:19:15 +0200 Subject: [PATCH 11/22] More OMP fixes for folds. --- src/mlpack/methods/det/det_main.cpp | 8 ++++++-- src/mlpack/methods/det/dt_utils_impl.hpp | 9 ++------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/mlpack/methods/det/det_main.cpp b/src/mlpack/methods/det/det_main.cpp index 3e5847a1d5..65603bfc43 100644 --- a/src/mlpack/methods/det/det_main.cpp +++ b/src/mlpack/methods/det/det_main.cpp @@ -172,11 +172,15 @@ int main(int argc, char *argv[]) const int maxLeafSize = CLI::GetParam("max_leaf_size"); const int minLeafSize = CLI::GetParam("min_leaf_size"); const bool skipPruning = CLI::HasParam("skip_pruning"); + size_t folds = CLI::GetParam("folds"); + + if (folds == 0) + folds = trainingData.n_cols; // Obtain the optimal tree. Timer::Start("det_training"); - tree = Trainer(trainingData, CLI::GetParam("folds"), - regularization, maxLeafSize, minLeafSize, + tree = Trainer(trainingData, folds, regularization, + maxLeafSize, minLeafSize, CLI::GetParam("unpruned_estimates"), skipPruning); Timer::Stop("det_training"); diff --git a/src/mlpack/methods/det/dt_utils_impl.hpp b/src/mlpack/methods/det/dt_utils_impl.hpp index 2966574c43..289d15e06d 100644 --- a/src/mlpack/methods/det/dt_utils_impl.hpp +++ b/src/mlpack/methods/det/dt_utils_impl.hpp @@ -107,7 +107,7 @@ void PrintVariableImportance(const DTree* dtree, // folds. template DTree* Trainer(MatType& dataset, - size_t folds, + const size_t folds, const bool useVolumeReg, const size_t maxLeafSize, const size_t minLeafSize, @@ -160,15 +160,10 @@ DTree* Trainer(MatType& dataset, if (skipPruning) return dtree; - if (folds == 0) - { - folds = dataset.n_cols; + if (folds == dataset.n_cols) Log::Info << "Performing leave-one-out cross validation." << std::endl; - } else - { Log::Info << "Performing " << folds << "-fold cross validation." << std::endl; - } Timer::Start("prunning_sequence"); From dbff1b137743fc3bba1eb11ef58962b853580683 Mon Sep 17 00:00:00 2001 From: theJonan Date: Tue, 7 Feb 2017 19:45:08 +0200 Subject: [PATCH 12/22] - New options fixes. --- src/mlpack/methods/det/det_main.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/mlpack/methods/det/det_main.cpp b/src/mlpack/methods/det/det_main.cpp index 65603bfc43..13fac90246 100644 --- a/src/mlpack/methods/det/det_main.cpp +++ b/src/mlpack/methods/det/det_main.cpp @@ -61,14 +61,14 @@ PARAM_STRING_OUT("vi_file", "The file to output the variable importance values " PARAM_STRING_IN("path_format", "The format of path printing - lr|id-lr|lr-id", "p", "lr"); -PARAM_STRING_OUT("tag_counters", "The file to output tag counters.", "c"); +PARAM_STRING_OUT("tag_counters_file", "The file to output tag counters.", "c"); + +PARAM_STRING_OUT("raw_estimates_file", "The file to output the estimations from " + "the unpruned tree.", "u"); PARAM_STRING_OUT("tag_file", "The file to output the tags (and possibly paths) " " for each sample in the test set.", "g"); -PARAM_STRING_OUT("unpruned_estimates", "The file to output the estimations from " - "the unpruned tree.", "u"); - PARAM_FLAG("skip_pruning", "Whether to bypass the pruning process and output " "the unpruned tree only", "s"); @@ -181,7 +181,7 @@ int main(int argc, char *argv[]) Timer::Start("det_training"); tree = Trainer(trainingData, folds, regularization, maxLeafSize, minLeafSize, - CLI::GetParam("unpruned_estimates"), + CLI::GetParam("raw_estimates_file"), skipPruning); Timer::Stop("det_training"); @@ -285,8 +285,8 @@ int main(int argc, char *argv[]) } } - if (CLI::GetParam("tag_counters") != "") - data::Save(CLI::GetParam("tag_counters"), counters); + if (CLI::GetParam("tag_counters_file") != "") + data::Save(CLI::GetParam("tag_counters_file"), counters); Timer::Stop("det_test_set_tagging"); ofs.close(); From 6dad5abef333aa40a8b1b23cb645420e9d1d4274 Mon Sep 17 00:00:00 2001 From: theJonan Date: Tue, 7 Feb 2017 22:41:59 +0200 Subject: [PATCH 13/22] - More path printing features. --- src/mlpack/methods/det/det_main.cpp | 75 ++++++++++++++++++----------- 1 file changed, 47 insertions(+), 28 deletions(-) diff --git a/src/mlpack/methods/det/det_main.cpp b/src/mlpack/methods/det/det_main.cpp index 13fac90246..0315c8ce7f 100644 --- a/src/mlpack/methods/det/det_main.cpp +++ b/src/mlpack/methods/det/det_main.cpp @@ -101,26 +101,25 @@ public: template PathCacher(PathFormat fmt, DTree* tree); - ~PathCacher(); - template void Enter(const DTree* node, const DTree* parent); template void Leave(const DTree* node, const DTree* parent); - const std::string& PathFor(int tag) const; + const std::string& PathFor(int tag) const; - size_t NumLeaves() const { return numLeaves; } + int ParentOf(int tag) const; -private: - typedef std::list > PathType; + size_t NumNodes() const { return pathCache.size(); } + +protected: + typedef std::list > PathType; + typedef std::vector > PathCacheType; PathType path; PathFormat format; - - int numLeaves; - std::string* pathCache; + PathCacheType pathCache; std::string BuildString(); }; @@ -245,7 +244,9 @@ int main(int argc, char *argv[]) } else if (CLI::HasParam("path_format")) { + const bool reqCounters = CLI::HasParam("tag_counters_file"); const string pathFormat = CLI::GetParam("path_format"); + PathCacher::PathFormat theFormat; if (pathFormat == "lr" || pathFormat == "LR") theFormat = PathCacher::FormatLR; @@ -261,14 +262,30 @@ int main(int argc, char *argv[]) } PathCacher path(theFormat, tree); - counters.zeros(path.NumLeaves()); + counters.zeros(path.NumNodes()); for (size_t i = 0; i < testData.n_cols; i++) { - const int tag = tree->FindBucket(testData.unsafe_col(i)); - - counters(tag) += 1; + int tag = tree->FindBucket(testData.unsafe_col(i)); + ofs << tag << " " << path.PathFor(tag) << std::endl; + for (;tag >= 0 && reqCounters; tag = path.ParentOf(tag)) + counters(tag) += 1; + } + + ofs.close(); + + if (reqCounters) + { + ofs.open(CLI::GetParam("tag_counters_file"), + std::ofstream::out); + + for (size_t j = 0;j < counters.n_elem; ++j) + ofs << j << " " + << counters(j) << " " + << path.PathFor(j) << endl; + + ofs.close(); } } else @@ -283,10 +300,11 @@ int main(int argc, char *argv[]) ofs << tag << std::endl; counters(tag) += 1; } + + if (CLI::HasParam("tag_counters_file")) + data::Save(CLI::GetParam("tag_counters_file"), counters); } - if (CLI::GetParam("tag_counters_file") != "") - data::Save(CLI::GetParam("tag_counters_file"), counters); Timer::Stop("det_test_set_tagging"); ofs.close(); @@ -309,19 +327,14 @@ int main(int argc, char *argv[]) template PathCacher::PathCacher(PathCacher::PathFormat fmt, DTree* dtree) : format(fmt) { - numLeaves = dtree->TagTree(0, true); - pathCache = new std::string [numLeaves]; - assert(!!pathCache); + pathCache.resize(dtree->TagTree(0, true)); + pathCache[0] = PathCacheType::value_type(-1, ""); dtree->EnumerateTree(*this); } -PathCacher::~PathCacher() -{ - delete[] pathCache; -} - template -void PathCacher::Enter(const DTree* node, const DTree* parent) +void PathCacher::Enter(const DTree* node, + const DTree* parent) { if (parent == nullptr) return; @@ -329,8 +342,10 @@ void PathCacher::Enter(const DTree* node, const DTreeBucketTag(); path.push_back(PathType::value_type(parent->Left() == node, tag)); - if (tag >= 0) - pathCache[tag] = BuildString(); + pathCache[tag] = PathCacheType::value_type(parent->BucketTag(), + (node->SubtreeLeaves() > 1) ? + "" : BuildString() + ); } template @@ -362,8 +377,12 @@ std::string PathCacher::BuildString() return str; } +int PathCacher::ParentOf(int tag) const +{ + return pathCache[tag].first; +} + const std::string& PathCacher::PathFor(int tag) const { - assert(tag >= 0 && tag < numLeaves ); - return pathCache[tag]; + return pathCache[tag].second; } From 4412d8ae88b8aff845f61b90f8a17a253c587ff1 Mon Sep 17 00:00:00 2001 From: theJonan Date: Wed, 8 Feb 2017 00:45:51 +0200 Subject: [PATCH 14/22] - After merge fix. --- src/mlpack/methods/det/det_main.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/mlpack/methods/det/det_main.cpp b/src/mlpack/methods/det/det_main.cpp index 249b037661..b9714cfea7 100644 --- a/src/mlpack/methods/det/det_main.cpp +++ b/src/mlpack/methods/det/det_main.cpp @@ -213,8 +213,7 @@ int main(int argc, char *argv[]) if (CLI::HasParam("test")) { arma::mat testData = std::move(CLI::GetParam("test")); - data::Load(testFile, testData, true); - if (CLI::HasParam("test_set_estimates_file")) + if (CLI::HasParam("test_set_estimates")) { // Compute test set densities. Timer::Start("det_test_set_estimation"); @@ -316,7 +315,7 @@ int main(int argc, char *argv[]) { arma::vec importances; tree->ComputeVariableImportance(importances); - CLI::GetParam("vi") = std::move(importances.t()); + CLI::GetParam("vi") = importances.t(); } // Save the model, if desired. From b558187e3163687da78403e48ad57062f69bcb5d Mon Sep 17 00:00:00 2001 From: theJonan Date: Thu, 28 Sep 2017 18:59:07 +0300 Subject: [PATCH 15/22] - After merge clearings. - training / testing set usable for tagging. - PRINT_PARAM_STRING() utilized more. --- src/mlpack/methods/det/det_main.cpp | 224 ++++++++++++----------- src/mlpack/methods/det/dt_utils_impl.hpp | 2 +- src/mlpack/methods/det/dtree_impl.hpp | 1 + 3 files changed, 122 insertions(+), 105 deletions(-) diff --git a/src/mlpack/methods/det/det_main.cpp b/src/mlpack/methods/det/det_main.cpp index e20d1c1a11..a7159cd121 100644 --- a/src/mlpack/methods/det/det_main.cpp +++ b/src/mlpack/methods/det/det_main.cpp @@ -133,46 +133,60 @@ void mlpackMain() { // Validate input parameters. if (CLI::HasParam("training") && CLI::HasParam("input_model")) - Log::Fatal << "Only one of --training_file (-t) or --input_model_file (-m) " - << "may be specified!" << endl; + Log::Fatal << "Only one of " << PRINT_PARAM_STRING("training") << " or " << + PRINT_PARAM_STRING("input_model") << " may be specified!" << endl; if (!CLI::HasParam("training") && !CLI::HasParam("input_model")) - Log::Fatal << "Neither --training_file (-t) nor --input_model_file (-m) " - << "are specified!" << endl; + Log::Fatal << "Neither " << PRINT_PARAM_STRING("training") << " nor " << + PRINT_PARAM_STRING("input_model") << " are specified!" << endl; + + if (CLI::HasParam("tag_file") && + !CLI::HasParam("training") && !CLI::HasParam("test")) + { + Log::Fatal << "Neither " << PRINT_PARAM_STRING("training") << " nor " << + PRINT_PARAM_STRING("test") << " are specified, but needed when " << + PRINT_PARAM_STRING("tag_file") << " is asked." << endl; + } if (!CLI::HasParam("training")) { if (CLI::HasParam("training_set_estimates")) - Log::Warn << "--training_set_estimates_file (-e) ignored because " - << "--training_file (-t) is not specified." << endl; + Log::Warn << PRINT_PARAM_STRING("training_set_estimates") << + " ignored because " << PRINT_PARAM_STRING("training") << + " is not specified." << endl; if (CLI::HasParam("folds")) - Log::Warn << "--folds (-f) ignored because --training_file (-t) is not " - << "specified." << endl; + Log::Warn << PRINT_PARAM_STRING("folds") << " ignored because " << + PRINT_PARAM_STRING("training") << " is not specified." << endl; if (CLI::HasParam("min_leaf_size")) - Log::Warn << "--min_leaf_size (-l) ignored because --training_file (-t) " - << "is not specified." << endl; + Log::Warn << PRINT_PARAM_STRING("min_leaf_size") << " ignored because " << + PRINT_PARAM_STRING("training") << " is not specified." << endl; if (CLI::HasParam("max_leaf_size")) - Log::Warn << "--max_leaf_size (-L) ignored because --training_file (-t) " - << "is not specified." << endl; + Log::Warn << PRINT_PARAM_STRING("max_leaf_size") << " ignored because " << + PRINT_PARAM_STRING("training") << " is not specified." << endl; } else if (!CLI::HasParam("output_model") && !CLI::HasParam("training_set_estimates") && !CLI::HasParam("vi")) { - Log::Warn << "None of --output_model_file (-M), --training_set_estimates " - << "(-e), or --vi (-i) are specified; no output will be saved!" << endl; + Log::Warn << "None of " << PRINT_PARAM_STRING("output_model") << ", " << + PRINT_PARAM_STRING("training_set_estimates") << ", or " << + PRINT_PARAM_STRING("vi") << " are specified; no output will be saved!" << + endl; } if (!CLI::HasParam("test") && CLI::HasParam("test_set_estimates")) - Log::Warn << "--test_set_estimates_file (-E) ignored because --test_file " - << "(-T) is not specified." << endl; + Log::Warn << PRINT_PARAM_STRING("test_set_estimates") << " ignored " << + "because " << PRINT_PARAM_STRING("test") << " is not specified." << endl; // Are we training a DET or loading from file? DTree* tree; + arma::mat trainingData; + arma::mat testData; + if (CLI::HasParam("training")) { - arma::mat trainingData = std::move(CLI::GetParam("training")); - + trainingData = std::move(CLI::GetParam("training")); + const bool regularization = false; // const bool regularization = CLI::HasParam("volume_regularization"); const int maxLeafSize = CLI::GetParam("max_leaf_size"); @@ -214,7 +228,7 @@ void mlpackMain() // the given file. if (CLI::HasParam("test")) { - arma::mat testData = std::move(CLI::GetParam("test")); + testData = std::move(CLI::GetParam("test")); if (CLI::HasParam("test_set_estimates")) { // Compute test set densities. @@ -229,95 +243,97 @@ void mlpackMain() CLI::GetParam("test_set_estimates") = std::move(testDensities); } - if (CLI::HasParam("tag_file")) + // Print variable importance. + if (CLI::HasParam("vi")) { - const string tagFile = CLI::GetParam("tag_file"); - std::ofstream ofs; - ofs.open(tagFile, std::ofstream::out); - - arma::Row counters; - - Timer::Start("det_test_set_tagging"); - if (!ofs.is_open()) - { - Log::Warn << "Unable to open file '" << tagFile - << "' to save tag membership info." - << std::endl; - } - else if (CLI::HasParam("path_format")) - { - const bool reqCounters = CLI::HasParam("tag_counters_file"); - const string pathFormat = CLI::GetParam("path_format"); - - PathCacher::PathFormat theFormat; - if (pathFormat == "lr" || pathFormat == "LR") - theFormat = PathCacher::FormatLR; - else if (pathFormat == "lr-id" || pathFormat == "LR-ID") - theFormat = PathCacher::FormatLR_ID; - else if (pathFormat == "id-lr" || pathFormat == "ID-LR") - theFormat = PathCacher::FormatID_LR; - else - { - Log::Warn << "Unknown path format specified: '" << pathFormat - << "'. Valid are: lr | lr-id | id-lr. Defaults to 'lr'" << endl; - theFormat = PathCacher::FormatLR; - } - - PathCacher path(theFormat, tree); - counters.zeros(path.NumNodes()); - - for (size_t i = 0; i < testData.n_cols; i++) - { - int tag = tree->FindBucket(testData.unsafe_col(i)); - - ofs << tag << " " << path.PathFor(tag) << std::endl; - for (;tag >= 0 && reqCounters; tag = path.ParentOf(tag)) - counters(tag) += 1; - } - - ofs.close(); - - if (reqCounters) - { - ofs.open(CLI::GetParam("tag_counters_file"), - std::ofstream::out); - - for (size_t j = 0;j < counters.n_elem; ++j) - ofs << j << " " - << counters(j) << " " - << path.PathFor(j) << endl; - - ofs.close(); - } - } - else - { - int numLeaves = tree->TagTree(); - counters.zeros(numLeaves); - - for (size_t i = 0; i < testData.n_cols; i++) - { - const int tag = tree->FindBucket(testData.unsafe_col(i)); - - ofs << tag << std::endl; - counters(tag) += 1; - } - - if (CLI::HasParam("tag_counters_file")) - data::Save(CLI::GetParam("tag_counters_file"), counters); - } - - Timer::Stop("det_test_set_tagging"); - ofs.close(); + arma::vec importances; + tree->ComputeVariableImportance(importances); + CLI::GetParam("vi") = importances.t(); } } - - // Print variable importance. - if (CLI::HasParam("vi")) + + if (CLI::HasParam("tag_file")) { - arma::vec importances; - tree->ComputeVariableImportance(importances); - CLI::GetParam("vi") = importances.t(); + const arma::mat& estimationData = + CLI::HasParam("test") ? testData : trainingData; + const string tagFile = CLI::GetParam("tag_file"); + std::ofstream ofs; + ofs.open(tagFile, std::ofstream::out); + + arma::Row counters; + + Timer::Start("det_test_set_tagging"); + if (!ofs.is_open()) + { + Log::Warn << "Unable to open file '" << tagFile + << "' to save tag membership info." + << std::endl; + } + else if (CLI::HasParam("path_format")) + { + const bool reqCounters = CLI::HasParam("tag_counters_file"); + const string pathFormat = CLI::GetParam("path_format"); + + PathCacher::PathFormat theFormat; + if (pathFormat == "lr" || pathFormat == "LR") + theFormat = PathCacher::FormatLR; + else if (pathFormat == "lr-id" || pathFormat == "LR-ID") + theFormat = PathCacher::FormatLR_ID; + else if (pathFormat == "id-lr" || pathFormat == "ID-LR") + theFormat = PathCacher::FormatID_LR; + else + { + Log::Warn << "Unknown path format specified: '" << pathFormat + << "'. Valid are: lr | lr-id | id-lr. Defaults to 'lr'" << endl; + theFormat = PathCacher::FormatLR; + } + + PathCacher path(theFormat, tree); + counters.zeros(path.NumNodes()); + + for (size_t i = 0; i < estimationData.n_cols; i++) + { + int tag = tree->FindBucket(estimationData.unsafe_col(i)); + + ofs << tag << " " << path.PathFor(tag) << std::endl; + for (;tag >= 0 && reqCounters; tag = path.ParentOf(tag)) + counters(tag) += 1; + } + + ofs.close(); + + if (reqCounters) + { + ofs.open(CLI::GetParam("tag_counters_file"), + std::ofstream::out); + + for (size_t j = 0;j < counters.n_elem; ++j) + ofs << j << " " + << counters(j) << " " + << path.PathFor(j) << endl; + + ofs.close(); + } + } + else + { + int numLeaves = tree->TagTree(); + counters.zeros(numLeaves); + + for (size_t i = 0; i < estimationData.n_cols; i++) + { + const int tag = tree->FindBucket(estimationData.unsafe_col(i)); + + ofs << tag << std::endl; + counters(tag) += 1; + } + + if (CLI::HasParam("tag_counters_file")) + data::Save(CLI::GetParam("tag_counters_file"), counters); + } + + Timer::Stop("det_test_set_tagging"); + ofs.close(); } // Save the model, if desired. diff --git a/src/mlpack/methods/det/dt_utils_impl.hpp b/src/mlpack/methods/det/dt_utils_impl.hpp index 64c0503358..dc376d15a6 100644 --- a/src/mlpack/methods/det/dt_utils_impl.hpp +++ b/src/mlpack/methods/det/dt_utils_impl.hpp @@ -172,7 +172,7 @@ DTree* Trainer(MatType& dataset, while (dtree->SubtreeLeaves() > 1) { std::pair treeSeq(oldAlpha, - dtree.SubtreeLeavesLogNegError()); + dtree->SubtreeLeavesLogNegError()); prunedSequence.push_back(treeSeq); oldAlpha = alpha; alpha = dtree->PruneAndUpdate(oldAlpha, dataset.n_cols, useVolumeReg); diff --git a/src/mlpack/methods/det/dtree_impl.hpp b/src/mlpack/methods/det/dtree_impl.hpp index 4a589e6808..4f50999e9a 100644 --- a/src/mlpack/methods/det/dtree_impl.hpp +++ b/src/mlpack/methods/det/dtree_impl.hpp @@ -1044,6 +1044,7 @@ void DTree::Serialize(Archive& ar, ar & CreateNVP(maxVals, "maxVals"); ar & CreateNVP(minVals, "minVals"); + // This is added in order to reduce (dramatically!) the model file size. if (Archive::is_loading::value && left && right) FillMinMax(minVals, maxVals); } From 59c9c2dd71a5ad983e2b5b3512ffa09ed998896b Mon Sep 17 00:00:00 2001 From: theJonan Date: Tue, 3 Oct 2017 10:41:19 +0300 Subject: [PATCH 16/22] [core] Moved EnumerateTree() from DET to core/tree. [det] Style and documentation fixes. --- src/mlpack/core/tree/CMakeLists.txt | 1 + src/mlpack/core/tree/enumerate_tree.hpp | 61 +++++++++ src/mlpack/methods/det/det_main.cpp | 161 +++++------------------ src/mlpack/methods/det/dt_utils.hpp | 45 ++++++- src/mlpack/methods/det/dt_utils_impl.hpp | 86 ++++++++++-- src/mlpack/methods/det/dtree.hpp | 31 +++-- src/mlpack/methods/det/dtree_impl.hpp | 41 +----- 7 files changed, 238 insertions(+), 188 deletions(-) create mode 100644 src/mlpack/core/tree/enumerate_tree.hpp diff --git a/src/mlpack/core/tree/CMakeLists.txt b/src/mlpack/core/tree/CMakeLists.txt index adc5bae0b2..0b2f01a5f3 100644 --- a/src/mlpack/core/tree/CMakeLists.txt +++ b/src/mlpack/core/tree/CMakeLists.txt @@ -120,6 +120,7 @@ set(SOURCES statistic.hpp traversal_info.hpp tree_traits.hpp + enumerate_tree.hpp ) # add directory name to sources diff --git a/src/mlpack/core/tree/enumerate_tree.hpp b/src/mlpack/core/tree/enumerate_tree.hpp new file mode 100644 index 0000000000..7dcf0e258d --- /dev/null +++ b/src/mlpack/core/tree/enumerate_tree.hpp @@ -0,0 +1,61 @@ +/** + * @file enumerate_tree.hpp + * @author Ivan (Jonan) Georgiev + * + * This file contains function that performs a simple depth-first walk on the tree + * calling `Enter` and `Leave` methods of a provided walker. + * + * mlpack is free software; you may redistribute it and/or modify it under the + * terms of the 3-clause BSD license. You should have received a copy of the + * 3-clause BSD license along with mlpack. If not, see + * http://www.opensource.org/licenses/BSD-3-Clause for more information. + */ +#ifndef MLPACK_CORE_TREE_ENUMERATE_TREE_HPP +#define MLPACK_CORE_TREE_ENUMERATE_TREE_HPP + +namespace mlpack { +namespace tree /** Trees and tree-building procedures. */ { +namespace enumerate { + + // Actual implementation of the enumeration. The problem is the unified detection + // if we're on the root, because Enter and Leave expect the parent being passed. + template + void EnumerateTreeImpl(TreeType* tree, Walker& walker, bool root) + { + if (root) + walker.Enter(tree, (const TreeType*)nullptr); + + const size_t numChildren = tree->NumChildren(); + for (size_t i = 0; i < numChildren; ++i) + { + TreeType* child = tree->ChildPtr(i); + walker.Enter(child, tree); + EnumerateTreeImpl(child, walker, false); + walker.Leave(child, tree); + } + + if (root) + walker.Leave(tree, (const TreeType*)nullptr); + } +} // namespace enumerate + +/** + * Traverses all nodes of the tree, including the inner ones. On each node + * two methods of the `enumer` are called: + * + * Enter(DTree* node, DTree* parent); + * Leave(Dtree* node, DTree* parent); + * + * @param walker An instance of custom class, receiver of the enumeration. + */ +template +inline void EnumerateTree(TreeType* tree, Walker& walker) +{ + enumerate::EnumerateTreeImpl(tree, walker, true); +} + +} // namespace tree +} // namespace mlpack + + +#endif // MLPACK_CORE_TREE_ENUMERATE_TREE_HPP diff --git a/src/mlpack/methods/det/det_main.cpp b/src/mlpack/methods/det/det_main.cpp index a7159cd121..06dc3b1afe 100644 --- a/src/mlpack/methods/det/det_main.cpp +++ b/src/mlpack/methods/det/det_main.cpp @@ -33,6 +33,11 @@ PROGRAM_INFO("Density Estimation With Density Estimation Trees", " with the " + PRINT_PARAM_STRING("training_set_estimates") + " output " "parameter." "\n\n" + "Enabling path printing for each node outputs strings like `LRLRLR` for " + "each entry in the test set, or training one, if test is not provided. " + "If `lr-id` or `id-lr` is provided, it'll also print the id (tag) of the " + "node (not just leaf!) in tree along the path to the leaf." + "\n\n" "This program also can provide density estimates for a set of test points, " "specified in the " + PRINT_PARAM_STRING("test") + " parameter. The " "density estimation tree used for this task will be the tree that was " @@ -65,12 +70,13 @@ PARAM_MATRIX_OUT("vi", "The output variable importance values for each " PARAM_STRING_IN("path_format", "The format of path printing - lr|id-lr|lr-id", "p", "lr"); -PARAM_STRING_OUT("tag_counters_file", "The file to output tag counters.", "c"); +PARAM_STRING_OUT("tag_counters_file", "The file to output number of points " + "that went to each leaf.", "c"); -PARAM_STRING_OUT("raw_estimates_file", "The file to output the estimations from " - "the unpruned tree.", "u"); +PARAM_STRING_OUT("raw_estimates_file", "The file to output the estimations from" + " the unpruned tree.", "u"); -PARAM_STRING_OUT("tag_file", "The file to output the tags (and possibly paths) " +PARAM_STRING_OUT("tag_file", "The file to output the tags (and possibly paths)" " for each sample in the test set.", "g"); PARAM_FLAG("skip_pruning", "Whether to bypass the pruning process and output " @@ -92,43 +98,6 @@ PARAM_FLAG("volume_regularization", "This flag gives the used the option to use" */ -class PathCacher -{ -public: - enum PathFormat - { - FormatLR, - FormatLR_ID, - FormatID_LR - }; - - template - PathCacher(PathFormat fmt, DTree* tree); - - template - void Enter(const DTree* node, const DTree* parent); - - template - void Leave(const DTree* node, const DTree* parent); - - const std::string& PathFor(int tag) const; - - int ParentOf(int tag) const; - - size_t NumNodes() const { return pathCache.size(); } - -protected: - typedef std::list > PathType; - typedef std::vector > PathCacheType; - - PathType path; - PathFormat format; - PathCacheType pathCache; - - std::string BuildString(); -}; - - void mlpackMain() { // Validate input parameters. @@ -139,7 +108,7 @@ void mlpackMain() if (!CLI::HasParam("training") && !CLI::HasParam("input_model")) Log::Fatal << "Neither " << PRINT_PARAM_STRING("training") << " nor " << PRINT_PARAM_STRING("input_model") << " are specified!" << endl; - + if (CLI::HasParam("tag_file") && !CLI::HasParam("training") && !CLI::HasParam("test")) { @@ -182,29 +151,29 @@ void mlpackMain() DTree* tree; arma::mat trainingData; arma::mat testData; - + if (CLI::HasParam("training")) { trainingData = std::move(CLI::GetParam("training")); - + const bool regularization = false; // const bool regularization = CLI::HasParam("volume_regularization"); const int maxLeafSize = CLI::GetParam("max_leaf_size"); const int minLeafSize = CLI::GetParam("min_leaf_size"); const bool skipPruning = CLI::HasParam("skip_pruning"); size_t folds = CLI::GetParam("folds"); - + if (folds == 0) folds = trainingData.n_cols; // Obtain the optimal tree. Timer::Start("det_training"); - tree = Trainer(trainingData, folds, regularization, + tree = Trainer(trainingData, folds, regularization, maxLeafSize, minLeafSize, CLI::GetParam("raw_estimates_file"), skipPruning); Timer::Stop("det_training"); - + // Compute training set estimates, if desired. if (CLI::HasParam("training_set_estimates")) { @@ -234,15 +203,15 @@ void mlpackMain() // Compute test set densities. Timer::Start("det_test_set_estimation"); arma::rowvec testDensities(testData.n_cols); - + for (size_t i = 0; i < testData.n_cols; i++) testDensities[i] = tree->ComputeValue(testData.unsafe_col(i)); - + Timer::Stop("det_test_set_estimation"); CLI::GetParam("test_set_estimates") = std::move(testDensities); } - + // Print variable importance. if (CLI::HasParam("vi")) { @@ -251,7 +220,7 @@ void mlpackMain() CLI::GetParam("vi") = importances.t(); } } - + if (CLI::HasParam("tag_file")) { const arma::mat& estimationData = @@ -259,7 +228,7 @@ void mlpackMain() const string tagFile = CLI::GetParam("tag_file"); std::ofstream ofs; ofs.open(tagFile, std::ofstream::out); - + arma::Row counters; Timer::Start("det_test_set_tagging"); @@ -284,34 +253,34 @@ void mlpackMain() else { Log::Warn << "Unknown path format specified: '" << pathFormat - << "'. Valid are: lr | lr-id | id-lr. Defaults to 'lr'" << endl; + << "'. Valid are: lr | lr-id | id-lr. Defaults to 'lr'." << endl; theFormat = PathCacher::FormatLR; } - + PathCacher path(theFormat, tree); counters.zeros(path.NumNodes()); - + for (size_t i = 0; i < estimationData.n_cols; i++) { int tag = tree->FindBucket(estimationData.unsafe_col(i)); ofs << tag << " " << path.PathFor(tag) << std::endl; - for (;tag >= 0 && reqCounters; tag = path.ParentOf(tag)) + for (; tag >= 0 && reqCounters; tag = path.ParentOf(tag)) counters(tag) += 1; } - + ofs.close(); - + if (reqCounters) { ofs.open(CLI::GetParam("tag_counters_file"), std::ofstream::out); - - for (size_t j = 0;j < counters.n_elem; ++j) + + for (size_t j = 0; j < counters.n_elem; ++j) ofs << j << " " << counters(j) << " " << path.PathFor(j) << endl; - + ofs.close(); } } @@ -319,15 +288,15 @@ void mlpackMain() { int numLeaves = tree->TagTree(); counters.zeros(numLeaves); - + for (size_t i = 0; i < estimationData.n_cols; i++) { const int tag = tree->FindBucket(estimationData.unsafe_col(i)); - + ofs << tag << std::endl; counters(tag) += 1; } - + if (CLI::HasParam("tag_counters_file")) data::Save(CLI::GetParam("tag_counters_file"), counters); } @@ -344,67 +313,3 @@ void mlpackMain() if (!CLI::HasParam("input_model") && !CLI::HasParam("output_model")) delete tree; } - - -template -PathCacher::PathCacher(PathCacher::PathFormat fmt, DTree* dtree) : format(fmt) -{ - pathCache.resize(dtree->TagTree(0, true)); - pathCache[0] = PathCacheType::value_type(-1, ""); - dtree->EnumerateTree(*this); -} - -template -void PathCacher::Enter(const DTree* node, - const DTree* parent) -{ - if (parent == nullptr) - return; - - int tag = node->BucketTag(); - - path.push_back(PathType::value_type(parent->Left() == node, tag)); - pathCache[tag] = PathCacheType::value_type(parent->BucketTag(), - (node->SubtreeLeaves() > 1) ? - "" : BuildString() - ); -} - -template -void PathCacher::Leave(const DTree* , const DTree* parent) -{ - if (parent != nullptr) - path.pop_back(); -} - -std::string PathCacher::BuildString() -{ - std::string str(""); - for (PathType::iterator it = path.begin(); it != path.end(); it++) - { - switch (format) - { - case FormatLR: - str += it->first ? "L" : "R"; - break; - case FormatLR_ID: - str += (it->first ? "L" : "R") + std::to_string(it->second); - break; - case FormatID_LR: - str += std::to_string(it->second) + (it->first ? "L" : "R"); - break; - } - } - - return str; -} - -int PathCacher::ParentOf(int tag) const -{ - return pathCache[tag].first; -} - -const std::string& PathCacher::PathFor(int tag) const -{ - return pathCache[tag].second; -} diff --git a/src/mlpack/methods/det/dt_utils.hpp b/src/mlpack/methods/det/dt_utils.hpp index 7469b8358b..9fc81c2541 100644 --- a/src/mlpack/methods/det/dt_utils.hpp +++ b/src/mlpack/methods/det/dt_utils.hpp @@ -36,7 +36,7 @@ void PrintLeafMembership(DTree* dtree, const arma::Mat& labels, const size_t numClasses, const std::string& leafClassMembershipFile = ""); - + /** * Print the variable importance of each dimension of a density estimation tree. * Optionally, pass the name of a file to print this information to (otherwise @@ -70,6 +70,49 @@ DTree* Trainer(MatType& dataset, const std::string unprunedTreeOutput = "", const bool skipPruning = false); +/** + * The class responsible for cacheing the path to each node of the tree. Its instance + * is provided to EnumerateTree() utility ONCE and it caches the paths to all the + * leafs and then easily (and quickly) retrieves these paths for each test entry. + */ +class PathCacher +{ +public: + enum PathFormat + { + FormatLR, + FormatLR_ID, + FormatID_LR + }; + + template + PathCacher(PathFormat fmt, DTree* tree); + + template + void Enter(const DTree* node, + const DTree* parent); + + template + void Leave(const DTree* node, + const DTree* parent); + + const std::string& PathFor(int tag) const; + + int ParentOf(int tag) const; + + size_t NumNodes() const { return pathCache.size(); } + +protected: + typedef std::list > PathType; + typedef std::vector > PathCacheType; + + PathType path; + PathFormat format; + PathCacheType pathCache; + + std::string BuildString(); +}; + } // namespace det } // namespace mlpack diff --git a/src/mlpack/methods/det/dt_utils_impl.hpp b/src/mlpack/methods/det/dt_utils_impl.hpp index dc376d15a6..dcd69e9f23 100644 --- a/src/mlpack/methods/det/dt_utils_impl.hpp +++ b/src/mlpack/methods/det/dt_utils_impl.hpp @@ -14,6 +14,7 @@ #define MLPACK_METHODS_DET_DT_UTILS_IMPL_HPP #include "dt_utils.hpp" +#include namespace mlpack { namespace det { @@ -65,7 +66,7 @@ void PrintLeafMembership(DTree* dtree, return; } - + template void PrintVariableImportance(const DTree* dtree, const std::string viFile) @@ -156,17 +157,18 @@ DTree* Trainer(MatType& dataset, outfile.close(); } - + if (skipPruning) return dtree; if (folds == dataset.n_cols) Log::Info << "Performing leave-one-out cross validation." << std::endl; else - Log::Info << "Performing " << folds << "-fold cross validation." << std::endl; - - Timer::Start("prunning_sequence"); - + Log::Info << "Performing " << folds << "-fold cross validation." << + std::endl; + + Timer::Start("pruning_sequence"); + // Sequentially prune and save the alpha values and the values of c_t^2 * r_t. std::vector > prunedSequence; while (dtree->SubtreeLeaves() > 1) @@ -186,10 +188,11 @@ DTree* Trainer(MatType& dataset, Log::Assert(dtree->SubtreeLeavesLogNegError() <= treeSeq.second); } - std::pair treeSeq(oldAlpha, dtree->SubtreeLeavesLogNegError()); + std::pair treeSeq(oldAlpha, + dtree->SubtreeLeavesLogNegError()); prunedSequence.push_back(treeSeq); - Timer::Stop("prunning_sequence"); + Timer::Stop("pruning_sequence"); Log::Info << prunedSequence.size() << " trees in the sequence; maximum alpha:" << " " << oldAlpha << "." << std::endl; @@ -205,7 +208,7 @@ DTree* Trainer(MatType& dataset, // implementation. omp_size_t is the appropriate type according to the // platform. #pragma omp parallel for default(none) \ - shared(cvData, prunedSequence, regularizationConstants) + shared(prunedSequence, regularizationConstants) for (omp_size_t fold = 0; fold < (omp_size_t) folds; fold++) { // Break up data into train and test sets. @@ -340,6 +343,71 @@ DTree* Trainer(MatType& dataset, return dtree; } +template +PathCacher::PathCacher(PathCacher::PathFormat fmt, DTree* dtree) : + format(fmt) +{ + // Here we use TagTree()'s output to determine the number of _nodes_ in the tree). + pathCache.resize(dtree->TagTree(0, true)); + pathCache[0] = PathCacheType::value_type(-1, ""); + tree::EnumerateTree(dtree, *this); +} + +template +void PathCacher::Enter(const DTree* node, + const DTree* parent) +{ + if (parent == nullptr) + return; + + int tag = node->BucketTag(); + + path.push_back(PathType::value_type(parent->Left() == node, tag)); + pathCache[tag] = PathCacheType::value_type(parent->BucketTag(), + (node->SubtreeLeaves() > 1) ? + "" : BuildString()); +} + +template +void PathCacher::Leave(const DTree* , + const DTree* parent) +{ + if (parent != nullptr) + path.pop_back(); +} + +std::string PathCacher::BuildString() +{ + std::string str(""); + for (PathType::iterator it = path.begin(); it != path.end(); it++) + { + switch (format) + { + case FormatLR: + str += it->first ? "L" : "R"; + break; + case FormatLR_ID: + str += (it->first ? "L" : "R") + std::to_string(it->second); + break; + case FormatID_LR: + str += std::to_string(it->second) + (it->first ? "L" : "R"); + break; + } + } + + return str; +} + +int PathCacher::ParentOf(int tag) const +{ + return pathCache[tag].first; +} + +const std::string& PathCacher::PathFor(int tag) const +{ + return pathCache[tag].second; +} + } // namespace det } // namespace mlpack diff --git a/src/mlpack/methods/det/dtree.hpp b/src/mlpack/methods/det/dtree.hpp index ba9196cb89..a167aad6df 100644 --- a/src/mlpack/methods/det/dtree.hpp +++ b/src/mlpack/methods/det/dtree.hpp @@ -189,20 +189,7 @@ class DTree * @param tag Tag for the next leaf; leave at 0 for the initial call. */ TagType TagTree(const TagType& tag = 0, bool internal = false); - - - /** - * Traverses all nodes of the tree, including the inner ones. On each node - * two methods of the `enumer` are called: - * - * Enter(DTree* node, DTree* parent); - * Leave(Dtree* node, DTree* parent); - * - * @param walker An instance of custom class, receiver of the enumeration. - */ - template - void EnumerateTree(Walker& walker) const; - + /** * Return the tag of the leaf containing the query. This is useful for @@ -212,6 +199,7 @@ class DTree */ TagType FindBucket(const VecType& query) const; + /** * Compute the variable importance of each dimension in the learned tree. * @@ -316,6 +304,18 @@ class DTree double AlphaUpper() const { return alphaUpper; } //! Return the current bucket's ID, if leaf, or -1 otherwise TagType BucketTag() const { return bucketTag; } + //! Return the number of children in this node. + size_t NumChildren() const { return !left ? 0 : 2; } + + /** + * Return the specified child (0 will be left, 1 will be right). If the index + * is greater than 1, this will return the right child. + * + * @param child Index of child to return. + */ + DTree& Child(const size_t child) const { return !child ? *left : *right; } + + DTree*& ChildPtr(const size_t child) { return (!child) ? left : right; } //! Return the maximum values. const StatType& MaxVals() const { return maxVals; } @@ -349,10 +349,9 @@ class DTree const size_t splitDim, const ElemType splitValue, arma::Col& oldFromNew) const; - + void FillMinMax(const StatType& mins, const StatType& maxs); - }; } // namespace det diff --git a/src/mlpack/methods/det/dtree_impl.hpp b/src/mlpack/methods/det/dtree_impl.hpp index 4f50999e9a..c5afc9c142 100644 --- a/src/mlpack/methods/det/dtree_impl.hpp +++ b/src/mlpack/methods/det/dtree_impl.hpp @@ -67,7 +67,7 @@ namespace details { typedef std::pair SplitItem; arma::rowvec dimVec = data(dim, arma::span(start, end - 1)); - + // We sort these, in-place (it's a copy of the data, anyways). std::sort(dimVec.begin(), dimVec.end()); @@ -891,7 +891,7 @@ TagType DTree::TagTree(const TagType& tag, bool internal) bucketTag = tag; return (tag + 1); } - + TagType nextTag; if (internal) { @@ -900,36 +900,10 @@ TagType DTree::TagTree(const TagType& tag, bool internal) } else nextTag = tag; - + return right->TagTree(left->TagTree(nextTag, internal), internal); } -// Enumerate the nodes of the tree. -template -template -void DTree::EnumerateTree(Walker& walker) const -{ - if (root == 1) - walker.Enter(this, (const DTree*)nullptr); - - if (subtreeLeaves > 1) - { - // walk the left ... - walker.Enter(left, this); - left->EnumerateTree(walker); - walker.Leave(left, this); - - // ... and the right. - walker.Enter(right, this); - right->EnumerateTree(walker); - walker.Leave(right, this); - } - - if (root == 1) - walker.Leave(this, (const DTree*)nullptr); - -} - template TagType DTree::FindBucket(const VecType& query) const { @@ -941,7 +915,7 @@ TagType DTree::FindBucket(const VecType& query) const if (!WithinRange(query)) return -1; } - + // If we are a leaf... if (subtreeLeaves == 1) { @@ -994,14 +968,14 @@ void DTree::FillMinMax(const StatType& mins, minVals = mins; maxVals = maxs; } - + if (left && right) { StatType maxValsL(maxs); StatType maxValsR(maxs); StatType minValsL(mins); StatType minValsR(mins); - + maxValsL[splitDim] = minValsR[splitDim] = splitValue; left->FillMinMax(minValsL, maxValsL); right->FillMinMax(minValsR, maxValsR); @@ -1038,7 +1012,7 @@ void DTree::Serialize(Archive& ar, ar & CreateNVP(left, "left"); ar & CreateNVP(right, "right"); - + if (root) { ar & CreateNVP(maxVals, "maxVals"); @@ -1049,4 +1023,3 @@ void DTree::Serialize(Archive& ar, FillMinMax(minVals, maxVals); } } - From 191c375a8d733ab68c7ca68bb52386d63eb9bfb9 Mon Sep 17 00:00:00 2001 From: theJonan Date: Tue, 3 Oct 2017 14:10:20 +0300 Subject: [PATCH 17/22] CI fixes. --- src/mlpack/core/tree/enumerate_tree.hpp | 38 +++++++++++++----------- src/mlpack/methods/det/dt_utils.hpp | 4 +-- src/mlpack/methods/det/dt_utils_impl.hpp | 5 ++-- 3 files changed, 25 insertions(+), 22 deletions(-) diff --git a/src/mlpack/core/tree/enumerate_tree.hpp b/src/mlpack/core/tree/enumerate_tree.hpp index 7dcf0e258d..734f48df2d 100644 --- a/src/mlpack/core/tree/enumerate_tree.hpp +++ b/src/mlpack/core/tree/enumerate_tree.hpp @@ -17,26 +17,28 @@ namespace mlpack { namespace tree /** Trees and tree-building procedures. */ { namespace enumerate { - // Actual implementation of the enumeration. The problem is the unified detection - // if we're on the root, because Enter and Leave expect the parent being passed. - template - void EnumerateTreeImpl(TreeType* tree, Walker& walker, bool root) +// Actual implementation of the enumeration. The problem is the unified +// detection if we're on the root, because Enter and Leave expect the +// parent being passed. +template +void EnumerateTreeImpl(TreeType* tree, Walker& walker, bool root) +{ + if (root) + walker.Enter(tree, (const TreeType*)nullptr); + + const size_t numChildren = tree->NumChildren(); + for (size_t i = 0; i < numChildren; ++i) { - if (root) - walker.Enter(tree, (const TreeType*)nullptr); - - const size_t numChildren = tree->NumChildren(); - for (size_t i = 0; i < numChildren; ++i) - { - TreeType* child = tree->ChildPtr(i); - walker.Enter(child, tree); - EnumerateTreeImpl(child, walker, false); - walker.Leave(child, tree); - } - - if (root) - walker.Leave(tree, (const TreeType*)nullptr); + TreeType* child = tree->ChildPtr(i); + walker.Enter(child, tree); + EnumerateTreeImpl(child, walker, false); + walker.Leave(child, tree); } + + if (root) + walker.Leave(tree, (const TreeType*)nullptr); +} + } // namespace enumerate /** diff --git a/src/mlpack/methods/det/dt_utils.hpp b/src/mlpack/methods/det/dt_utils.hpp index 9fc81c2541..8e49597936 100644 --- a/src/mlpack/methods/det/dt_utils.hpp +++ b/src/mlpack/methods/det/dt_utils.hpp @@ -77,7 +77,7 @@ DTree* Trainer(MatType& dataset, */ class PathCacher { -public: + public: enum PathFormat { FormatLR, @@ -102,7 +102,7 @@ public: size_t NumNodes() const { return pathCache.size(); } -protected: + protected: typedef std::list > PathType; typedef std::vector > PathCacheType; diff --git a/src/mlpack/methods/det/dt_utils_impl.hpp b/src/mlpack/methods/det/dt_utils_impl.hpp index dcd69e9f23..b453e8d6df 100644 --- a/src/mlpack/methods/det/dt_utils_impl.hpp +++ b/src/mlpack/methods/det/dt_utils_impl.hpp @@ -347,7 +347,8 @@ template PathCacher::PathCacher(PathCacher::PathFormat fmt, DTree* dtree) : format(fmt) { - // Here we use TagTree()'s output to determine the number of _nodes_ in the tree). + // Here we use TagTree()'s output to determine the + // number of _nodes_ in the tree. pathCache.resize(dtree->TagTree(0, true)); pathCache[0] = PathCacheType::value_type(-1, ""); tree::EnumerateTree(dtree, *this); @@ -411,4 +412,4 @@ const std::string& PathCacher::PathFor(int tag) const } // namespace det } // namespace mlpack -#endif +#endif // MLPACK_METHODS_DET_DT_UTILS_IMPL_HPP From 81e5bf683b6a23be8c571278b05d4fcb15535b20 Mon Sep 17 00:00:00 2001 From: theJonan Date: Tue, 3 Oct 2017 15:36:12 +0300 Subject: [PATCH 18/22] - CI restart commit. --- src/mlpack/core/tree/enumerate_tree.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/mlpack/core/tree/enumerate_tree.hpp b/src/mlpack/core/tree/enumerate_tree.hpp index 734f48df2d..a115a7a5e5 100644 --- a/src/mlpack/core/tree/enumerate_tree.hpp +++ b/src/mlpack/core/tree/enumerate_tree.hpp @@ -41,6 +41,7 @@ void EnumerateTreeImpl(TreeType* tree, Walker& walker, bool root) } // namespace enumerate + /** * Traverses all nodes of the tree, including the inner ones. On each node * two methods of the `enumer` are called: From 5294299ee01f630b44ff1625065ec745a1cc79da Mon Sep 17 00:00:00 2001 From: theJonan Date: Tue, 3 Oct 2017 17:04:15 +0300 Subject: [PATCH 19/22] [det] Static analyzer fixes. --- src/mlpack/methods/det/dtree_impl.hpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/mlpack/methods/det/dtree_impl.hpp b/src/mlpack/methods/det/dtree_impl.hpp index c5afc9c142..97fb24c607 100644 --- a/src/mlpack/methods/det/dtree_impl.hpp +++ b/src/mlpack/methods/det/dtree_impl.hpp @@ -191,6 +191,9 @@ template DTree& DTree::operator=( const DTree& obj) { + if (this == &obj) + return *this; + // Copy the values from the other tree. start = obj.start; end = obj.end; @@ -213,7 +216,7 @@ DTree& DTree::operator=( // Copy the children. left = ((obj.left == NULL) ? NULL : new DTree(*obj.left)); - left = ((obj.right == NULL) ? NULL : new DTree(*obj.right)); + right = ((obj.right == NULL) ? NULL : new DTree(*obj.right)); return *this; } @@ -258,6 +261,9 @@ template DTree& DTree::operator=( DTree&& obj) { + if (this == &obj) + return *this; + // Move the values from the other tree. start = obj.start; end = obj.end; From 1d7e7940804426e6acddeea2d6f6cc5333e1bb62 Mon Sep 17 00:00:00 2001 From: theJonan Date: Wed, 4 Oct 2017 10:10:20 +0300 Subject: [PATCH 20/22] [det] raw_estimates removed. [det] Style cleanups. --- src/mlpack/core/tree/enumerate_tree.hpp | 4 ++-- src/mlpack/methods/det/det_main.cpp | 10 +++------- src/mlpack/methods/det/dt_utils_impl.hpp | 23 ----------------------- 3 files changed, 5 insertions(+), 32 deletions(-) diff --git a/src/mlpack/core/tree/enumerate_tree.hpp b/src/mlpack/core/tree/enumerate_tree.hpp index a115a7a5e5..8811c8f4c2 100644 --- a/src/mlpack/core/tree/enumerate_tree.hpp +++ b/src/mlpack/core/tree/enumerate_tree.hpp @@ -46,8 +46,8 @@ void EnumerateTreeImpl(TreeType* tree, Walker& walker, bool root) * Traverses all nodes of the tree, including the inner ones. On each node * two methods of the `enumer` are called: * - * Enter(DTree* node, DTree* parent); - * Leave(Dtree* node, DTree* parent); + * Enter(TreeType* node, TreeType* parent); + * Leave(TreeType* node, TreeType* parent); * * @param walker An instance of custom class, receiver of the enumeration. */ diff --git a/src/mlpack/methods/det/det_main.cpp b/src/mlpack/methods/det/det_main.cpp index 06dc3b1afe..cc55fccc34 100644 --- a/src/mlpack/methods/det/det_main.cpp +++ b/src/mlpack/methods/det/det_main.cpp @@ -73,14 +73,11 @@ PARAM_STRING_IN("path_format", "The format of path printing - lr|id-lr|lr-id", PARAM_STRING_OUT("tag_counters_file", "The file to output number of points " "that went to each leaf.", "c"); -PARAM_STRING_OUT("raw_estimates_file", "The file to output the estimations from" - " the unpruned tree.", "u"); - PARAM_STRING_OUT("tag_file", "The file to output the tags (and possibly paths)" " for each sample in the test set.", "g"); PARAM_FLAG("skip_pruning", "Whether to bypass the pruning process and output " - "the unpruned tree only", "s"); + "the unpruned tree only.", "s"); // Parameters for the training algorithm. PARAM_INT_IN("folds", "The number of folds of cross-validation to perform for " @@ -164,13 +161,12 @@ void mlpackMain() size_t folds = CLI::GetParam("folds"); if (folds == 0) - folds = trainingData.n_cols; + folds = trainingData.n_cols; // Obtain the optimal tree. Timer::Start("det_training"); tree = Trainer(trainingData, folds, regularization, maxLeafSize, minLeafSize, - CLI::GetParam("raw_estimates_file"), skipPruning); Timer::Stop("det_training"); @@ -224,7 +220,7 @@ void mlpackMain() if (CLI::HasParam("tag_file")) { const arma::mat& estimationData = - CLI::HasParam("test") ? testData : trainingData; + CLI::HasParam("test") ? testData : trainingData; const string tagFile = CLI::GetParam("tag_file"); std::ofstream ofs; ofs.open(tagFile, std::ofstream::out); diff --git a/src/mlpack/methods/det/dt_utils_impl.hpp b/src/mlpack/methods/det/dt_utils_impl.hpp index b453e8d6df..16aa42e427 100644 --- a/src/mlpack/methods/det/dt_utils_impl.hpp +++ b/src/mlpack/methods/det/dt_utils_impl.hpp @@ -112,7 +112,6 @@ DTree* Trainer(MatType& dataset, const bool useVolumeReg, const size_t maxLeafSize, const size_t minLeafSize, - const std::string unprunedTreeOutput, const bool skipPruning) { // Initialize the tree. @@ -136,28 +135,6 @@ DTree* Trainer(MatType& dataset, Log::Info << dtree->SubtreeLeaves() << " leaf nodes in the tree using full " << "dataset; minimum alpha: " << alpha << "." << std::endl; - // Compute densities for the training points in the full tree, if we were - // asked for this. - if (unprunedTreeOutput != "") - { - std::ofstream outfile(unprunedTreeOutput.c_str()); - if (outfile.good()) - { - for (size_t i = 0; i < dataset.n_cols; ++i) - { - arma::vec testPoint = dataset.unsafe_col(i); - outfile << dtree->ComputeValue(testPoint) << std::endl; - } - } - else - { - Log::Warn << "Can't open '" << unprunedTreeOutput << "' to write computed" - << " densities to." << std::endl; - } - - outfile.close(); - } - if (skipPruning) return dtree; From 6cacb8fbaba44035b55db0d43dae2422bede1b03 Mon Sep 17 00:00:00 2001 From: theJonan Date: Fri, 6 Oct 2017 09:43:23 +0300 Subject: [PATCH 21/22] [det] Minor fix on dtree tagging. --- src/mlpack/methods/det/dtree.hpp | 6 ++++-- src/mlpack/methods/det/dtree_impl.hpp | 10 +++++----- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/mlpack/methods/det/dtree.hpp b/src/mlpack/methods/det/dtree.hpp index a167aad6df..7ebcb4aea5 100644 --- a/src/mlpack/methods/det/dtree.hpp +++ b/src/mlpack/methods/det/dtree.hpp @@ -184,11 +184,13 @@ class DTree /** * Index the buckets for possible usage later; this results in every leaf in * the tree having a specific tag (accessible with BucketTag()). This - * function calls itself recursively. + * function calls itself recursively. The tag is incremented with + * `operator++()`, so any `TagType` overriding it will do. * * @param tag Tag for the next leaf; leave at 0 for the initial call. + * @param everyNodde Whether to increment on every node, not just leaves. */ - TagType TagTree(const TagType& tag = 0, bool internal = false); + TagType TagTree(const TagType& tag = 0, bool everyNode = false); /** diff --git a/src/mlpack/methods/det/dtree_impl.hpp b/src/mlpack/methods/det/dtree_impl.hpp index 97fb24c607..cb499dcef7 100644 --- a/src/mlpack/methods/det/dtree_impl.hpp +++ b/src/mlpack/methods/det/dtree_impl.hpp @@ -889,25 +889,25 @@ double DTree::ComputeValue(const VecType& query) const // Index the buckets for possible usage later. template -TagType DTree::TagTree(const TagType& tag, bool internal) +TagType DTree::TagTree(const TagType& tag, bool every) { if (subtreeLeaves == 1) { // Only label leaves. bucketTag = tag; - return (tag + 1); + return ++tag; } TagType nextTag; - if (internal) + if (every) { bucketTag = tag; - nextTag = tag + 1; + nextTag = ++tag; } else nextTag = tag; - return right->TagTree(left->TagTree(nextTag, internal), internal); + return right->TagTree(left->TagTree(nextTag, every), every); } template From bfb550e4c8a8254337bb1581905b7d3091798969 Mon Sep 17 00:00:00 2001 From: theJonan Date: Fri, 6 Oct 2017 17:13:04 +0300 Subject: [PATCH 22/22] [det] Stupid ++tag fix. --- src/mlpack/methods/det/dtree_impl.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mlpack/methods/det/dtree_impl.hpp b/src/mlpack/methods/det/dtree_impl.hpp index cb499dcef7..686b2804e4 100644 --- a/src/mlpack/methods/det/dtree_impl.hpp +++ b/src/mlpack/methods/det/dtree_impl.hpp @@ -895,14 +895,14 @@ TagType DTree::TagTree(const TagType& tag, bool every) { // Only label leaves. bucketTag = tag; - return ++tag; + return (tag + 1); } TagType nextTag; if (every) { bucketTag = tag; - nextTag = ++tag; + nextTag = (tag + 1); } else nextTag = tag;