Patch from Siddharth: templatize CF to accept arbitrary types of factorizers.

This commit is contained in:
Ryan Curtin
2014-03-02 04:39:49 +00:00
parent bf2ad9c99e
commit efda5e0463
6 changed files with 72 additions and 38 deletions
+1 -1
View File
@@ -2,7 +2,7 @@
# Anything not in this list will not be compiled into MLPACK.
set(SOURCES
cf.hpp
cf.cpp
cf_impl.hpp
)
# Add directory name to sources.
+31 -7
View File
@@ -12,10 +12,14 @@
#include <mlpack/core.hpp>
#include <mlpack/methods/neighbor_search/neighbor_search.hpp>
#include <mlpack/methods/nmf/nmf.hpp>
#include <mlpack/methods/nmf/als_update_rules.hpp>
#include <set>
#include <map>
#include <iostream>
using namespace mlpack::nmf;
namespace mlpack {
namespace cf /** Collaborative filtering. */{
@@ -32,7 +36,7 @@ namespace cf /** Collaborative filtering. */{
* arma::Mat<size_t> recommendations; // Recommendations
* size_t numRecommendations = 10;
*
* CF cf(data); // Default options.
* CF<> cf(data); // Default options.
*
* // Generate the default number of recommendations for all users.
* cf.GetRecommendations(recommendations);
@@ -49,8 +53,17 @@ namespace cf /** Collaborative filtering. */{
* should have three rows. The first represents the user; the second represents
* the item; and the third represents the rating. The user and item, while they
* are in a matrix that holds doubles, should hold integer (or size_t) values.
* The user and item indices are assumed to be starting from 0.
* The user and item indices are assumed to start at 0.
*
* @tparam FactorizerType The type of matrix factorization to use to decompose
* the rating matrix (a W and H matrix). This must implement the method
* Apply(arma::sp_mat& data, size_t rank, arma::mat& W, arma::mat& H).
*/
template<
typename FactorizerType = NMF<RandomInitialization,
WAlternatingLeastSquaresRule,
HAlternatingLeastSquaresRule>
>
class CF
{
public:
@@ -82,13 +95,13 @@ class CF
this->numRecs = recs;
}
//! Gets numRecs
size_t NumRecs()
//! Gets the number of recommendations.
size_t NumRecs() const
{
return numRecs;
}
//! Sets number of user for calculating similarity.
//! Sets number of users for calculating similarity.
void NumUsersForSimilarity(const size_t num)
{
if (num < 1)
@@ -101,7 +114,7 @@ class CF
}
//! Gets number of users for calculating similarity.
size_t NumUsersForSimilarity()
size_t NumUsersForSimilarity() const
{
return numUsersForSimilarity;
}
@@ -113,11 +126,17 @@ class CF
}
//! Gets rank parameter for matrix factorization.
size_t Rank()
size_t Rank() const
{
return rank;
}
//! Sets factorizer for NMF
void Factorizer(const FactorizerType& f)
{
this->factorizer = f;
}
//! Get the User Matrix.
const arma::mat& W() const { return w; }
//! Get the Item Matrix.
@@ -182,6 +201,8 @@ class CF
size_t numUsersForSimilarity;
//! Rank used for matrix factorization.
size_t rank;
//! Instantiated factorizer object.
FactorizerType factorizer;
//! User matrix.
arma::mat w;
//! Item matrix.
@@ -214,4 +235,7 @@ class CF
}; // namespace cf
}; // namespace mlpack
//Include implementation
#include "cf_impl.hpp"
#endif
@@ -22,10 +22,11 @@ namespace cf {
/**
* Construct the CF object.
*/
CF::CF(arma::mat& data,
const size_t numRecs,
const size_t numUsersForSimilarity,
const size_t rank) :
template<typename FactorizerType>
CF<FactorizerType>::CF(arma::mat& data,
const size_t numRecs,
const size_t numUsersForSimilarity,
const size_t rank) :
data(data),
numRecs(numRecs),
numUsersForSimilarity(numUsersForSimilarity),
@@ -49,10 +50,15 @@ CF::CF(arma::mat& data,
this->numUsersForSimilarity = 5;
}
//Set default factorizer
FactorizerType f(10000, 1e-5);
Factorizer(f);
CleanData();
}
void CF::GetRecommendations(arma::Mat<size_t>& recommendations)
template<typename FactorizerType>
void CF<FactorizerType>::GetRecommendations(arma::Mat<size_t>& recommendations)
{
// Used to save user IDs.
arma::Col<size_t> users =
@@ -65,8 +71,9 @@ void CF::GetRecommendations(arma::Mat<size_t>& recommendations)
GetRecommendations(recommendations, users);
}
void CF::GetRecommendations(arma::Mat<size_t>& recommendations,
arma::Col<size_t>& users)
template<typename FactorizerType>
void CF<FactorizerType>::GetRecommendations(arma::Mat<size_t>& recommendations,
arma::Col<size_t>& users)
{
// Base function for calculating recommendations.
@@ -87,11 +94,8 @@ void CF::GetRecommendations(arma::Mat<size_t>& recommendations,
// Operations independent of the query:
// Decompose the sparse data matrix to user and data matrices.
// Presently only ALS (via NMF) is supported as an optimizer. This should be
// converted to a template when more optimizers are available.
NMF<RandomInitialization, WAlternatingLeastSquaresRule,
HAlternatingLeastSquaresRule> als(10000, 1e-5);
als.Apply(cleanedData, rank, w, h);
// Presently only ALS (via NMF) is supported as an optimizer.
factorizer.Apply(cleanedData, rank, w, h);
// Generate new table by multiplying approximate values.
rating = w * h;
@@ -171,8 +175,9 @@ void CF::GetRecommendations(arma::Mat<size_t>& recommendations,
}
}
void CF::GetRecommendations(arma::Mat<size_t>& recommendations,
arma::Col<size_t>& users,size_t num)
template<typename FactorizerType>
void CF<FactorizerType>::GetRecommendations(arma::Mat<size_t>& recommendations,
arma::Col<size_t>& users,size_t num)
{
//Setting Number of Recommendations
NumRecs(num);
@@ -180,8 +185,10 @@ void CF::GetRecommendations(arma::Mat<size_t>& recommendations,
GetRecommendations(recommendations,users);
}
void CF::GetRecommendations(arma::Mat<size_t>& recommendations,
arma::Col<size_t>& users,size_t num,size_t s)
template<typename FactorizerType>
void CF<FactorizerType>::GetRecommendations(arma::Mat<size_t>& recommendations,
arma::Col<size_t>& users,size_t num,
size_t s)
{
//Setting number of users that should be used for calculating
//neighbours
@@ -192,7 +199,8 @@ void CF::GetRecommendations(arma::Mat<size_t>& recommendations,
GetRecommendations(recommendations,users,num);
}
void CF::CleanData()
template<typename FactorizerType>
void CF<FactorizerType>::CleanData()
{
// Generate list of locations for batch insert constructor for sparse
// matrices.
@@ -222,12 +230,13 @@ void CF::CleanData()
* @param neighbor Index of item being inserted as a recommendation.
* @param value Value of recommendation.
*/
void CF::InsertNeighbor(const size_t queryIndex,
const size_t pos,
const size_t neighbor,
const double value,
arma::Mat<size_t>& recommendations,
arma::mat& values) const
template<typename FactorizerType>
void CF<FactorizerType>::InsertNeighbor(const size_t queryIndex,
const size_t pos,
const size_t neighbor,
const double value,
arma::Mat<size_t>& recommendations,
arma::mat& values) const
{
// We only memmove() if there is actually a need to shift something.
if (pos < (recommendations.n_rows - 1))
@@ -247,7 +256,8 @@ void CF::InsertNeighbor(const size_t queryIndex,
}
// Return string of object.
std::string CF::ToString() const
template<typename FactorizerType>
std::string CF<FactorizerType>::ToString() const
{
std::ostringstream convert;
convert << "Collaborative Filtering [" << this << "]" << std::endl;
+1 -1
View File
@@ -70,7 +70,7 @@ int main(int argc, char** argv)
// Perform decomposition to prepare for recommendations.
Log::Info << "Performing CF matrix decomposition on dataset..." << endl;
CF c(dataset);
CF<> c(dataset);
c.NumRecs(numRecs);
c.NumUsersForSimilarity(neighborhood);
+4 -4
View File
@@ -33,7 +33,7 @@ BOOST_AUTO_TEST_CASE(CFConstructorTest)
// Number of users for similarity (not the default).
const size_t numUsersForSimilarity = 8;
CF c(dataset, numRecs, numUsersForSimilarity);
CF<> c(dataset, numRecs, numUsersForSimilarity);
// Check parameters.
BOOST_REQUIRE_EQUAL(c.NumRecs(), numRecs);
@@ -68,7 +68,7 @@ BOOST_AUTO_TEST_CASE(CFGetRecommendationsAllUsersTest)
data::Load("GroupLens100k.csv", dataset);
// Creat a CF object
CF c(dataset);
CF<> c(dataset);
// Set number of recommendations.
c.NumRecs(numRecs);
@@ -106,7 +106,7 @@ BOOST_AUTO_TEST_CASE(CFGetRecommendationsQueriedUserTest)
arma::mat dataset;
data::Load("GroupLens100k.csv", dataset);
CF c(dataset);
CF<> c(dataset);
// Generate recommendations when query set is specified.
c.GetRecommendations(recommendations, users);
@@ -161,7 +161,7 @@ BOOST_AUTO_TEST_CASE(RecommendationAccuracyTest)
}
// Now create the CF object.
CF c(dataset);
CF<> c(dataset);
// Obtain 150 recommendations for the users in savedCols, and make sure the
// missing item shows up in most of them. First, create the list of users,
+1 -1
View File
@@ -292,7 +292,7 @@ BOOST_AUTO_TEST_CASE(CFString)
c(0, 2) = 1;
c(1, 2) = 3;
c(2, 2) = 0.7;
mlpack::cf::CF d(c, a, a);
mlpack::cf::CF<> d(c, a, a);
Log::Debug << d;
std::string s = d.ToString();
BOOST_REQUIRE_NE(s, "");