From 18aba9b8bdd615303d7ca2eb3b60f44a132b7f51 Mon Sep 17 00:00:00 2001 From: Manish Date: Sat, 23 Jun 2018 11:06:26 +0530 Subject: [PATCH] make constraint member of the LMNN function class --- src/mlpack/methods/lmnn/constraints.hpp | 56 ++++++++++--- src/mlpack/methods/lmnn/constraints_impl.hpp | 79 ++++++++++++++----- src/mlpack/methods/lmnn/lmnn_function.hpp | 4 + .../methods/lmnn/lmnn_function_impl.hpp | 28 +++---- 4 files changed, 118 insertions(+), 49 deletions(-) diff --git a/src/mlpack/methods/lmnn/constraints.hpp b/src/mlpack/methods/lmnn/constraints.hpp index 3f19ca1ae2..2ef39bd89c 100644 --- a/src/mlpack/methods/lmnn/constraints.hpp +++ b/src/mlpack/methods/lmnn/constraints.hpp @@ -51,28 +51,40 @@ class Constraints * passed matrix. * * @param outputMatrix Coordinates matrix to store target neighbors. + * @param dataset Input dataset. + * @param labels Input dataset labels. */ - void TargetNeighbors(arma::Mat& outputMatrix); + void TargetNeighbors(arma::Mat& outputMatrix, + const arma::mat& dataset, + const arma::Row& labels); /** * Calculates k similar labeled nearest neighbors for a batch of dataset and * stores them into the passed matrix. * * @param outputMatrix Coordinates matrix to store target neighbors. + * @param dataset Input dataset. + * @param labels Input dataset labels. * @param begin Index of the initial point of dataset. * @param batchSize Number of data points to use. */ void TargetNeighbors(arma::Mat& outputMatrix, - const size_t begin, - const size_t batchSize); + const arma::mat& dataset, + const arma::Row& labels, + const size_t begin, + const size_t batchSize); /** * Calculates k differently labeled nearest neighbors for each datapoint and * writes them back to passed matrix. * * @param outputMatrix Coordinates matrix to store impostors. + * @param dataset Input dataset. + * @param labels Input dataset labels. */ - void Impostors(arma::Mat& outputMatrix); + void Impostors(arma::Mat& outputMatrix, + const arma::mat& dataset, + const arma::Row& labels); /** * Calculates k differently labeled nearest neighbors & distances to @@ -80,19 +92,27 @@ class Constraints * * @param outputNeighbors Coordinates matrix to store impostors. * @param outputDistance matrix to store distance. + * @param dataset Input dataset. + * @param labels Input dataset labels. */ void Impostors(arma::Mat& outputNeighbors, - arma::mat& outputDistance); + arma::mat& outputDistance, + const arma::mat& dataset, + const arma::Row& labels); /** * Calculates k differently labeled nearest neighbors for a batch of dataset * and writes them back to passed matrix. * * @param outputMatrix Coordinates matrix to store impostors. + * @param dataset Input dataset. + * @param labels Input dataset labels. * @param begin Index of the initial point of dataset. * @param batchSize Number of data points to use. */ void Impostors(arma::Mat& outputMatrix, + const arma::mat& dataset, + const arma::Row& labels, const size_t begin, const size_t batchSize); @@ -102,11 +122,15 @@ class Constraints * * @param outputNeighbors Coordinates matrix to store impostors. * @param outputDistance matrix to store distance. + * @param dataset Input dataset. + * @param labels Input dataset labels. * @param begin Index of the initial point of dataset. * @param batchSize Number of data points to use. */ void Impostors(arma::Mat& outputNeighbors, arma::mat& outputDistance, + const arma::mat& dataset, + const arma::Row& labels, const size_t begin, const size_t batchSize); @@ -115,21 +139,24 @@ class Constraints * triplets to matrix passed. * * @param outputMatrix Coordinates matrix to store triplets. + * @param dataset Input dataset. + * @param labels Input dataset labels. */ - void Triplets(arma::Mat& outputMatrix); + void Triplets(arma::Mat& outputMatrix, + const arma::mat& dataset, + const arma::Row& labels); //! Access the value of k. const size_t& K() const { return k; } //! Modify the value of k. size_t& K() { return k; } + //! Access the boolean value of precalculated. + const bool& PreCalulated() const { return precalculated; } + //! Modify the value of precalculated. + bool& PreCalulated() { return precalculated; } + private: - //! An alias of dataset. - arma::mat dataset; - - //! An alias of Labels. - arma::Row labels; - //! Number of target neighbors & impostors to calulate. size_t k; @@ -142,11 +169,14 @@ class Constraints //! Store indices of data points having different label. std::vector indexDiff; + //! False if nothing has ever been precalculated. + bool precalculated; + /** * Precalculate the unique labels, and indices of similar * and different datapoints on the basis of labels. */ - void Precalculate(); + inline void Precalculate(const arma::Row& labels); }; } // namespace lmnn diff --git a/src/mlpack/methods/lmnn/constraints_impl.hpp b/src/mlpack/methods/lmnn/constraints_impl.hpp index a44207425a..11fece8d65 100644 --- a/src/mlpack/methods/lmnn/constraints_impl.hpp +++ b/src/mlpack/methods/lmnn/constraints_impl.hpp @@ -20,12 +20,11 @@ namespace lmnn { template Constraints::Constraints( - const arma::mat& dataset, + const arma::mat& /* dataset */, const arma::Row& labels, size_t k) : - dataset(math::MakeAlias(const_cast(dataset), false)), - labels(math::MakeAlias(const_cast&>(labels), false)), - k(k) + k(k), + precalculated(false) { // Ensure a valid k is passed. size_t minCount = arma::min(arma::histc(labels, arma::unique(labels))); @@ -36,15 +35,17 @@ Constraints::Constraints( << minCount << " instances, but value of k is " << k << " " << "(k should be < " << minCount << ")!" << std::endl; } - - // Perform pre-calculation. - Precalculate(); } // Calculates k similar labeled nearest neighbors. template -void Constraints::TargetNeighbors(arma::Mat& outputMatrix) +void Constraints::TargetNeighbors(arma::Mat& outputMatrix, + const arma::mat& dataset, + const arma::Row& labels) { + // Perform pre-calculation. If neccesary. + Precalculate(labels); + // KNN instance. KNN knn; @@ -71,9 +72,14 @@ void Constraints::TargetNeighbors(arma::Mat& outputMatrix) // batch of data points. template void Constraints::TargetNeighbors(arma::Mat& outputMatrix, - const size_t begin, - const size_t batchSize) + const arma::mat& dataset, + const arma::Row& labels, + const size_t begin, + const size_t batchSize) { + // Perform pre-calculation. If neccesary. + Precalculate(labels); + arma::mat subDataset = dataset.cols(begin, begin + batchSize - 1); arma::Row sublabels = labels.cols(begin, begin + batchSize - 1); @@ -107,8 +113,13 @@ void Constraints::TargetNeighbors(arma::Mat& outputMatrix, // Calculates k differently labeled nearest neighbors. template -void Constraints::Impostors(arma::Mat& outputMatrix) +void Constraints::Impostors(arma::Mat& outputMatrix, + const arma::mat& dataset, + const arma::Row& labels) { + // Perform pre-calculation. If neccesary. + Precalculate(labels); + // KNN instance. KNN knn; @@ -135,8 +146,13 @@ void Constraints::Impostors(arma::Mat& outputMatrix) // writes back calculated neighbors & distances to passed matrices. template void Constraints::Impostors(arma::Mat& outputNeighbors, - arma::mat& outputDistance) + arma::mat& outputDistance, + const arma::mat& dataset, + const arma::Row& labels) { + // Perform pre-calculation. If neccesary. + Precalculate(labels); + // KNN instance. KNN knn; @@ -164,9 +180,14 @@ void Constraints::Impostors(arma::Mat& outputNeighbors, // batch of data points. template void Constraints::Impostors(arma::Mat& outputMatrix, - const size_t begin, - const size_t batchSize) + const arma::mat& dataset, + const arma::Row& labels, + const size_t begin, + const size_t batchSize) { + // Perform pre-calculation. If neccesary. + Precalculate(labels); + arma::mat subDataset = dataset.cols(begin, begin + batchSize - 1); arma::Row sublabels = labels.cols(begin, begin + batchSize - 1); @@ -202,10 +223,15 @@ void Constraints::Impostors(arma::Mat& outputMatrix, // batch of data points. template void Constraints::Impostors(arma::Mat& outputNeighbors, - arma::mat& outputDistance, - const size_t begin, - const size_t batchSize) + arma::mat& outputDistance, + const arma::mat& dataset, + const arma::Row& labels, + const size_t begin, + const size_t batchSize) { + // Perform pre-calculation. If neccesary. + Precalculate(labels); + arma::mat subDataset = dataset.cols(begin, begin + batchSize - 1); arma::Row sublabels = labels.cols(begin, begin + batchSize - 1); @@ -241,15 +267,20 @@ void Constraints::Impostors(arma::Mat& outputNeighbors, // Generates {data point, target neighbors, impostors} triplets using // TargetNeighbors() and Impostors(). template -void Constraints::Triplets(arma::Mat& outputMatrix) +void Constraints::Triplets(arma::Mat& outputMatrix, + const arma::mat& dataset, + const arma::Row& labels) { + // Perform pre-calculation. If neccesary. + Precalculate(labels); + size_t N = dataset.n_cols; arma::Mat impostors; - Impostors(impostors); + Impostors(impostors, dataset); arma::Mat targetNeighbors; - TargetNeighbors(targetNeighbors); + TargetNeighbors(targetNeighbors, dataset); outputMatrix = arma::Mat(3, k * k * N , arma::fill::zeros); @@ -269,8 +300,12 @@ void Constraints::Triplets(arma::Mat& outputMatrix) } template -void Constraints::Precalculate() +inline void Constraints::Precalculate(const arma::Row& labels) { + // Make sure the calculation is necessary. + if (precalculated) + return; + uniqueLabels = arma::unique(labels); indexSame.resize(uniqueLabels.n_elem); @@ -282,6 +317,8 @@ void Constraints::Precalculate() indexSame[i] = arma::find(labels == uniqueLabels[i]); indexDiff[i] = arma::find(labels != uniqueLabels[i]); } + + precalculated = true; } } // namespace lmnn diff --git a/src/mlpack/methods/lmnn/lmnn_function.hpp b/src/mlpack/methods/lmnn/lmnn_function.hpp index fa6b1a3873..0c01277d2c 100644 --- a/src/mlpack/methods/lmnn/lmnn_function.hpp +++ b/src/mlpack/methods/lmnn/lmnn_function.hpp @@ -16,6 +16,8 @@ #include #include +#include "constraints.hpp" + namespace mlpack { namespace lmnn { @@ -187,6 +189,8 @@ class LMNNFunction size_t iteration; //! Range after which impostors need to be recalculated. size_t range; + //! Constraints Object. + Constraints constraint; //! Holds pre-calculated cij. arma::mat p_cij; /** diff --git a/src/mlpack/methods/lmnn/lmnn_function_impl.hpp b/src/mlpack/methods/lmnn/lmnn_function_impl.hpp index b6acd11a6f..6ee0454a0e 100644 --- a/src/mlpack/methods/lmnn/lmnn_function_impl.hpp +++ b/src/mlpack/methods/lmnn/lmnn_function_impl.hpp @@ -13,7 +13,6 @@ #define MLPACK_METHODS_LMNN_FUNCTION_IMPL_HPP #include "lmnn_function.hpp" -#include "constraints.hpp" #include #include @@ -33,7 +32,8 @@ LMNNFunction::LMNNFunction(const arma::mat& dataset, metric(metric), regularization(regularization), iteration(0), - range(1) + range(1), + constraint(dataset, labels, k) { // Initialize the initial learning point. initialPoint.eye(dataset.n_rows, dataset.n_rows); @@ -44,9 +44,9 @@ LMNNFunction::LMNNFunction(const arma::mat& dataset, targetNeighbors = arma::Mat(k, dataset.n_cols, arma::fill::zeros); impostors = arma::Mat(k, dataset.n_cols, arma::fill::zeros); distance = arma::mat(k, dataset.n_cols, arma::fill::zeros); - Constraints constraint(dataset, labels, k); - constraint.TargetNeighbors(targetNeighbors); - constraint.Impostors(impostors); + + constraint.TargetNeighbors(targetNeighbors, dataset, labels); + constraint.Impostors(impostors, dataset, labels); // Precalculate and save the gradient due to target neighbors. Precalculate(); @@ -68,8 +68,8 @@ void LMNNFunction::Shuffle() labels = std::move(newLabels); // Re-calculate target neighbors as indices changed. - Constraints constraint(dataset, labels, k); - constraint.TargetNeighbors(targetNeighbors); + constraint.PreCalulated() = false; + constraint.TargetNeighbors(targetNeighbors, dataset, labels); } //! Evaluate cost over whole dataset. @@ -84,8 +84,7 @@ double LMNNFunction::Evaluate(const arma::mat& transformation) if (iteration % range == 0) { // Re-calculate impostors on transformed dataset. - Constraints constraint(transformedDataset, labels, k); - constraint.Impostors(impostors, distance); + constraint.Impostors(impostors, distance, transformedDataset, labels); } for (size_t i = 0; i < dataset.n_cols; i++) @@ -151,8 +150,8 @@ double LMNNFunction::Evaluate(const arma::mat& transformation, if (iteration % range == 0) { // Re-calculate impostors on transformed dataset. - Constraints constraint(transformedDataset, labels, k); - constraint.Impostors(impostors, distance, begin, batchSize); + constraint.Impostors(impostors, distance, transformedDataset, labels, + begin, batchSize); } for (size_t i = begin; i < begin + batchSize; i++) @@ -322,8 +321,7 @@ double LMNNFunction::EvaluateWithGradient( if (iteration % range == 0) { // Re-calculate impostors on transformed dataset. - Constraints constraint(transformedDataset, labels, k); - constraint.Impostors(impostors, distance); + constraint.Impostors(impostors, distance, transformedDataset, labels); } gradient.zeros(transformation.n_rows, transformation.n_cols); @@ -409,8 +407,8 @@ double LMNNFunction::EvaluateWithGradient( if (iteration % range == 0) { // Re-calculate impostors on transformed dataset. - Constraints constraint(transformedDataset, labels, k); - constraint.Impostors(impostors, distance, begin, batchSize); + constraint.Impostors(impostors, distance, transformedDataset, labels, + begin, batchSize); } gradient.zeros(transformation.n_rows, transformation.n_cols);