Merge pull request #1126 from thejonan/feature/det_path_printing

DET path printing
This commit is contained in:
Ryan Curtin
2017-10-09 10:28:10 -04:00
committed by GitHub
7 changed files with 458 additions and 104 deletions
+1
View File
@@ -120,6 +120,7 @@ set(SOURCES
statistic.hpp
traversal_info.hpp
tree_traits.hpp
enumerate_tree.hpp
)
# add directory name to sources
+64
View File
@@ -0,0 +1,64 @@
/**
* @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 <class TreeType, class Walker>
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(TreeType* node, TreeType* parent);
* Leave(TreeType* node, TreeType* parent);
*
* @param walker An instance of custom class, receiver of the enumeration.
*/
template <class TreeType, class Walker>
inline void EnumerateTree(TreeType* tree, Walker& walker)
{
enumerate::EnumerateTreeImpl(tree, walker, true);
}
} // namespace tree
} // namespace mlpack
#endif // MLPACK_CORE_TREE_ENUMERATE_TREE_HPP
+158 -44
View File
@@ -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 "
@@ -61,6 +66,19 @@ PARAM_MATRIX_OUT("test_set_estimates", "The output estimates on the test set "
PARAM_MATRIX_OUT("vi", "The output variable importance values for each "
"feature.", "i");
// Tagging and path printing options
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 number of points "
"that went to each leaf.", "c");
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");
// 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);
@@ -76,71 +94,80 @@ PARAM_FLAG("volume_regularization", "This flag gives the used the option to use"
"penalize low volume leaves.", "R");
*/
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<arma::mat, int>* tree;
arma::mat trainingData;
arma::mat testData;
if (CLI::HasParam("training"))
{
arma::mat trainingData = std::move(CLI::GetParam<arma::mat>("training"));
// Cross-validation here.
size_t folds = CLI::GetParam<int>("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;
}
trainingData = std::move(CLI::GetParam<arma::mat>("training"));
const bool regularization = false;
// const bool regularization = CLI::HasParam("volume_regularization");
const int maxLeafSize = CLI::GetParam<int>("max_leaf_size");
const int minLeafSize = CLI::GetParam<int>("min_leaf_size");
const bool skipPruning = CLI::HasParam("skip_pruning");
size_t folds = CLI::GetParam<int>("folds");
if (folds == 0)
folds = trainingData.n_cols;
// Obtain the optimal tree.
Timer::Start("det_training");
tree = Trainer<arma::mat, int>(trainingData, folds, regularization,
maxLeafSize, minLeafSize, "");
maxLeafSize, minLeafSize,
skipPruning);
Timer::Stop("det_training");
// Compute training set estimates, if desired.
@@ -166,25 +193,112 @@ void mlpackMain()
// the given file.
if (CLI::HasParam("test"))
{
arma::mat testData = std::move(CLI::GetParam<arma::mat>("test"));
// 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");
testData = std::move(CLI::GetParam<arma::mat>("test"));
if (CLI::HasParam("test_set_estimates"))
{
// 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<arma::mat>("test_set_estimates") = std::move(testDensities);
}
// Print variable importance.
if (CLI::HasParam("vi"))
{
arma::vec importances;
tree->ComputeVariableImportance(importances);
CLI::GetParam<arma::mat>("vi") = importances.t();
}
}
// Print variable importance.
if (CLI::HasParam("vi"))
if (CLI::HasParam("tag_file"))
{
arma::vec importances;
tree->ComputeVariableImportance(importances);
CLI::GetParam<arma::mat>("vi") = std::move(importances.t());
const arma::mat& estimationData =
CLI::HasParam("test") ? testData : trainingData;
const string tagFile = CLI::GetParam<string>("tag_file");
std::ofstream ofs;
ofs.open(tagFile, std::ofstream::out);
arma::Row<size_t> 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<string>("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<string>("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<string>("tag_counters_file"), counters);
}
Timer::Stop("det_test_set_tagging");
ofs.close();
}
// Save the model, if desired.
+46 -2
View File
@@ -35,7 +35,7 @@ void PrintLeafMembership(DTree<MatType, TagType>* dtree,
const MatType& data,
const arma::Mat<size_t>& 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.
@@ -67,7 +67,51 @@ DTree<MatType, TagType>* 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);
/**
* 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 <typename MatType>
PathCacher(PathFormat fmt, DTree<MatType, int>* tree);
template <typename MatType>
void Enter(const DTree<MatType, int>* node,
const DTree<MatType, int>* parent);
template <typename MatType>
void Leave(const DTree<MatType, int>* node,
const DTree<MatType, int>* parent);
const std::string& PathFor(int tag) const;
int ParentOf(int tag) const;
size_t NumNodes() const { return pathCache.size(); }
protected:
typedef std::list<std::pair<bool, int> > PathType;
typedef std::vector<std::pair<int, std::string> > PathCacheType;
PathType path;
PathFormat format;
PathCacheType pathCache;
std::string BuildString();
};
} // namespace det
} // namespace mlpack
+107 -46
View File
@@ -14,19 +14,20 @@
#define MLPACK_METHODS_DET_DT_UTILS_IMPL_HPP
#include "dt_utils.hpp"
#include <mlpack/core/tree/enumerate_tree.hpp>
namespace mlpack {
namespace det {
template <typename MatType, typename TagType>
void PrintLeafMembership(DTree<MatType, TagType>* dtree,
template <typename MatType>
void PrintLeafMembership(DTree<MatType, int>* dtree,
const MatType& data,
const arma::Mat<size_t>& 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<size_t> table(numLeaves, (numClasses + 1));
table.zeros();
@@ -34,7 +35,7 @@ void PrintLeafMembership(DTree<MatType, TagType>* 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;
}
@@ -111,11 +112,12 @@ DTree<MatType, TagType>* Trainer(MatType& dataset,
const bool useVolumeReg,
const size_t maxLeafSize,
const size_t minLeafSize,
const std::string unprunedTreeOutput)
const bool skipPruning)
{
// Initialize the tree.
DTree<MatType, TagType> dtree(dataset);
DTree<MatType, TagType>* dtree = new DTree<MatType, TagType>(dataset);
Timer::Start("tree_growing");
// Prepare to grow the tree...
arma::Col<size_t> oldFromNew(dataset.n_cols);
for (size_t i = 0; i < oldFromNew.n_elem; i++)
@@ -126,60 +128,52 @@ DTree<MatType, TagType>* 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);
Log::Info << dtree.SubtreeLeaves() << " leaf nodes in the tree using full "
Timer::Stop("tree_growing");
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;
}
if (skipPruning)
return dtree;
outfile.close();
}
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("pruning_sequence");
// Sequentially prune and save the alpha values and the values of c_t^2 * r_t.
std::vector<std::pair<double, double> > prunedSequence;
while (dtree.SubtreeLeaves() > 1)
while (dtree->SubtreeLeaves() > 1)
{
std::pair<double, double> treeSeq(oldAlpha,
dtree.SubtreeLeavesLogNegError());
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<double>::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<double, double> treeSeq(oldAlpha, dtree.SubtreeLeavesLogNegError());
std::pair<double, double> treeSeq(oldAlpha,
dtree->SubtreeLeavesLogNegError());
prunedSequence.push_back(treeSeq);
Timer::Stop("pruning_sequence");
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());
@@ -191,7 +185,7 @@ DTree<MatType, TagType>* 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.
@@ -289,8 +283,9 @@ DTree<MatType, TagType>* Trainer(MatType& dataset,
Log::Info << "Optimal alpha: " << optimalAlpha << "." << std::endl;
// Initialize the tree.
DTree<MatType, TagType>* dtreeOpt = new DTree<MatType, TagType>(dataset);
// Re-Initialize the tree.
delete dtree;
dtree = new DTree<MatType, TagType>(dataset);
// Getting ready to grow the tree...
for (size_t i = 0; i < oldFromNew.n_elem; i++)
@@ -301,31 +296,97 @@ DTree<MatType, TagType>* 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<double>::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;
}
template <typename MatType>
PathCacher::PathCacher(PathCacher::PathFormat fmt, DTree<MatType, int>* 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 <typename MatType>
void PathCacher::Enter(const DTree<MatType, int>* node,
const DTree<MatType, int>* 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 <typename MatType>
void PathCacher::Leave(const DTree<MatType, int>* ,
const DTree<MatType, int>* 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
#endif
#endif // MLPACK_METHODS_DET_DT_UTILS_IMPL_HPP
+22 -3
View File
@@ -184,11 +184,14 @@ 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);
TagType TagTree(const TagType& tag = 0, bool everyNode = false);
/**
* Return the tag of the leaf containing the query. This is useful for
@@ -198,6 +201,7 @@ class DTree
*/
TagType FindBucket(const VecType& query) const;
/**
* Compute the variable importance of each dimension in the learned tree.
*
@@ -301,7 +305,19 @@ 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 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; }
@@ -335,6 +351,9 @@ class DTree
const size_t splitDim,
const ElemType splitValue,
arma::Col<size_t>& oldFromNew) const;
void FillMinMax(const StatType& mins,
const StatType& maxs);
};
} // namespace det
+60 -9
View File
@@ -66,7 +66,7 @@ namespace details
const size_t minLeafSize)
{
typedef std::pair<ElemType, size_t> SplitItem;
arma::vec dimVec = data(dim, arma::span(start, end - 1)).t();
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());
@@ -191,6 +191,9 @@ template <typename MatType, typename TagType>
DTree<MatType, TagType>& DTree<MatType, TagType>::operator=(
const DTree<MatType, TagType>& obj)
{
if (this == &obj)
return *this;
// Copy the values from the other tree.
start = obj.start;
end = obj.end;
@@ -213,7 +216,7 @@ DTree<MatType, TagType>& DTree<MatType, TagType>::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 <typename MatType, typename TagType>
DTree<MatType, TagType>& DTree<MatType, TagType>::operator=(
DTree<MatType, TagType>&& obj)
{
if (this == &obj)
return *this;
// Move the values from the other tree.
start = obj.start;
end = obj.end;
@@ -883,7 +889,7 @@ double DTree<MatType, TagType>::ComputeValue(const VecType& query) const
// Index the buckets for possible usage later.
template <typename MatType, typename TagType>
TagType DTree<MatType, TagType>::TagTree(const TagType& tag)
TagType DTree<MatType, TagType>::TagTree(const TagType& tag, bool every)
{
if (subtreeLeaves == 1)
{
@@ -891,10 +897,17 @@ TagType DTree<MatType, TagType>::TagTree(const TagType& tag)
bucketTag = tag;
return (tag + 1);
}
else
TagType nextTag;
if (every)
{
return right->TagTree(left->TagTree(tag));
bucketTag = tag;
nextTag = (tag + 1);
}
else
nextTag = tag;
return right->TagTree(left->TagTree(nextTag, every), every);
}
template <typename MatType, typename TagType>
@@ -902,7 +915,15 @@ TagType DTree<MatType, TagType>::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 -1;
}
// If we are a leaf...
if (subtreeLeaves == 1)
{
return bucketTag;
}
@@ -944,6 +965,29 @@ DTree<MatType, TagType>::ComputeVariableImportance(arma::vec& importances) const
}
}
template <typename MatType, typename TagType>
void DTree<MatType, TagType>::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 <typename MatType, typename TagType>
template <typename Archive>
void DTree<MatType, TagType>::Serialize(Archive& ar,
@@ -953,8 +997,6 @@ void DTree<MatType, TagType>::Serialize(Archive& ar,
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");
@@ -976,5 +1018,14 @@ void DTree<MatType, TagType>::Serialize(Archive& ar,
ar & CreateNVP(left, "left");
ar & CreateNVP(right, "right");
}
if (root)
{
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);
}
}