Abstract class for rating predictors that keep the rating data in memory for training (and possibly prediction)
Inheritance: IRatingPredictor, ICloneable
Example #1
0
		/// <summary>Find the the parameters resulting in the minimal results for a given evaluation measure (2D)</summary>
		/// <remarks>The recommender will be set to the best parameter value after calling this method.</remarks>
		/// <param name="evaluation_measure">the name of the evaluation measure</param>
		/// <param name="hp_name1">the name of the first hyperparameter to optimize</param>
		/// <param name="hp_values1">the values of the first hyperparameter to try out</param>
		/// <param name="hp_name2">the name of the second hyperparameter to optimize</param>
		/// <param name="hp_values2">the values of the second hyperparameter to try out</param>
		/// <param name="recommender">the recommender</param>
		/// <param name="split">the dataset split to use</param>
		/// <returns>the best (lowest) average value for the hyperparameter</returns>
		public static double FindMinimum(
			string evaluation_measure,
			string hp_name1, string hp_name2,
			double[] hp_values1, double[] hp_values2,
			RatingPredictor recommender,
			ISplit<IRatings> split)
		{
			double min_result = double.MaxValue;
			int min_i = -1;
			int min_j = -1;

			for (int i = 0; i < hp_values1.Length; i++)
				for (int j = 0; j < hp_values2.Length; j++)
				{
					recommender.SetProperty(hp_name1, hp_values1[i].ToString(CultureInfo.InvariantCulture));
					recommender.SetProperty(hp_name2, hp_values2[j].ToString(CultureInfo.InvariantCulture));

					Console.Error.WriteLine("reg_u={0} reg_i={1}", hp_values1[i].ToString(CultureInfo.InvariantCulture), hp_values2[j].ToString(CultureInfo.InvariantCulture)); // TODO this is not generic
					double result = recommender.DoCrossValidation(split)[evaluation_measure];
					if (result < min_result)
					{
						min_i = i;
						min_j = j;
						min_result = result;
					}
				}

			// set to best hyperparameter values
			recommender.SetProperty(hp_name1, hp_values1[min_i].ToString(CultureInfo.InvariantCulture));
			recommender.SetProperty(hp_name2, hp_values2[min_j].ToString(CultureInfo.InvariantCulture));

			return min_result;
		}
Example #2
0
        /// <summary>Find the the parameters resulting in the minimal results for a given evaluation measure (1D)</summary>
        /// <remarks>The recommender will be set to the best parameter value after calling this method.</remarks>
        /// <param name="evaluation_measure">the name of the evaluation measure</param>
        /// <param name="hyperparameter_name">the name of the hyperparameter to optimize</param>
        /// <param name="hyperparameter_values">the values of the hyperparameter to try out</param>
        /// <param name="recommender">the recommender</param>
        /// <param name="split">the dataset split to use</param>
        /// <returns>the best (lowest) average value for the hyperparameter</returns>
        public static double FindMinimum(
			string evaluation_measure,
			string hyperparameter_name,
			double[] hyperparameter_values,
			RatingPredictor recommender,
			ISplit<IRatings> split)
        {
            double min_result = double.MaxValue;
            int min_i = -1;

            for (int i = 0; i < hyperparameter_values.Length; i++)
            {
                recommender.SetProperty(hyperparameter_name, hyperparameter_values[i].ToString(CultureInfo.InvariantCulture));
                double result = recommender.DoCrossValidation(split)[evaluation_measure];

                if (result < min_result)
                {
                    min_i = i;
                    min_result = result;
                }
            }
            recommender.SetProperty(hyperparameter_name, hyperparameter_values[min_i].ToString(CultureInfo.InvariantCulture));

            return min_result;
        }
Example #3
0
		static double Run(RatingPredictor recommender, ISplit<IRatings> split, string hp_string, string evaluation_measure)
		{
			recommender.Configure(hp_string);

			double result = recommender.DoCrossValidation(split)[evaluation_measure];
			Console.Error.WriteLine("Nelder-Mead: {0}: {1}", hp_string, result.ToString(CultureInfo.InvariantCulture));
			return result;
		}
Example #4
0
		/// <summary>Find best hyperparameter (according to an error measure) using Nelder-Mead search</summary>
		/// <param name="error_measure">an error measure (lower is better)</param>
		/// <param name="recommender">a rating predictor (will be set to best hyperparameter combination)</param>
		/// <returns>the estimated error of the best hyperparameter combination</returns>
		public static double FindMinimum(
			string error_measure,
			RatingPredictor recommender)
		{
			var split = new RatingsSimpleSplit(recommender.Ratings, split_ratio);
			//var split = new RatingCrossValidationSplit(recommender.Ratings, 5);

			IList<string> hp_names;
			IList<DenseVector> initial_hp_values;

			// TODO manage this via reflection?
			if (recommender is UserItemBaseline)
			{
				hp_names = new string[] { "reg_u", "reg_i" };
				initial_hp_values = new DenseVector[] {
					new DenseVector( new double[] { 25, 10 } ),
					new DenseVector( new double[] { 10, 25 } ),
					new DenseVector( new double[] { 2, 5 } ),
					new DenseVector( new double[] { 5, 2 } ),
					new DenseVector( new double[] { 1, 4 } ),
					new DenseVector( new double[] { 4, 1 } ),
					new DenseVector( new double[] { 3, 3 } ),
				};
			}
			else if (recommender is BiasedMatrixFactorization)
			{
				hp_names = new string[] { "regularization", "bias_reg" };
				initial_hp_values = new DenseVector[] { // TODO reg_u and reg_i (in a second step?)
					new DenseVector( new double[] { 0.1,     0 } ),
					new DenseVector( new double[] { 0.01,    0 } ),
					new DenseVector( new double[] { 0.0001,  0 } ),
					new DenseVector( new double[] { 0.00001, 0 } ),
					new DenseVector( new double[] { 0.1,     0.0001 } ),
					new DenseVector( new double[] { 0.01,    0.0001 } ),
					new DenseVector( new double[] { 0.0001,  0.0001 } ),
					new DenseVector( new double[] { 0.00001, 0.0001 } ),
				};
			}
			else if (recommender is MatrixFactorization)
			{ // TODO normal interval search could be more efficient
				hp_names = new string[] { "regularization", };
				initial_hp_values = new DenseVector[] {
					new DenseVector( new double[] { 0.1     } ),
					new DenseVector( new double[] { 0.01    } ),
					new DenseVector( new double[] { 0.0001  } ),
					new DenseVector( new double[] { 0.00001 } ),
				};				
			}
			// TODO kNN-based methods
			else
			{
				throw new Exception("not prepared for type " + recommender.GetType().ToString());
			}

			return FindMinimum(
				error_measure,
				hp_names, initial_hp_values, recommender, split);
		}
Example #5
0
        /// <summary>Find the the parameters resulting in the minimal results for a given evaluation measure</summary>
        /// <remarks>The recommender will be set to the best parameter value after calling this method.</remarks>
        /// <param name="evaluation_measure">the name of the evaluation measure</param>
        /// <param name="hp_names">the names of the hyperparameters to optimize</param>
        /// <param name="initial_hp_values">the values of the hyperparameters to try out first</param>
        /// <param name="recommender">the recommender</param>
        /// <param name="split">the dataset split to use</param>
        /// <returns>the best (lowest) average value for the hyperparameter</returns>
        public static double FindMinimum(string evaluation_measure,
		                                 IList<string> hp_names,
		                                 IList<Vector> initial_hp_values,
		                                 RatingPredictor recommender, // TODO make more general?
		                                 ISplit<IRatings> split)
        {
            var results    = new Dictionary<string, double>();
            var hp_vectors = new Dictionary<string, Vector>();

            // initialize
            foreach (var hp_values in initial_hp_values)
            {
                string hp_string = CreateConfigString(hp_names, hp_values);
                results[hp_string] = Run(recommender, split, hp_string, evaluation_measure);
                hp_vectors[hp_string] = hp_values;
            }

            List<string> keys;
            for (int i = 0; i < num_it; i++)
            {
                if (results.Count != hp_vectors.Count)
                    throw new Exception(string.Format("{0} vs. {1}", results.Count, hp_vectors.Count));

                keys = new List<string>(results.Keys);
                keys.Sort(delegate(string k1, string k2) { return results[k1].CompareTo(results[k2]); });

                var min_key = keys.First();
                var max_key = keys.Last();

                Console.Error.WriteLine("Nelder-Mead: iteration {0} ({1})", i, results[min_key]);

                var worst_vector = hp_vectors[max_key];
                var worst_result = results[max_key];
                hp_vectors.Remove(max_key);
                results.Remove(max_key);

                // compute center
                var center = ComputeCenter(results, hp_vectors);

                // reflection
                //Console.Error.WriteLine("ref");
                var reflection = center + alpha * (center - worst_vector);
                string ref_string = CreateConfigString(hp_names, reflection);
                double ref_result = Run(recommender, split, ref_string, evaluation_measure);
                if (results[min_key] <= ref_result && ref_result < results.Values.Max())
                {
                    results[ref_string]    = ref_result;
                    hp_vectors[ref_string] = reflection;
                    continue;
                }

                // expansion
                if (ref_result < results[min_key])
                {
                    //Console.Error.WriteLine("exp");

                    var expansion = center + gamma * (center - worst_vector);
                    string exp_string = CreateConfigString(hp_names, expansion);
                    double exp_result = Run(recommender, split, exp_string, evaluation_measure);
                    if (exp_result < ref_result)
                    {
                        results[exp_string]    = exp_result;
                        hp_vectors[exp_string] = expansion;
                    }
                    else
                    {
                        results[ref_string]    = ref_result;
                        hp_vectors[ref_string] = reflection;
                    }
                    continue;
                }

                // contraction
                //Console.Error.WriteLine("con");
                var contraction = worst_vector + rho * (center - worst_vector);
                string con_string = CreateConfigString(hp_names, contraction);
                double con_result = Run(recommender, split, con_string, evaluation_measure);
                if (con_result < worst_result)
                {
                    results[con_string]    = con_result;
                    hp_vectors[con_string] = contraction;
                    continue;
                }

                // reduction
                //Console.Error.WriteLine("red");
                var best_vector = hp_vectors[min_key];
                var best_result = results[min_key];
                hp_vectors.Remove(min_key);
                results.Remove(min_key);
                foreach (var key in new List<string>(results.Keys))
                {
                    var reduction = hp_vectors[key] + sigma * (hp_vectors[key] - best_vector);
                    string red_string = CreateConfigString(hp_names, reduction);
                    double red_result = Run(recommender, split, red_string, evaluation_measure);

                    // replace by reduced vector
                    results.Remove(key);
                    hp_vectors.Remove(key);
                    results[red_string]    = red_result;
                    hp_vectors[red_string] = reduction;
                }
                results[min_key]    = best_result;
                hp_vectors[min_key] = best_vector;
                results[max_key]    = worst_result;
                hp_vectors[max_key] = worst_vector;
            }

            keys = new List<string>(results.Keys);
            keys.Sort(delegate(string k1, string k2) { return results[k1].CompareTo(results[k2]); });

            // set to best hyperparameter values
            Recommender.Configure(recommender, keys.First());

            return results[keys.First()];
        }
        /// <summary>Evaluate an iterative recommender on the folds of a dataset split, display results on STDOUT</summary>
        /// <param name="recommender">a rating predictor</param>
        /// <param name="split">a rating dataset split</param>
        /// <param name="max_iter">the maximum number of iterations</param>
        /// <param name="find_iter">the report interval</param>
        /// <param name="show_fold_results">if set to true to print per-fold results to STDERR</param>
        public static void DoIterativeCrossValidation(
			this RatingPredictor recommender,
			ISplit<IRatings> split,
			int max_iter,
			int find_iter = 1,
			bool show_fold_results = false)
        {
            if (!(recommender is IIterativeModel))
                throw new ArgumentException("recommender must be of type IIterativeModel");

            var split_recommenders     = new RatingPredictor[split.NumberOfFolds];
            var iterative_recommenders = new IIterativeModel[split.NumberOfFolds];
            var fold_results = new RatingPredictionEvaluationResults[split.NumberOfFolds];

            // initial training and evaluation
            Parallel.For(0, (int) split.NumberOfFolds, i =>
            {
                try
                {
                    split_recommenders[i] = (RatingPredictor) recommender.Clone(); // to avoid changes in recommender
                    split_recommenders[i].Ratings = split.Train[i];
                    if (recommender is ITransductiveRatingPredictor)
                        ((ITransductiveRatingPredictor) split_recommenders[i]).AdditionalFeedback = split.Test[i];
                    split_recommenders[i].Train();
                    iterative_recommenders[i] = (IIterativeModel) split_recommenders[i];
                    fold_results[i] = Ratings.Evaluate(split_recommenders[i], split.Test[i]);

                    if (show_fold_results)
                        Console.Error.WriteLine("fold {0} {1} iteration {2}", i, fold_results[i], iterative_recommenders[i].NumIter);
                }
                catch (Exception e)
                {
                    Console.Error.WriteLine("===> ERROR: " + e.Message + e.StackTrace);
                    throw e;
                }
            });
            Console.WriteLine("{0} iteration {1}", new RatingPredictionEvaluationResults(fold_results), iterative_recommenders[0].NumIter);

            // iterative training and evaluation
            for (int it = (int) iterative_recommenders[0].NumIter + 1; it <= max_iter; it++)
            {
                Parallel.For(0, (int) split.NumberOfFolds, i =>
                {
                    try
                    {
                        iterative_recommenders[i].Iterate();

                        if (it % find_iter == 0)
                        {
                            fold_results[i] = Ratings.Evaluate(split_recommenders[i], split.Test[i]);
                            if (show_fold_results)
                                Console.Error.WriteLine("fold {0} {1} iteration {2}", i, fold_results[i], it);
                        }
                    }
                    catch (Exception e)
                    {
                        Console.Error.WriteLine("===> ERROR: " + e.Message + e.StackTrace);
                        throw e;
                    }
                });
                Console.WriteLine("{0} iteration {1}", new RatingPredictionEvaluationResults(fold_results), it);
            }
        }
		/// <summary>Evaluate an iterative recommender on the folds of a dataset split, display results on STDOUT</summary>
		/// <param name="recommender">an item recommender</param>
		/// <param name="split">a positive-only feedback dataset split</param>
		/// <param name="test_users">a collection of integers with all test users</param>
		/// <param name="candidate_items">a collection of integers with all candidate items</param>
		/// <param name="candidate_item_mode">the mode used to determine the candidate items</param>
		/// <param name="repeated_events">allow repeated events in the evaluation (i.e. items accessed by a user before may be in the recommended list)</param>
		/// <param name="max_iter">the maximum number of iterations</param>
		/// <param name="find_iter">the report interval</param>
		/// <param name="show_fold_results">if set to true to print per-fold results to STDERR</param>
		static public void DoRatingBasedRankingIterativeCrossValidation(
			this RatingPredictor recommender,
			ISplit<IRatings> split,
			IList<int> test_users,
			IList<int> candidate_items,
			CandidateItems candidate_item_mode,
			RepeatedEvents repeated_events,
			uint max_iter,
			uint find_iter = 1,
			bool show_fold_results = false)
		{
			if (!(recommender is IIterativeModel))
				throw new ArgumentException("recommender must be of type IIterativeModel");

			var split_recommenders     = new RatingPredictor[split.NumberOfFolds];
			var iterative_recommenders = new IIterativeModel[split.NumberOfFolds];
			var fold_results = new ItemRecommendationEvaluationResults[split.NumberOfFolds];

			// initial training and evaluation
			Parallel.For(0, (int) split.NumberOfFolds, i =>
			{
				try
				{
					split_recommenders[i] = (RatingPredictor) recommender.Clone(); // to avoid changes in recommender
					split_recommenders[i].Ratings = split.Train[i];
					split_recommenders[i].Train();
					iterative_recommenders[i] = (IIterativeModel) split_recommenders[i];
					
					var test_data_posonly = new PosOnlyFeedback<SparseBooleanMatrix>(split.Test[i]);
					var training_data_posonly = new PosOnlyFeedback<SparseBooleanMatrix>(split.Train[i]);
					fold_results[i] = Items.Evaluate(split_recommenders[i], test_data_posonly, training_data_posonly, test_users, candidate_items, candidate_item_mode, repeated_events);
					if (show_fold_results)
						Console.WriteLine("fold {0} {1} iteration {2}", i, fold_results, iterative_recommenders[i].NumIter);
				}
				catch (Exception e)
				{
					Console.Error.WriteLine("===> ERROR: " + e.Message + e.StackTrace);
					throw;
				}
			});
			Console.WriteLine("{0} iteration {1}", new ItemRecommendationEvaluationResults(fold_results), iterative_recommenders[0].NumIter);

			// iterative training and evaluation
			for (int it = (int) iterative_recommenders[0].NumIter + 1; it <= max_iter; it++)
			{
				Parallel.For(0, (int) split.NumberOfFolds, i =>
				{
					try
					{
						iterative_recommenders[i].Iterate();

						if (it % find_iter == 0)
						{
							var test_data_posonly = new PosOnlyFeedback<SparseBooleanMatrix>(split.Test[i]);
							var training_data_posonly = new PosOnlyFeedback<SparseBooleanMatrix>(split.Train[i]);

							fold_results[i] = Items.Evaluate(split_recommenders[i], test_data_posonly, training_data_posonly, test_users, candidate_items, candidate_item_mode, repeated_events);
							if (show_fold_results)
								Console.WriteLine("fold {0} {1} iteration {2}", i, fold_results, it);
						}
					}
					catch (Exception e)
					{
						Console.Error.WriteLine("===> ERROR: " + e.Message + e.StackTrace);
						throw;
					}
				});
				Console.WriteLine("{0} iteration {1}", new ItemRecommendationEvaluationResults(fold_results), it);
			}
		}
Example #8
0
        /// <summary>Evaluate on the folds of a dataset split</summary>
        /// <param name="recommender">a rating predictor</param>
        /// <param name="split">a rating dataset split</param>
        /// <param name="show_results">set to true to print results to STDERR</param>
        /// <returns>a dictionary containing the average results over the different folds of the split</returns>
        public static Dictionary<string, double> EvaluateOnSplit(RatingPredictor recommender, ISplit<IRatings> split, bool show_results)
        {
            var avg_results = new Dictionary<string, double>();
            foreach (var key in Measures)
                avg_results[key] = 0;

            for (int i = 0; i < split.NumberOfFolds; i++)
            {
                var split_recommender = (RatingPredictor) recommender.Clone(); // to avoid changes in recommender
                split_recommender.Ratings = split.Train[i];
                split_recommender.Train();
                var fold_results = Evaluate(split_recommender, split.Test[i]);

                foreach (var key in fold_results.Keys)
                    avg_results[key] += fold_results[key];
                if (show_results)
                    Console.Error.WriteLine("fold {0}, RMSE {1,0:0.#####}, MAE {2,0:0.#####}", i, fold_results["RMSE"].ToString(CultureInfo.InvariantCulture), fold_results["MAE"].ToString(CultureInfo.InvariantCulture));
            }

            foreach (var key in avg_results.Keys.ToList())
                avg_results[key] /= split.NumberOfFolds;

            return avg_results;
        }
Example #9
0
 /// <summary>Evaluate on the folds of a dataset split</summary>
 /// <param name="recommender">a rating predictor</param>
 /// <param name="split">a rating dataset split</param>
 /// <returns>a dictionary containing the average results over the different folds of the split</returns>
 public static Dictionary<string, double> EvaluateOnSplit(RatingPredictor recommender, ISplit<IRatings> split)
 {
     return EvaluateOnSplit(recommender, split, false);
 }