From 8c8da569cdddd2932881412cf71fa4a46f20bb08 Mon Sep 17 00:00:00 2001 From: Roberto Hueso Gomez Date: Wed, 17 Jul 2019 20:40:33 +0200 Subject: [PATCH] Improve KDE docs and style --- src/mlpack/methods/kde/kde_main.cpp | 2 + src/mlpack/methods/kde/kde_rules.hpp | 8 ++-- src/mlpack/methods/kde/kde_rules_impl.hpp | 58 +++++++++++++++-------- 3 files changed, 43 insertions(+), 25 deletions(-) diff --git a/src/mlpack/methods/kde/kde_main.cpp b/src/mlpack/methods/kde/kde_main.cpp index 6a76b3d8af..90cafc7a8a 100644 --- a/src/mlpack/methods/kde/kde_main.cpp +++ b/src/mlpack/methods/kde/kde_main.cpp @@ -111,6 +111,8 @@ PROGRAM_INFO("Kernel Density Estimation", SEE_ALSO("@knn", "#knn"), SEE_ALSO("Kernel density estimation on Wikipedia", "https://en.wikipedia.org/wiki/Kernel_density_estimation"), + SEE_ALSO("Tree-Independent Dual-Tree Algorithms", + "https://arxiv.org/pdf/1304.4327.pdf"), SEE_ALSO("Fast High-dimensional Kernel Summations Using the Monte Carlo " "Multipole Method", "http://papers.nips.cc/paper/3539-fast-high-" "dimensional-kernel-summations-using-the-monte-carlo-multipole-method." diff --git a/src/mlpack/methods/kde/kde_rules.hpp b/src/mlpack/methods/kde/kde_rules.hpp index e7b240e8ff..44da899cd5 100644 --- a/src/mlpack/methods/kde/kde_rules.hpp +++ b/src/mlpack/methods/kde/kde_rules.hpp @@ -71,10 +71,10 @@ class KDERules TreeType& referenceNode, const double oldScore) const; - //! DoubleTree Score. + //! Dual-Tree Score. double Score(TreeType& queryNode, TreeType& referenceNode); - //! DoubleTree Rescore. + //! Dual-Tree Rescore. double Rescore(TreeType& queryNode, TreeType& referenceNode, const double oldScore) const; @@ -193,10 +193,10 @@ class KDECleanRules TreeType& /* referenceNode */, const double oldScore) const { return oldScore; } - //! DoubleTree Score. + //! Dual-Tree Score. double Score(TreeType& queryNode, TreeType& referenceNode); - //! DoubleTree Rescore. + //! Dual-Tree Rescore. double Rescore(TreeType& /* queryNode */, TreeType& /* referenceNode*/ , const double oldScore) const { return oldScore; } diff --git a/src/mlpack/methods/kde/kde_rules_impl.hpp b/src/mlpack/methods/kde/kde_rules_impl.hpp index 516d1f6cd0..dced36dbdc 100644 --- a/src/mlpack/methods/kde/kde_rules_impl.hpp +++ b/src/mlpack/methods/kde/kde_rules_impl.hpp @@ -96,6 +96,7 @@ Score(const size_t queryIndex, TreeType& referenceNode) double score, maxKernel, minKernel, bound, depthAlpha; const arma::vec& queryPoint = querySet.unsafe_col(queryIndex); const double minDistance = referenceNode.MinDistance(queryPoint); + // Calculations are not duplicated. bool newCalculations = true; // Calculate alpha if Monte Carlo is available. @@ -149,23 +150,27 @@ Score(const size_t queryIndex, TreeType& referenceNode) kernelIsGaussian) { // Monte Carlo probabilistic estimation. + // Calculate z using accumulated alpha if possible. const double alpha = depthAlpha + accumMCAlpha(queryIndex); - const boost::math::normal normalDist; const double z = std::abs(boost::math::quantile(normalDist, alpha / 2)); + + // Auxiliary variables. const size_t numDesc = referenceNode.NumDescendants(); arma::vec sample; size_t m = initialSampleSize; double meanSample; bool useMonteCarloPredictions = true; + + // Resample as long as confidence is not high enough. while (m > 0) { const size_t oldSize = sample.size(); const size_t newSize = oldSize + m; - // Don't use probabilistic estimation if this is gonna take a close - // amount of computation to the exact calculation. + // Don't use probabilistic estimation if this is going to take a similar + // amount of computations to the exact calculation. if (newSize >= mcBreakCoef * numDesc) { useMonteCarloPredictions = false; @@ -195,7 +200,7 @@ Score(const size_t queryIndex, TreeType& referenceNode) if (useMonteCarloPredictions) { - // Use Monte Carlo estimation. + // Confidence is high enough so we can use Monte Carlo estimation. densities(queryIndex) += numDesc * meanSample; score = DBL_MAX; @@ -218,7 +223,8 @@ Score(const size_t queryIndex, TreeType& referenceNode) { score = minDistance; - // If node is going to be exactly computed, reclaim not used alpha. + // If node is going to be exactly computed, reclaim not used alpha for + // Monte Carlo estimations. if (kernelIsGaussian && monteCarlo && newCalculations && @@ -235,16 +241,16 @@ Score(const size_t queryIndex, TreeType& referenceNode) } template -inline double KDERules::Rescore( - const size_t /* queryIndex */, - TreeType& /* referenceNode */, - const double oldScore) const +inline force_inline double KDERules:: +Rescore(const size_t /* queryIndex */, + TreeType& /* referenceNode */, + const double oldScore) const { // If it's pruned it continues to be pruned. return oldScore; } -//! Double-tree scoring function. +//! Dual-tree scoring function. template inline double KDERules:: Score(TreeType& queryNode, TreeType& referenceNode) @@ -262,7 +268,8 @@ Score(TreeType& queryNode, TreeType& referenceNode) else depthAlpha = -1; - // Check if not used alpha can be reclaimed for this combination of nodes. + // Check if not used Monte Carlo alpha can be reclaimed for this combination + // of nodes. const bool canReclaimAlpha = kernelIsGaussian && monteCarlo && @@ -326,29 +333,35 @@ Score(TreeType& queryNode, TreeType& referenceNode) kernelIsGaussian) { // Monte Carlo probabilistic estimation. + // Calculate z using accumulated alpha if possible. const double alpha = depthAlpha + queryStat.AccumAlpha(); - const boost::math::normal normalDist; const double z = std::abs(boost::math::quantile(normalDist, alpha / 2)); + + // Auxiliary variables. const size_t numDesc = referenceNode.NumDescendants(); arma::vec sample; arma::vec means = arma::zeros(queryNode.NumDescendants()); size_t m; double meanSample; bool useMonteCarloPredictions = true; + + // Pick a sample for every query node. for (size_t i = 0; i < queryNode.NumDescendants(); ++i) { const size_t queryIndex = queryNode.Descendant(i); sample.clear(); m = initialSampleSize; + + // Resample as long as confidence is not high enough. while (m > 0) { const size_t oldSize = sample.size(); const size_t newSize = oldSize + m; - // Don't use probabilistic estimation if this is gonna take a close - // amount of computation to the exact calculation. + // Don't use probabilistic estimation if this is going to take a similar + // amount of computations to the exact calculation. if (newSize >= mcBreakCoef * numDesc) { useMonteCarloPredictions = false; @@ -376,6 +389,7 @@ Score(TreeType& queryNode, TreeType& referenceNode) m = 0; } + // Store mean for the i_th query node descendant point. if (useMonteCarloPredictions) means(i) = meanSample; else @@ -384,11 +398,13 @@ Score(TreeType& queryNode, TreeType& referenceNode) if (useMonteCarloPredictions) { - // Use Monte Carlo estimation. - queryStat.AccumAlpha() = 0; + // Confidence is high enough so we can use Monte Carlo estimation. score = DBL_MAX; for (size_t i = 0; i < queryNode.NumDescendants(); ++i) densities(queryNode.Descendant(i)) += numDesc * means(i); + + // Accumulated alpha has been used. + queryStat.AccumAlpha() = 0; } else { @@ -397,7 +413,7 @@ Score(TreeType& queryNode, TreeType& referenceNode) if (canReclaimAlpha) { - // Reclaim not used alpha for Monte Carlo since the nodes will be + // Reclaim not used Monte Carlo alpha since the nodes will be // exactly computed. queryStat.AccumAlpha() += depthAlpha; } @@ -407,8 +423,8 @@ Score(TreeType& queryNode, TreeType& referenceNode) { score = minDistance; - // The node will be exactly computed, so we reclaim not used Monte Carlo - // alpha. + // If node is going to be exactly computed, reclaim not used alpha for + // Monte Carlo estimations. if (canReclaimAlpha) queryStat.AccumAlpha() += depthAlpha; } @@ -420,9 +436,9 @@ Score(TreeType& queryNode, TreeType& referenceNode) return score; } -//! Double-tree rescore. +//! Dual-tree rescore. template -inline double KDERules:: +inline force_inline double KDERules:: Rescore(TreeType& /*queryNode*/, TreeType& /*referenceNode*/, const double oldScore) const