From aa94bf7dc2e3c025bd3197fabe2a90141d96332f Mon Sep 17 00:00:00 2001 From: AYESDIE <34449856+AYESDIE@users.noreply.github.com> Date: Tue, 23 Oct 2018 18:04:46 +0530 Subject: [PATCH] Added support for different Interpolation and Neighbout Search Algorithms Added support for different Interpolation and Neighbout Search Algorithms --- src/mlpack/methods/cf/cf_main.cpp | 173 +++++++++++++++++++++++- src/mlpack/methods/cf/cf_model.hpp | 10 ++ src/mlpack/methods/cf/cf_model_impl.hpp | 36 +++-- 3 files changed, 206 insertions(+), 13 deletions(-) diff --git a/src/mlpack/methods/cf/cf_main.cpp b/src/mlpack/methods/cf/cf_main.cpp index 0d0741818f..ad896c122e 100644 --- a/src/mlpack/methods/cf/cf_main.cpp +++ b/src/mlpack/methods/cf/cf_main.cpp @@ -25,6 +25,15 @@ #include #include +#include +#include +#include + +#include +#include +#include + + using namespace mlpack; using namespace mlpack::cf; using namespace mlpack::amf; @@ -125,10 +134,21 @@ PARAM_INT_IN("recommendations", "Number of recommendations to generate for each" PARAM_INT_IN("seed", "Set the random seed (0 uses std::time(NULL)).", "s", 0); +// Interpolation and Neighbour Search Algorithms +PARAM_STRING_IN("interpolation", "Algorithm used for weight interpolation.", "i", + "Average"); + +PARAM_STRING_IN("neighbour_search", "Algorithm used for neighbour search.", "f", + "Euclidean"); + void ComputeRecommendations(CFModel* cf, const size_t numRecs, arma::Mat& recommendations) { + + const string ns_algo = CLI::GetParam("neighbour_search"); + const string iw_algo = CLI::GetParam("interpolation"); + // Reading users. if (CLI::HasParam("query")) { @@ -142,17 +162,112 @@ void ComputeRecommendations(CFModel* cf, Log::Info << "Generating recommendations for " << users.n_elem << " users." << endl; - cf->GetRecommendations(numRecs, recommendations, users.row(0).t()); + + // Making All the possible paths + if(ns_algo=="Cosine") + { + if(iw_algo=="Average") + { + cf->GetRecommendations(numRecs, recommendations, users.row(0).t()); + } + else if(iw_algo=="Regression") + { + cf->GetRecommendations(numRecs, recommendations, users.row(0).t()); + } + else if(iw_algo=="Similarity") + { + cf->GetRecommendations(numRecs, recommendations, users.row(0).t()); + } + } + else if(ns_algo=="Euclidean") + { + if(iw_algo=="Average") + { + cf->GetRecommendations(numRecs, recommendations, users.row(0).t()); + } + else if(iw_algo=="Regression") + { + cf->GetRecommendations(numRecs, recommendations, users.row(0).t()); + } + else if(iw_algo=="Similarity") + { + cf->GetRecommendations(numRecs, recommendations, users.row(0).t()); + } + } + else if(ns_algo=="Pearson") + { + if(iw_algo=="Average") + { + cf->GetRecommendations(numRecs, recommendations, users.row(0).t()); + } + else if(iw_algo=="Regression") + { + cf->GetRecommendations(numRecs, recommendations, users.row(0).t()); + } + else if(iw_algo=="Similarity") + { + cf->GetRecommendations(numRecs, recommendations, users.row(0).t()); + } + } } else { Log::Info << "Generating recommendations for all users." << endl; - cf->GetRecommendations(numRecs, recommendations); + if(ns_algo=="Cosine") + { + if(iw_algo=="Average") + { + cf->GetRecommendations(numRecs, recommendations); + } + else if(iw_algo=="Regression") + { + cf->GetRecommendations(numRecs, recommendations); + } + else if(iw_algo=="Similarity") + { + cf->GetRecommendations(numRecs, recommendations); + } + } + else if(ns_algo=="Euclidean") + { + if(iw_algo=="Average") + { + cf->GetRecommendations(numRecs, recommendations); + } + else if(iw_algo=="Regression") + { + cf->GetRecommendations(numRecs, recommendations); + } + else if(iw_algo=="Similarity") + { + cf->GetRecommendations(numRecs, recommendations); + } + } + else if(ns_algo=="Pearson") + { + if(iw_algo=="Average") + { + cf->GetRecommendations(numRecs, recommendations); + } + else if(iw_algo=="Regression") + { + cf->GetRecommendations(numRecs, recommendations); + } + else if(iw_algo=="Similarity") + { + cf->GetRecommendations(numRecs, recommendations); + } + } } } void ComputeRMSE(CFModel* cf) { + // Interpolation and Neighbour Search + + const string ns_algo = CLI::GetParam("neighbour_search"); + const string iw_algo = CLI::GetParam("interpolation"); + // Now, compute each test point. arma::mat testData = std::move(CLI::GetParam("test")); @@ -166,7 +281,52 @@ void ComputeRMSE(CFModel* cf) // Now compute the RMSE. arma::vec predictions; - cf->Predict(combinations, predictions); + + if(ns_algo=="Cosine") + { + if(iw_algo=="Average") + { + cf->Predict(combinations, predictions); + } + else if(iw_algo=="Regression") + { + cf->Predict(combinations, predictions); + } + else if(iw_algo=="Similarity") + { + cf->Predict(combinations, predictions); + } + } + else if(ns_algo=="Euclidean") + { + if(iw_algo=="Average") + { + cf->Predict(combinations, predictions); + } + else if(iw_algo=="Regression") + { + cf->Predict(combinations, predictions); + } + else if(iw_algo=="Similarity") + { + cf->Predict(combinations, predictions); + } + } + else if(ns_algo=="Pearson") + { + if(iw_algo=="Average") + { + cf->Predict(combinations, predictions); + } + else if(iw_algo=="Regression") + { + cf->Predict(combinations, predictions); + } + else if(iw_algo=="Similarity") + { + cf->Predict(combinations, predictions); + } + } // Compute the root of the sum of the squared errors, divide by the number of // points to get the RMSE. It turns out this is just the L2-norm divided by @@ -287,6 +447,13 @@ static void mlpackMain() "SVDIncompleteIncremental", "SVDCompleteIncremental", "RegSVD", "RandSVD", "BiasSVD", "SVDPP" }, true, "unknown algorithm"); + // Validate the interpolation and neighbour_search policy + RequireParamInSet("interpolation", { "Average", "Regression", "Similarity" }, + true, "unknown interpolation algorithm"); + + RequireParamInSet("neighbour_search", { "Cosine", "Euclidean", "Pearson" }, + true, "unknown neighbour search algorithm"); + ReportIgnoredParam({{ "iteration_only_termination", true }}, "min_residue"); RequireParamValue("recommendations", [](int x) { return x > 0; }, true, diff --git a/src/mlpack/methods/cf/cf_model.hpp b/src/mlpack/methods/cf/cf_model.hpp index 0b82687bbc..ae568e371d 100644 --- a/src/mlpack/methods/cf/cf_model.hpp +++ b/src/mlpack/methods/cf/cf_model.hpp @@ -54,6 +54,8 @@ class GetValueVisitor : public boost::static_visitor * PredictVisitor uses the CFType object to make predictions on the given * combinations of users and items. */ +template class PredictVisitor : public boost::static_visitor { private: @@ -76,6 +78,8 @@ class PredictVisitor : public boost::static_visitor * RecommendationVisitor uses the CFType object to get recommendations for the * given users. */ +template class RecommendationVisitor : public boost::static_visitor { private: @@ -142,15 +146,21 @@ class CFModel const bool mit); //! Make predictions. + template void Predict(const arma::Mat& combinations, arma::vec& predictions); //! Compute recommendations for query users. + template void GetRecommendations(const size_t numRecs, arma::Mat& recommendations, const arma::Col& users); //! Compute recommendations for all users. + template void GetRecommendations(const size_t numRecs, arma::Mat& recommendations); diff --git a/src/mlpack/methods/cf/cf_model_impl.hpp b/src/mlpack/methods/cf/cf_model_impl.hpp index 2c54b13e5d..5f9f244b6c 100644 --- a/src/mlpack/methods/cf/cf_model_impl.hpp +++ b/src/mlpack/methods/cf/cf_model_impl.hpp @@ -34,15 +34,19 @@ void* GetValueVisitor::operator()(CFType* c) const return (void*) c; } -PredictVisitor::PredictVisitor( +template +PredictVisitor::PredictVisitor( const arma::Mat& combinations, arma::vec& predictions) : combinations(combinations), predictions(predictions) { } +template template -void PredictVisitor::operator()(CFType* c) const +void PredictVisitor::operator()(CFType* c) const { if (!c) { @@ -50,10 +54,13 @@ void PredictVisitor::operator()(CFType* c) const return; } - c->Predict(combinations, predictions); + c->template Predict(combinations, predictions); } -RecommendationVisitor::RecommendationVisitor( +template +RecommendationVisitor + ::RecommendationVisitor( const size_t numRecs, arma::Mat& recommendations, const arma::Col& users, @@ -64,8 +71,11 @@ RecommendationVisitor::RecommendationVisitor( usersGiven(usersGiven) { } +template template -void RecommendationVisitor::operator()(CFType* c) const +void RecommendationVisitor + ::operator()(CFType* c) const { if (!c) { @@ -74,9 +84,9 @@ void RecommendationVisitor::operator()(CFType* c) const } if (usersGiven) - c->GetRecommendations(numRecs, recommendations, users); + c->template GetRecommendations(numRecs, recommendations, users); else - c->GetRecommendations(numRecs, recommendations); + c->template GetRecommendations(numRecs, recommendations); } CFModel::~CFModel() @@ -103,28 +113,34 @@ void CFModel::Train(const MatType& data, } //! Make predictions. +template void CFModel::Predict(const arma::Mat& combinations, arma::vec& predictions) { - PredictVisitor predict(combinations, predictions); + PredictVisitor predict(combinations, predictions); boost::apply_visitor(predict, cf); } //! Compute recommendations for queried users. +template void CFModel::GetRecommendations(const size_t numRecs, arma::Mat& recommendations, const arma::Col& users) { - RecommendationVisitor recommendation(numRecs, recommendations, users, true); + RecommendationVisitor recommendation(numRecs, recommendations, users, true); boost::apply_visitor(recommendation, cf); } //! Compute recommendations for all users. +template void CFModel::GetRecommendations(const size_t numRecs, arma::Mat& recommendations) { arma::Col users; - RecommendationVisitor recommendation(numRecs, recommendations, users, false); + RecommendationVisitor recommendation(numRecs, recommendations, users, false); boost::apply_visitor(recommendation, cf); }