From 2f1a2272a14fe52dc12fe24a8297558a0f391c59 Mon Sep 17 00:00:00 2001 From: Jun An Date: Wed, 14 Mar 2018 01:01:15 +0800 Subject: [PATCH 01/10] Add 1 centroid if none converged after max iterations in mean shift --- src/mlpack/methods/mean_shift/mean_shift_impl.hpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/mlpack/methods/mean_shift/mean_shift_impl.hpp b/src/mlpack/methods/mean_shift/mean_shift_impl.hpp index 8ac716f8b5..fa58689504 100644 --- a/src/mlpack/methods/mean_shift/mean_shift_impl.hpp +++ b/src/mlpack/methods/mean_shift/mean_shift_impl.hpp @@ -260,6 +260,18 @@ inline void MeanShift::Cluster( } } + // If no centroid has converged due to too little iterations, take 1 random + // centroid calculated. + if (centroids.empty()) + { + if (maxIterations == 0) + { + centroids.insert_cols(centroids.n_cols, data.col(0)); + } else { + centroids.insert_cols(centroids.n_cols, allCentroids.col(0)); + } + } + // Assign centroids to each point. neighbor::KNN neighborSearcher(centroids); arma::mat neighborDistances; From 2e6737e7e338c268814cc6ae5fef7542c57098ff Mon Sep 17 00:00:00 2001 From: Jun An Date: Thu, 15 Mar 2018 16:04:42 +0800 Subject: [PATCH 02/10] Add force_convergence flag to mean_shift_main --- src/mlpack/methods/mean_shift/mean_shift_main.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/mlpack/methods/mean_shift/mean_shift_main.cpp b/src/mlpack/methods/mean_shift/mean_shift_main.cpp index bc1f43f85f..614980bb42 100644 --- a/src/mlpack/methods/mean_shift/mean_shift_main.cpp +++ b/src/mlpack/methods/mean_shift/mean_shift_main.cpp @@ -53,6 +53,9 @@ PARAM_FLAG("in_place", "If specified, a column containing the learned cluster " "--output_file is overridden. (Do not use with Python.)", "P"); PARAM_FLAG("labels_only", "If specified, only the output labels will be " "written to the file specified by --output_file.", "l"); +PARAM_FLAG("force_convergence", "If specified, the mean shift algorithm will " + "continue running regardless of max_iterations until the clusters converge." + ,"f"); PARAM_MATRIX_OUT("output", "Matrix to write output labels or labeled data to.", "o"); PARAM_MATRIX_OUT("centroid", "If specified, the centroids of each cluster will " From c739d9f8014352a969e8886f776890563ccfc239 Mon Sep 17 00:00:00 2001 From: Jun An Date: Thu, 15 Mar 2018 16:10:32 +0800 Subject: [PATCH 03/10] Update mean_shift::Cluster to take in forceConvergence flag --- src/mlpack/methods/mean_shift/mean_shift.hpp | 1 + src/mlpack/methods/mean_shift/mean_shift_impl.hpp | 1 + src/mlpack/methods/mean_shift/mean_shift_main.cpp | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/mlpack/methods/mean_shift/mean_shift.hpp b/src/mlpack/methods/mean_shift/mean_shift.hpp index 8541bb2213..ba62da2373 100644 --- a/src/mlpack/methods/mean_shift/mean_shift.hpp +++ b/src/mlpack/methods/mean_shift/mean_shift.hpp @@ -84,6 +84,7 @@ class MeanShift void Cluster(const MatType& data, arma::Row& assignments, arma::mat& centroids, + bool forceConvergence = true, bool useSeeds = true); //! Get the maximum number of iterations. diff --git a/src/mlpack/methods/mean_shift/mean_shift_impl.hpp b/src/mlpack/methods/mean_shift/mean_shift_impl.hpp index fa58689504..31a59e50df 100644 --- a/src/mlpack/methods/mean_shift/mean_shift_impl.hpp +++ b/src/mlpack/methods/mean_shift/mean_shift_impl.hpp @@ -185,6 +185,7 @@ inline void MeanShift::Cluster( const MatType& data, arma::Row& assignments, arma::mat& centroids, + bool forceConvergence, bool useSeeds) { if (radius <= 0) diff --git a/src/mlpack/methods/mean_shift/mean_shift_main.cpp b/src/mlpack/methods/mean_shift/mean_shift_main.cpp index 614980bb42..a45a7bdc05 100644 --- a/src/mlpack/methods/mean_shift/mean_shift_main.cpp +++ b/src/mlpack/methods/mean_shift/mean_shift_main.cpp @@ -92,7 +92,7 @@ static void mlpackMain() Timer::Start("clustering"); Log::Info << "Performing mean shift clustering..." << endl; - meanShift.Cluster(dataset, assignments, centroids); + meanShift.Cluster(dataset, assignments, centroids, CLI::HasParam("force_convergence")); Timer::Stop("clustering"); Log::Info << "Found " << centroids.n_cols << " centroids." << endl; From 41e01ce7f5a9d001e6cb351f8f0cb28e9a29d441 Mon Sep 17 00:00:00 2001 From: Jun An Date: Thu, 15 Mar 2018 16:26:10 +0800 Subject: [PATCH 04/10] Update mean_shift::Cluster to use forceConvergence flag. --- src/mlpack/methods/mean_shift/mean_shift_impl.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/mlpack/methods/mean_shift/mean_shift_impl.hpp b/src/mlpack/methods/mean_shift/mean_shift_impl.hpp index 31a59e50df..84a34e4279 100644 --- a/src/mlpack/methods/mean_shift/mean_shift_impl.hpp +++ b/src/mlpack/methods/mean_shift/mean_shift_impl.hpp @@ -217,8 +217,8 @@ inline void MeanShift::Cluster( { // Initial centroid is the seed itself. allCentroids.col(i) = pSeeds->unsafe_col(i); - for (size_t completedIterations = 0; completedIterations < maxIterations; - completedIterations++) + for (size_t completedIterations = 0; completedIterations < maxIterations + || forceConvergence; completedIterations++) { // Store new centroid in this. arma::colvec newCentroid = arma::zeros(pSeeds->n_rows); @@ -261,8 +261,8 @@ inline void MeanShift::Cluster( } } - // If no centroid has converged due to too little iterations, take 1 random - // centroid calculated. + // If no centroid has converged due to too little iterations and without + // forcing convergence, take 1 random centroid calculated. if (centroids.empty()) { if (maxIterations == 0) From 6a89ea91bbbf0a1e48bed2e788cb0784d34e6c88 Mon Sep 17 00:00:00 2001 From: Jun An Date: Thu, 15 Mar 2018 16:49:02 +0800 Subject: [PATCH 05/10] Update mean_shift::Cluster to give warning message if no convergence. --- src/mlpack/methods/mean_shift/mean_shift_impl.hpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/mlpack/methods/mean_shift/mean_shift_impl.hpp b/src/mlpack/methods/mean_shift/mean_shift_impl.hpp index 84a34e4279..e44a545895 100644 --- a/src/mlpack/methods/mean_shift/mean_shift_impl.hpp +++ b/src/mlpack/methods/mean_shift/mean_shift_impl.hpp @@ -265,6 +265,10 @@ inline void MeanShift::Cluster( // forcing convergence, take 1 random centroid calculated. if (centroids.empty()) { + Log::Warn << "No clusters converge, setting 1 random centroid calculated." + << std::endl << "Try a larger max_iterations or pass force_convergence flag." + << std::endl; + if (maxIterations == 0) { centroids.insert_cols(centroids.n_cols, data.col(0)); From f9629d42ac8398cd2e84a6816606704c160c65fe Mon Sep 17 00:00:00 2001 From: Jun An Date: Thu, 15 Mar 2018 17:08:37 +0800 Subject: [PATCH 06/10] Optimize mean_shift::Cluster when setting 1 random centroid. --- .../methods/mean_shift/mean_shift_impl.hpp | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/src/mlpack/methods/mean_shift/mean_shift_impl.hpp b/src/mlpack/methods/mean_shift/mean_shift_impl.hpp index e44a545895..179b9fe409 100644 --- a/src/mlpack/methods/mean_shift/mean_shift_impl.hpp +++ b/src/mlpack/methods/mean_shift/mean_shift_impl.hpp @@ -263,7 +263,7 @@ inline void MeanShift::Cluster( // If no centroid has converged due to too little iterations and without // forcing convergence, take 1 random centroid calculated. - if (centroids.empty()) + if (centroids.empty()) { Log::Warn << "No clusters converge, setting 1 random centroid calculated." << std::endl << "Try a larger max_iterations or pass force_convergence flag." @@ -272,17 +272,23 @@ inline void MeanShift::Cluster( if (maxIterations == 0) { centroids.insert_cols(centroids.n_cols, data.col(0)); - } else { + } + else + { centroids.insert_cols(centroids.n_cols, allCentroids.col(0)); } - } + assignments.zeros(); - // Assign centroids to each point. - neighbor::KNN neighborSearcher(centroids); - arma::mat neighborDistances; - arma::Mat resultingNeighbors; - neighborSearcher.Search(data, 1, resultingNeighbors, neighborDistances); - assignments = resultingNeighbors; + } + else + { + // Assign centroids to each point. + neighbor::KNN neighborSearcher(centroids); + arma::mat neighborDistances; + arma::Mat resultingNeighbors; + neighborSearcher.Search(data, 1, resultingNeighbors, neighborDistances); + assignments = resultingNeighbors; + } } } // namespace meanshift From 19ae320eabe936307467fed3f8fdcae83ef9b7f6 Mon Sep 17 00:00:00 2001 From: Jun An Date: Thu, 15 Mar 2018 17:45:51 +0800 Subject: [PATCH 07/10] Add force_convergence flag test in mean_shift_test. --- .../tests/main_tests/mean_shift_test.cpp | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/mlpack/tests/main_tests/mean_shift_test.cpp b/src/mlpack/tests/main_tests/mean_shift_test.cpp index 50f11beb0b..b0b6a3a512 100644 --- a/src/mlpack/tests/main_tests/mean_shift_test.cpp +++ b/src/mlpack/tests/main_tests/mean_shift_test.cpp @@ -118,6 +118,41 @@ BOOST_AUTO_TEST_CASE(MeanShiftInPlaceTest) BOOST_REQUIRE_EQUAL(CLI::GetParam("output").n_cols, numCols); } +/** + * Ensure that force_convergence is used by testing that the + * force_convergence flag makes a difference in the program. + */ +BOOST_AUTO_TEST_CASE(MeanShiftForceConvergenceTest) +{ + arma::mat x; + if (!data::Load("iris_test.csv", x)) + BOOST_FAIL("Cannot load test dataset iris_test.csv!"); + + // Input random data points. + SetInputParam("input", x); + // Set a very small max_iterations. + SetInputParam("max_iterations", (int) 1); + + mlpackMain(); + + const int numCentroids1 = CLI::GetParam("centroid").n_cols; + + ResetSettings(); + + // Input same random data points. + SetInputParam("input", std::move(x)); + // Set the same small max_iterations. + SetInputParam("max_iterations", (int) 1); + // Set the force_convergence flag on. + SetInputParam("force_convergence", true); + + mlpackMain(); + + const int numCentroids2 = CLI::GetParam("centroid").n_cols; + // Resulting number of centroids should be different. + BOOST_REQUIRE_NE(numCentroids1, numCentroids2); +} + /** * Ensure that radius is used by testing that the radius * makes a difference in the program. From f0714973692914aef943cd88edc2f57794942eaf Mon Sep 17 00:00:00 2001 From: Jun An Date: Thu, 15 Mar 2018 18:46:46 +0800 Subject: [PATCH 08/10] Resolve style issues. --- src/mlpack/methods/mean_shift/mean_shift_impl.hpp | 12 +++++------- src/mlpack/methods/mean_shift/mean_shift_main.cpp | 5 +++-- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/mlpack/methods/mean_shift/mean_shift_impl.hpp b/src/mlpack/methods/mean_shift/mean_shift_impl.hpp index 179b9fe409..5810e5f8cc 100644 --- a/src/mlpack/methods/mean_shift/mean_shift_impl.hpp +++ b/src/mlpack/methods/mean_shift/mean_shift_impl.hpp @@ -265,22 +265,20 @@ inline void MeanShift::Cluster( // forcing convergence, take 1 random centroid calculated. if (centroids.empty()) { - Log::Warn << "No clusters converge, setting 1 random centroid calculated." - << std::endl << "Try a larger max_iterations or pass force_convergence flag." - << std::endl; + Log::Warn << "No clusters converge, setting 1 random centroid calculated. " + "Try a larger max_iterations or pass force_convergence flag." << std::endl; if (maxIterations == 0) { centroids.insert_cols(centroids.n_cols, data.col(0)); } - else + else { centroids.insert_cols(centroids.n_cols, allCentroids.col(0)); } assignments.zeros(); - - } - else + } + else { // Assign centroids to each point. neighbor::KNN neighborSearcher(centroids); diff --git a/src/mlpack/methods/mean_shift/mean_shift_main.cpp b/src/mlpack/methods/mean_shift/mean_shift_main.cpp index a45a7bdc05..0ba89724c7 100644 --- a/src/mlpack/methods/mean_shift/mean_shift_main.cpp +++ b/src/mlpack/methods/mean_shift/mean_shift_main.cpp @@ -55,7 +55,7 @@ PARAM_FLAG("labels_only", "If specified, only the output labels will be " "written to the file specified by --output_file.", "l"); PARAM_FLAG("force_convergence", "If specified, the mean shift algorithm will " "continue running regardless of max_iterations until the clusters converge." - ,"f"); + , "f"); PARAM_MATRIX_OUT("output", "Matrix to write output labels or labeled data to.", "o"); PARAM_MATRIX_OUT("centroid", "If specified, the centroids of each cluster will " @@ -92,7 +92,8 @@ static void mlpackMain() Timer::Start("clustering"); Log::Info << "Performing mean shift clustering..." << endl; - meanShift.Cluster(dataset, assignments, centroids, CLI::HasParam("force_convergence")); + meanShift.Cluster(dataset, assignments, centroids, + CLI::HasParam("force_convergence")); Timer::Stop("clustering"); Log::Info << "Found " << centroids.n_cols << " centroids." << endl; From 26f49059256e8409133eca69c627758ede5b715d Mon Sep 17 00:00:00 2001 From: Jun An Date: Thu, 15 Mar 2018 22:51:49 +0800 Subject: [PATCH 09/10] Update mean_shift::Cluster with comments and optimization. --- src/mlpack/methods/mean_shift/mean_shift.hpp | 6 +++++- src/mlpack/methods/mean_shift/mean_shift_impl.hpp | 4 ++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/mlpack/methods/mean_shift/mean_shift.hpp b/src/mlpack/methods/mean_shift/mean_shift.hpp index ba62da2373..cf8730eae2 100644 --- a/src/mlpack/methods/mean_shift/mean_shift.hpp +++ b/src/mlpack/methods/mean_shift/mean_shift.hpp @@ -33,9 +33,11 @@ namespace meanshift /** Mean shift clustering. */ { * extern arma::mat data; // Dataset we want to run mean shift on. * arma::Row assignments; // Cluster assignments. * arma::mat centroids; // Cluster centroids. + * bool forceConvergence; // Flag whether to force each centroid seed to + * converge regardless of maxIterations. * * MeanShift<> meanShift(); - * meanShift.Cluster(dataset, assignments, centroids); + * meanShift.Cluster(dataset, assignments, centroids, forceConvergence); * @endcode * * @tparam UseKernel Use kernel or mean to calculate new centroid. @@ -80,6 +82,8 @@ class MeanShift * @param data Dataset to cluster. * @param assignments Vector to store cluster assignments in. * @param centroids Matrix in which centroids are stored. + * @param forceConvergence Flag whether to force each centroid seed to + * converge regardless of maxIterations. */ void Cluster(const MatType& data, arma::Row& assignments, diff --git a/src/mlpack/methods/mean_shift/mean_shift_impl.hpp b/src/mlpack/methods/mean_shift/mean_shift_impl.hpp index 5810e5f8cc..dabe2980a6 100644 --- a/src/mlpack/methods/mean_shift/mean_shift_impl.hpp +++ b/src/mlpack/methods/mean_shift/mean_shift_impl.hpp @@ -278,6 +278,10 @@ inline void MeanShift::Cluster( } assignments.zeros(); } + else if (centroids.n_cols == 1) + { + assignments.zeros(); + } else { // Assign centroids to each point. From a77b0b8fdafd23b1ff6a9d8883f5be86d5c2206f Mon Sep 17 00:00:00 2001 From: Jun An Date: Sun, 25 Mar 2018 02:07:31 +0800 Subject: [PATCH 10/10] Minor updates to comments to style. --- src/mlpack/methods/mean_shift/mean_shift.hpp | 4 ++-- src/mlpack/methods/mean_shift/mean_shift_main.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/mlpack/methods/mean_shift/mean_shift.hpp b/src/mlpack/methods/mean_shift/mean_shift.hpp index cf8730eae2..b393fac2a9 100644 --- a/src/mlpack/methods/mean_shift/mean_shift.hpp +++ b/src/mlpack/methods/mean_shift/mean_shift.hpp @@ -33,8 +33,8 @@ namespace meanshift /** Mean shift clustering. */ { * extern arma::mat data; // Dataset we want to run mean shift on. * arma::Row assignments; // Cluster assignments. * arma::mat centroids; // Cluster centroids. - * bool forceConvergence; // Flag whether to force each centroid seed to - * converge regardless of maxIterations. + * bool forceConvergence = true; // Flag whether to force each centroid seed + * to converge regardless of maxIterations. * * MeanShift<> meanShift(); * meanShift.Cluster(dataset, assignments, centroids, forceConvergence); diff --git a/src/mlpack/methods/mean_shift/mean_shift_main.cpp b/src/mlpack/methods/mean_shift/mean_shift_main.cpp index 0ba89724c7..4710ccaadd 100644 --- a/src/mlpack/methods/mean_shift/mean_shift_main.cpp +++ b/src/mlpack/methods/mean_shift/mean_shift_main.cpp @@ -93,7 +93,7 @@ static void mlpackMain() Timer::Start("clustering"); Log::Info << "Performing mean shift clustering..." << endl; meanShift.Cluster(dataset, assignments, centroids, - CLI::HasParam("force_convergence")); + CLI::HasParam("force_convergence")); Timer::Stop("clustering"); Log::Info << "Found " << centroids.n_cols << " centroids." << endl;