Skip to content
Browse files

Implemented a way to save and load SVD models.

  • Loading branch information...
1 parent 78492c2 commit b05b8c6780c2750958d5aecd826fc9b2db84bc5c @gligoran committed Aug 28, 2011
View
5 RecommendationSystem.Data/RatingProvider.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Globalization;
using System.IO;
using System.Linq;
using RecommendationSystem.Entities;
@@ -30,7 +31,7 @@ public static List<IRating> ImportFromDataset(string filename, List<string> user
ratings.Add(new Rating(
userIndex,
artists.BinarySearch(parts[2]),
- float.Parse(parts[3])
+ float.Parse(parts[3], CultureInfo.CurrentCulture)
));
}
@@ -51,7 +52,7 @@ public static List<IRating> Load(string filename, int limit = int.MaxValue)
while ((line = reader.ReadLine()) != null && limit > 0)
{
var parts = line.Split(sep, StringSplitOptions.None);
- ratings.Add(new Rating(int.Parse(parts[0]), int.Parse(parts[1]), float.Parse(parts[2])));
+ ratings.Add(new Rating(int.Parse(parts[0]), int.Parse(parts[1]), float.Parse(parts[2], CultureInfo.CurrentCulture)));
limit--;
}
View
16 RecommendationSystem.MatrixFactorization/Basic/Models/BasicSvdModel.cs
@@ -1,22 +1,22 @@
-using RecommendationSystem.MatrixFactorization.Models;
-using RecommendationSystem.MatrixFactorization.Training;
-
namespace RecommendationSystem.MatrixFactorization.Basic.Models
{
public class BasicSvdModel : IBasicSvdModel
{
public float[,] UserFeatures { get; set; }
public float[,] ArtistFeatures { get; set; }
- public TrainingParameters UserTrainingParameters { get; set; }
- internal BasicSvdModel()
- {}
+ public int FeatureCount
+ {
+ get { return UserFeatures.GetUpperBound(0) + 1; }
+ }
- public BasicSvdModel(float[,] userFeatures, float[,] artistFeatures, TrainingParameters trainingParameters)
+ public BasicSvdModel(float[,] userFeatures, float[,] artistFeatures)
{
UserFeatures = userFeatures;
ArtistFeatures = artistFeatures;
- UserTrainingParameters = trainingParameters;
}
+
+ internal BasicSvdModel()
+ {}
}
}
View
5 RecommendationSystem.MatrixFactorization/Basic/Recommendations/BasicSvdRecommender.cs
@@ -1,3 +1,4 @@
+using System;
using System.Collections.Generic;
using RecommendationSystem.Entities;
using RecommendationSystem.MatrixFactorization.Basic.Models;
@@ -9,12 +10,12 @@ public class BasicSvdRecommender : IRecommender<IBasicSvdModel>
{
public float PredictRatingForArtist(IUser user, IBasicSvdModel model, List<IArtist> artists, int artist)
{
- throw new System.NotImplementedException();
+ throw new NotImplementedException();
}
public IEnumerable<IRecommendation> GenerateRecommendations(IUser user, IBasicSvdModel model, List<IArtist> artists)
{
- throw new System.NotImplementedException();
+ throw new NotImplementedException();
}
}
}
View
15 RecommendationSystem.MatrixFactorization/Basic/Training/BasicSvdTrainer.cs
@@ -7,16 +7,19 @@ namespace RecommendationSystem.MatrixFactorization.Basic.Training
{
public class BasicSvdTrainer : SvdTrainerBase<IBasicSvdModel>
{
- public override IBasicSvdModel TrainModel(List<string> users, List<string> artists, List<IRating> ratings, TrainingParameters trainingParameters)
+ protected override IBasicSvdModel InitializeNewModel(List<string> users, List<string> artists, List<IRating> ratings)
{
- var model = new BasicSvdModel();
- CalculateFeatures(model, trainingParameters);
- return model;
+ return new BasicSvdModel();
}
- protected override float PredictRatingUsingResiduals(IBasicSvdModel model, int rating, int feature)
+ protected override float PredictRatingUsingResiduals(IBasicSvdModel model, int rating, int feature, List<IRating> ratings)
{
- return ResidualRatingValues[rating] + model.UserFeatures[feature, Ratings[rating].UserIndex] * model.ArtistFeatures[feature, Ratings[rating].ArtistIndex];
+ return ResidualRatingValues[rating] + model.UserFeatures[feature, ratings[rating].UserIndex] * model.ArtistFeatures[feature, ratings[rating].ArtistIndex];
+ }
+
+ protected override IBasicSvdModel GetNewModel()
+ {
+ return new BasicSvdModel();
}
}
}
View
6 RecommendationSystem.MatrixFactorization/Bias/BiasSvdRecommendationSystem.cs
@@ -7,7 +7,7 @@
namespace RecommendationSystem.MatrixFactorization.Bias
{
- class BiasSvdRecommendationSystem : ISvdRecommendationSystem<IBiasSvdModel>
+ internal class BiasSvdRecommendationSystem : ISvdRecommendationSystem<IBiasSvdModel>
{
public ITrainer<IBiasSvdModel, IUser> Trainer { get; set; }
public IRecommender<IBiasSvdModel> Recommender { get; set; }
@@ -20,11 +20,11 @@ public BiasSvdRecommendationSystem()
public BiasSvdRecommendationSystem(ITrainer<IBiasSvdModel, IUser> trainer)
: this(trainer, new BiasSvdRecommender())
- { }
+ {}
public BiasSvdRecommendationSystem(IRecommender<IBiasSvdModel> recommender)
: this(new BiasSvdTrainer(), recommender)
- { }
+ {}
public BiasSvdRecommendationSystem(ITrainer<IBiasSvdModel, IUser> trainer, IRecommender<IBiasSvdModel> recommender)
{
View
22 RecommendationSystem.MatrixFactorization/Bias/Models/BiasSvdModel.cs
@@ -1,4 +1,3 @@
-using RecommendationSystem.MatrixFactorization.Models;
using RecommendationSystem.MatrixFactorization.Training;
namespace RecommendationSystem.MatrixFactorization.Bias.Models
@@ -7,24 +6,33 @@ public class BiasSvdModel : IBiasSvdModel
{
public float[,] UserFeatures { get; set; }
public float[,] ArtistFeatures { get; set; }
+
+ public int FeatureCount
+ {
+ get { return UserFeatures.GetUpperBound(0) + 1; }
+ }
+
public float GlobalAverage { get; set; }
public float[] UserBias { get; set; }
public float[] ArtistBias { get; set; }
- public TrainingParameters TrainingParameters { get; set; }
- public BiasSvdModel(TrainingParameters trainingParameters)
- {
- TrainingParameters = trainingParameters;
- }
+ internal BiasSvdModel()
+ {}
public BiasSvdModel(float[,] userFeatures, float[,] artistFeatures, float globalAverage, float[] userBias, float[] artistBias, TrainingParameters trainingParameters)
- : this(trainingParameters)
{
UserFeatures = userFeatures;
ArtistFeatures = artistFeatures;
GlobalAverage = globalAverage;
UserBias = userBias;
ArtistBias = artistBias;
}
+
+ public BiasSvdModel(float globalAverage, float[] userBias, float[] artistBias)
+ {
+ GlobalAverage = globalAverage;
+ UserBias = userBias;
+ ArtistBias = artistBias;
+ }
}
}
View
5 RecommendationSystem.MatrixFactorization/Bias/Recommendations/BiasSvdRecommender.cs
@@ -1,3 +1,4 @@
+using System;
using System.Collections.Generic;
using RecommendationSystem.Entities;
using RecommendationSystem.MatrixFactorization.Bias.Models;
@@ -9,12 +10,12 @@ public class BiasSvdRecommender : IRecommender<IBiasSvdModel>
{
public float PredictRatingForArtist(IUser user, IBiasSvdModel model, List<IArtist> artists, int artist)
{
- throw new System.NotImplementedException();
+ throw new NotImplementedException();
}
public IEnumerable<IRecommendation> GenerateRecommendations(IUser user, IBiasSvdModel model, List<IArtist> artists)
{
- throw new System.NotImplementedException();
+ throw new NotImplementedException();
}
}
}
View
116 RecommendationSystem.MatrixFactorization/Bias/Training/BiasSvdTrainer.cs
@@ -1,4 +1,8 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.IO;
+using System.Linq;
using RecommendationSystem.Entities;
using RecommendationSystem.MatrixFactorization.Bias.Models;
using RecommendationSystem.MatrixFactorization.Training;
@@ -7,37 +11,35 @@ namespace RecommendationSystem.MatrixFactorization.Bias.Training
{
public class BiasSvdTrainer : SvdTrainerBase<IBiasSvdModel>
{
- public override IBiasSvdModel TrainModel(List<string> users, List<string> artists, List<IRating> ratings, TrainingParameters trainingParameters)
+ protected override IBiasSvdModel InitializeNewModel(List<string> users, List<string> artists, List<IRating> ratings)
{
- var model = new BiasSvdModel(trainingParameters);
- ComputeBiases(model);
- CalculateFeatures(model, trainingParameters);
+ var model = new BiasSvdModel();
+ ComputeGlobalAverageAndBiases(model, users, artists, ratings);
return model;
}
- protected override float PredictRatingUsingResiduals(IBiasSvdModel model, int rating, int feature)
+ #region PredictRatingUsingResiduals
+ protected override float PredictRatingUsingResiduals(IBiasSvdModel model, int rating, int feature, List<IRating> ratings)
{
return ResidualRatingValues[rating] +
- model.UserFeatures[feature, Ratings[rating].UserIndex] * model.ArtistFeatures[feature, Ratings[rating].ArtistIndex] +
+ model.UserFeatures[feature, ratings[rating].UserIndex] * model.ArtistFeatures[feature, ratings[rating].ArtistIndex] +
model.GlobalAverage +
- model.UserBias[Ratings[rating].UserIndex] +
- model.ArtistBias[Ratings[rating].ArtistIndex];
+ model.UserBias[ratings[rating].UserIndex] +
+ model.ArtistBias[ratings[rating].ArtistIndex];
}
+ #endregion
- private void ComputeBiases(IBiasSvdModel model)
+ #region ComputeGlobalAverageAndBiases
+ private void ComputeGlobalAverageAndBiases(IBiasSvdModel model, List<string> users, List<string> artists, List<IRating> ratings)
{
- model.GlobalAverage = 0.0f;
- model.UserBias = new float[Users.Count];
- model.ArtistBias = new float[Artists.Count];
+ model.UserBias = new float[users.Count];
+ model.ArtistBias = new float[artists.Count];
- foreach (var rating in Ratings)
- model.GlobalAverage += rating.Value;
+ model.GlobalAverage = ratings.Average(rating => rating.Value);
- model.GlobalAverage /= Ratings.Count;
-
- var userCount = new int[Users.Count];
- var artistCount = new int[Artists.Count];
- foreach (var rating in Ratings)
+ var userCount = new int[users.Count];
+ var artistCount = new int[artists.Count];
+ foreach (var rating in ratings)
{
var d = rating.Value - model.GlobalAverage;
@@ -54,5 +56,79 @@ private void ComputeBiases(IBiasSvdModel model)
for (var i = 0; i < model.ArtistBias.Length; i++)
model.ArtistBias[i] /= artistCount[i];
}
+ #endregion
+
+ #region SaveModel
+ protected override void SaveProperties(IBiasSvdModel model, TextWriter writer)
+ {
+ base.SaveProperties(model, writer);
+
+ writer.WriteLine("GlobalAverage={0}", model.GlobalAverage);
+ }
+
+ protected override void SaveData(IBiasSvdModel model, TextWriter writer)
+ {
+ base.SaveData(model, writer);
+
+ SaveBiases(writer, model.UserBias);
+ SaveBiases(writer, model.ArtistBias);
+ }
+
+ private static void SaveBiases(TextWriter writer, float[] biases)
+ {
+ for (var i = 0; i < biases.Length; i++)
+ {
+ if (i != 0)
+ writer.Write("\t");
+
+ writer.Write(biases[i]);
+ }
+ writer.WriteLine();
+ }
+ #endregion
+
+ #region LoadModel
+ protected override IBiasSvdModel GetNewModel()
+ {
+ return new BiasSvdModel();
+ }
+
+ protected override void LoadProperties(IBiasSvdModel model, TextReader reader)
+ {
+ base.LoadProperties(model, reader);
+
+ //get global average
+ var line = reader.ReadLine();
+ if (line == null)
+ throw new ArgumentException("File {0} is not a valide SvdModel.");
+ model.GlobalAverage = float.Parse(line.Split(new[] {'='}, StringSplitOptions.None)[1], CultureInfo.CurrentCulture);
+
+ model.UserBias = new float[model.UserFeatures.GetUpperBound(1) + 1];
+ model.ArtistBias = new float[model.ArtistFeatures.GetUpperBound(1) + 1];
+ }
+
+ protected override void LoadData(IBiasSvdModel model, TextReader reader)
+ {
+ base.LoadData(model, reader);
+
+ FillBiases(model.UserBias, reader);
+ FillBiases(model.ArtistBias, reader);
+ }
+
+ private void FillBiases(float[] biases, TextReader reader)
+ {
+ var sep = new[] {'\t'};
+ var line = reader.ReadLine();
+ if (line == null)
+ throw new ArgumentException("File is not a valid SvdModel.");
+
+ var factors = line.Split(sep, StringSplitOptions.None);
+ if (factors.Length != biases.Length)
+ throw new ArgumentException("File is not a valid SvdModel.");
+
+ for (var i = 0; i < factors.Length; i++)
+ biases[i] = float.Parse(factors[i], CultureInfo.CurrentCulture);
+ }
+ #endregion
}
}
View
6 RecommendationSystem.MatrixFactorization/ISvdRecommendationSystem.cs
@@ -7,7 +7,5 @@ namespace RecommendationSystem.MatrixFactorization
{
public interface ISvdRecommendationSystem<TSvdModel> : IRecommendationSystem<TSvdModel, IUser, ITrainer<TSvdModel, IUser>, IRecommender<TSvdModel>>
where TSvdModel : ISvdModel
- {
-
- }
-}
+ {}
+}
View
1 RecommendationSystem.MatrixFactorization/Models/ISvdModel.cs
@@ -6,5 +6,6 @@ public interface ISvdModel : IModel
{
float[,] UserFeatures { get; set; }
float[,] ArtistFeatures { get; set; }
+ int FeatureCount { get; }
}
}
View
7 RecommendationSystem.MatrixFactorization/Training/ISvdTrainer.cs
@@ -4,7 +4,10 @@
namespace RecommendationSystem.MatrixFactorization.Training
{
- public interface ISvdTrainer<out TSvdModel> : ITrainer<TSvdModel, IUser>
+ public interface ISvdTrainer<TSvdModel> : ITrainer<TSvdModel, IUser>
where TSvdModel : ISvdModel
- {}
+ {
+ void SaveModel(string filename, TSvdModel model);
+ TSvdModel LoadModel(string filename);
+ }
}
View
209 RecommendationSystem.MatrixFactorization/Training/SvdTrainerBase.cs
@@ -1,5 +1,7 @@
using System;
using System.Collections.Generic;
+using System.Globalization;
+using System.IO;
using System.Linq;
using RecommendationSystem.Data;
using RecommendationSystem.Entities;
@@ -10,14 +12,14 @@ namespace RecommendationSystem.MatrixFactorization.Training
public abstract class SvdTrainerBase<TSvdModel> : ISvdTrainer<TSvdModel>
where TSvdModel : ISvdModel
{
+ #region Properties
protected float[] ResidualRatingValues;
- protected List<string> Users { get; set; }
- protected List<string> Artists { get; set; }
- protected List<IRating> Ratings { get; set; }
private float rmsePrev = float.MaxValue;
private float rmse = float.MaxValue;
+ #endregion
+ #region TrainModel
public TSvdModel TrainModel(List<IUser> users, List<IArtist> artists, List<IRating> ratings)
{
return TrainModel(users, artists, ratings, new TrainingParameters());
@@ -28,15 +30,24 @@ public TSvdModel TrainModel(IEnumerable<IUser> users, IEnumerable<IArtist> artis
return TrainModel(users.GetLookupTable(), artists.GetLookupTable(), ratings, trainingParameters);
}
- public abstract TSvdModel TrainModel(List<string> users, List<string> artists, List<IRating> ratings, TrainingParameters trainingParameters);
+ public TSvdModel TrainModel(List<string> users, List<string> artists, List<IRating> ratings, TrainingParameters trainingParameters)
+ {
+ var model = InitializeNewModel(users, artists, ratings);
+ CalculateFeatures(model, users, artists, ratings, trainingParameters);
+ return model;
+ }
- protected void CalculateFeatures(TSvdModel model, TrainingParameters trainingParameters)
+ protected abstract TSvdModel InitializeNewModel(List<string> users, List<string> artists, List<IRating> ratings);
+ #endregion
+
+ #region CalculateFeatures
+ protected void CalculateFeatures(TSvdModel model, List<string> users, List<string> artists, List<IRating> ratings, TrainingParameters trainingParameters)
{
//init
- model.UserFeatures = new float[trainingParameters.FeatureCount,Users.Count];
- model.ArtistFeatures = new float[trainingParameters.FeatureCount,Artists.Count];
+ model.UserFeatures = new float[trainingParameters.FeatureCount,users.Count];
+ model.ArtistFeatures = new float[trainingParameters.FeatureCount,artists.Count];
- ResidualRatingValues = new float[Ratings.Count];
+ ResidualRatingValues = new float[ratings.Count];
model.UserFeatures.Populate(0.1f);
model.ArtistFeatures.Populate(0.1f);
@@ -50,55 +61,193 @@ protected void CalculateFeatures(TSvdModel model, TrainingParameters trainingPar
Console.WriteLine("Training feature {0}", f);
#endif
- ConvergeFeature(model, f, trainingParameters);
- CacheResidualRatings(model, f);
+ ConvergeFeature(model, f, ratings, trainingParameters);
+ CacheResidualRatings(model, f, ratings);
}
}
+ #endregion
- private void ConvergeFeature(TSvdModel model, int f, TrainingParameters trainingParameters)
+ #region ConvergeFeature
+ private void ConvergeFeature(TSvdModel model, int f, List<IRating> ratings, TrainingParameters trainingParameters)
{
var count = 0;
- var rmseDiff = float.MaxValue;
+ var rmseImprovment = float.MaxValue;
- while (count < trainingParameters.EpochLimit && rmseDiff > -0.1f)
+ while ((rmseImprovment > trainingParameters.RmseImprovementTreshold || count > trainingParameters.MinEpochTreshold) && count < trainingParameters.MaxEpochTreshold)
{
rmsePrev = rmse;
- rmse = TrainFeature(model, f, trainingParameters);
- rmseDiff = rmsePrev - rmse;
+ rmse = TrainFeature(model, f, ratings, trainingParameters);
+ rmseImprovment = Math.Abs(rmse - rmsePrev) / (rmse + rmsePrev);
-#if DEBUG
count++;
- Console.WriteLine("Pass {0}/{1}:\trmse = {2}\trmseDiff = {3}", f, count, rmse, rmseDiff);
-#endif
+ Console.WriteLine("Pass {0}/{1}:\trmse = {2}\trmseImpr = {3}", f, count, rmse, rmseImprovment);
}
rmsePrev = rmse;
}
+ #endregion
- protected float TrainFeature(TSvdModel model, int f, TrainingParameters trainingParameters)
+ #region TrainFeatures
+ protected float TrainFeature(TSvdModel model, int f, List<IRating> ratings, TrainingParameters trainingParameters)
{
- var e = Ratings.Select((r, i) => TrainSample(model, i, f, trainingParameters)).Sum() / Ratings.Count;
+ var e = ratings.Select((r, i) => TrainSample(model, i, f, ratings, trainingParameters)).Sum() / ratings.Count;
return (float)Math.Sqrt(e);
}
+ #endregion
- protected float TrainSample(TSvdModel model, int r, int f, TrainingParameters trainingParameters)
+ #region TrainSample
+ protected float TrainSample(TSvdModel model, int r, int f, List<IRating> ratings, TrainingParameters trainingParameters)
{
- var e = Ratings[r].Value - PredictRatingUsingResiduals(model, r, f);
- var uv = model.UserFeatures[f, Ratings[r].UserIndex];
+ var e = ratings[r].Value - PredictRatingUsingResiduals(model, r, f, ratings);
+ var uv = model.UserFeatures[f, ratings[r].UserIndex];
- model.UserFeatures[f, Ratings[r].UserIndex] += trainingParameters.LRate *
- (e * model.ArtistFeatures[f, Ratings[r].ArtistIndex] - trainingParameters.K * model.UserFeatures[f, Ratings[r].UserIndex]);
- model.ArtistFeatures[f, Ratings[r].ArtistIndex] += trainingParameters.LRate * (e * uv - trainingParameters.K * model.ArtistFeatures[f, Ratings[r].ArtistIndex]);
+ model.UserFeatures[f, ratings[r].UserIndex] += trainingParameters.LRate *
+ (e * model.ArtistFeatures[f, ratings[r].ArtistIndex] - trainingParameters.K * model.UserFeatures[f, ratings[r].UserIndex]);
+ model.ArtistFeatures[f, ratings[r].ArtistIndex] += trainingParameters.LRate * (e * uv - trainingParameters.K * model.ArtistFeatures[f, ratings[r].ArtistIndex]);
return e * e;
}
+ #endregion
+
+ #region PredictRatingUsingResiduals
+ protected abstract float PredictRatingUsingResiduals(TSvdModel model, int rating, int feature, List<IRating> ratings);
+ #endregion
+
+ #region CacheResidualRatings
+ private void CacheResidualRatings(TSvdModel model, int f, List<IRating> ratings)
+ {
+ for (var i = 0; i < ratings.Count; i++)
+ ResidualRatingValues[i] += model.UserFeatures[f, ratings[i].UserIndex] * model.ArtistFeatures[f, ratings[i].ArtistIndex];
+ }
+ #endregion
+
+ #region SaveModel
+ public void SaveModel(string filename, TSvdModel model)
+ {
+ if (model.UserFeatures == null || model.ArtistFeatures == null)
+ return;
+
+ var writer = GetWriter(filename);
+
+ SaveProperties(model, writer);
+ SaveData(model, writer);
+
+ EndSaving(writer);
+ }
+
+ private TextWriter GetWriter(string filename)
+ {
+ var dir = Path.GetDirectoryName(filename);
+ if (dir != null && !Directory.Exists(dir))
+ Directory.CreateDirectory(dir);
+
+ TextWriter writer = new StreamWriter(filename);
+ return writer;
+ }
+
+ protected virtual void SaveProperties(TSvdModel model, TextWriter writer)
+ {
+ writer.WriteLine("FeatureCount={0}", model.UserFeatures.GetUpperBound(0) + 1);
+ writer.WriteLine("UserCount={0}", model.UserFeatures.GetUpperBound(1) + 1);
+ writer.WriteLine("ArtistCount={0}", model.ArtistFeatures.GetUpperBound(1) + 1);
+ }
+
+ protected virtual void SaveData(TSvdModel model, TextWriter writer)
+ {
+ SaveFeatures(writer, model.UserFeatures);
+ SaveFeatures(writer, model.ArtistFeatures);
+ }
+
+ private void SaveFeatures(TextWriter writer, float[,] features)
+ {
+ for (var i = 0; i <= features.GetUpperBound(0); i++)
+ {
+ for (var j = 0; j <= features.GetUpperBound(1); j++)
+ {
+ if (j != 0)
+ writer.Write("\t");
+
+ writer.Write(features[i, j]);
+ }
+ writer.WriteLine();
+ }
+ }
+
+ private void EndSaving(TextWriter writer)
+ {
+ writer.Flush();
+ writer.Close();
+ }
+ #endregion
+
+ #region LoadModel
+ public TSvdModel LoadModel(string filename)
+ {
+ TextReader reader = new StreamReader(filename);
+
+ var model = GetNewModel();
+ LoadProperties(model, reader);
+ LoadData(model, reader);
+
+ EndLoadingModel(reader);
+
+ return model;
+ }
- protected abstract float PredictRatingUsingResiduals(TSvdModel model, int rating, int feature);
+ protected abstract TSvdModel GetNewModel();
+
+ protected virtual void LoadProperties(TSvdModel model, TextReader reader)
+ {
+ //get feature count
+ var line = reader.ReadLine();
+ if (line == null)
+ throw new ArgumentException("File is not a valide SvdModel.");
+ var featureCount = int.Parse(line.Split(new[] {'='}, StringSplitOptions.None)[1]);
+
+ //get user count
+ line = reader.ReadLine();
+ if (line == null)
+ throw new ArgumentException("File {0} is not a valide SvdModel.");
+ var userCount = int.Parse(line.Split(new[] {'='}, StringSplitOptions.None)[1]);
+
+ //get artist count
+ line = reader.ReadLine();
+ if (line == null)
+ throw new ArgumentException("File {0} is not a valide SvdModel.");
+ var artistCount = int.Parse(line.Split(new[] {'='}, StringSplitOptions.None)[1]);
+
+ model.UserFeatures = new float[featureCount,userCount];
+ model.ArtistFeatures = new float[featureCount,artistCount];
+ }
+
+ protected virtual void LoadData(TSvdModel model, TextReader reader)
+ {
+ FillFeatures(model.UserFeatures, reader);
+ FillFeatures(model.ArtistFeatures, reader);
+ }
+
+ private void FillFeatures(float[,] features, TextReader reader)
+ {
+ var sep = new[] {"\t"};
+ for (var i = 0; i <= features.GetUpperBound(0); i++)
+ {
+ var line = reader.ReadLine();
+ if (line == null)
+ throw new ArgumentException("File is not a valid SvdModel.");
+
+ var factors = line.Split(sep, StringSplitOptions.None);
+ if (factors.Length != features.GetUpperBound(1) + 1)
+ throw new ArgumentException("File is not a valid SvdModel.");
+
+ for (var j = 0; j < factors.Length; j++)
+ features[i, j] = float.Parse(factors[j], CultureInfo.CurrentCulture);
+ }
+ }
- private void CacheResidualRatings(TSvdModel model, int f)
+ private void EndLoadingModel(TextReader reader)
{
- for (var i = 0; i < Ratings.Count; i++)
- ResidualRatingValues[i] += model.UserFeatures[f, Ratings[i].UserIndex] * model.ArtistFeatures[f, Ratings[i].ArtistIndex];
+ reader.Close();
}
+ #endregion
}
}
View
14 RecommendationSystem.MatrixFactorization/Training/TrainingParameters.cs
@@ -2,21 +2,21 @@
{
public class TrainingParameters
{
- public float EpochLimit { get; set; }
- public float RmseDiffLimit { get; set; }
+ public float MinEpochTreshold { get; set; }
+ public float MaxEpochTreshold { get; set; }
+ public double RmseImprovementTreshold { get; set; }
public float K { get; set; }
public float LRate { get; set; }
public int FeatureCount { get; set; }
- public TrainingParameters(int featureCount = 100, float lRate = 0.001f, float k = 0.02f, float rmseDiffLimit = 0.00001f, float epochLimit = 100)
+ public TrainingParameters(int featureCount = 100, float lRate = 0.001f, float k = 0.02f, float rmseImprovementTreshold = 0.0001f, int minEpochTreshold = 120, int maxEpochTreshold = 200)
{
FeatureCount = featureCount;
LRate = lRate;
K = k;
- RmseDiffLimit = rmseDiffLimit;
- EpochLimit = epochLimit;
+ RmseImprovementTreshold = rmseImprovementTreshold;
+ MinEpochTreshold = minEpochTreshold;
+ MaxEpochTreshold = maxEpochTreshold;
}
-
- public static TrainingParameters DefaultTrainingParameters = new TrainingParameters(25, 0.001f, 0.02f, 0.00001f, 70);
}
}
View
22 RecommendationSystem.QualityTesting/Program.cs
@@ -8,6 +8,8 @@
using RecommendationSystem.Knn.Recommendations;
using RecommendationSystem.Knn.Similarity;
using RecommendationSystem.Knn.Training;
+using RecommendationSystem.MatrixFactorization.Bias.Training;
+using RecommendationSystem.MatrixFactorization.Training;
using RecommendationSystem.QualityTesting.Testers;
namespace RecommendationSystem.QualityTesting
@@ -83,9 +85,9 @@ public static void Main(string[] args)
}
}
- var trainer = new KnnTrainer();
+ var knnTrainer = new KnnTrainer();
timer.Restart();
- var knnModel = trainer.TrainModel(trainUsers, artists, trainRatings);
+ var knnModel = knnTrainer.TrainModel(trainUsers, artists, trainRatings);
timer.Stop();
Console.WriteLine("Model trained in {0}ms.", timer.ElapsedMilliseconds);
@@ -104,7 +106,7 @@ public static void Main(string[] args)
Ra = ra,
TestUsers = testUsers,
KnnModel = knnModel,
- Trainer = trainer,
+ Trainer = knnTrainer,
Artists = artists,
NumberOfTests = numberOfTests
};
@@ -120,7 +122,7 @@ public static void Main(string[] args)
Ra = ra,
TestUsers = testUsers,
KnnModel = knnModel,
- Trainer = trainer,
+ Trainer = knnTrainer,
Artists = artists,
NumberOfTests = numberOfTests
};
@@ -133,17 +135,21 @@ public static void Main(string[] args)
break;
case "svd":
case "mf":
- Console.WriteLine("Matrix factorization (SVD) tests are not yet implemented.");
+ var svdTrainer = new BiasSvdTrainer();
+ var model = svdTrainer.TrainModel(trainUsers, artists, trainRatings, new TrainingParameters(2, rmseImprovementTreshold: 1f, minEpochTreshold: 1, maxEpochTreshold: 2));
+ svdTrainer.SaveModel(@"D:\Dataset\models\svdmodel.rs", model);
+ model = svdTrainer.LoadModel(@"D:\Dataset\models\svdmodel.rs");
+
break;
}
-
- if (args.Where(arg => arg.ToLower() == "-wait").Count() != 0)
- Console.ReadLine();
}
catch (Exception e)
{
Console.WriteLine("{0}{1}{1}{2}", e, Environment.NewLine, e.Message);
}
+
+ if (args.Where(arg => arg.ToLower() == "-wait").Count() != 0)
+ Console.ReadLine();
}
#region LoadData
View
2 RecommendationSystem.QualityTesting/Testers/SimpleTester.cs
@@ -35,7 +35,7 @@ public override void Test()
try
{
- var rss = new[] { "ar", "mr", "mcr" };
+ var rss = new[] {"ar", "mr", "mcr"};
var test = Parallel.ForEach(rss, rs =>
{
switch (rs)

0 comments on commit b05b8c6

Please sign in to comment.
Something went wrong with that request. Please try again.