Skip to content

Commit

Permalink
Added threading support for running multiple tests at the same time. …
Browse files Browse the repository at this point in the history
…This is using SmartThreadPool.dll.
  • Loading branch information
gligoran committed Aug 25, 2011
1 parent 368cf26 commit 2208b2e
Show file tree
Hide file tree
Showing 5 changed files with 200 additions and 82 deletions.
134 changes: 134 additions & 0 deletions RecommendationSystem.QualityTesting/KnnTester.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using RecommendationSystem.Entities;
using RecommendationSystem.Knn;
using RecommendationSystem.Knn.Models;
using RecommendationSystem.Knn.RatingAggregation;
using RecommendationSystem.Knn.Similarity;
using RecommendationSystem.Models;
using RecommendationSystem.Recommendations;
using RecommendationSystem.Training;

namespace RecommendationSystem.QualityTesting
{
public class KnnTester<TRecommender>
where TRecommender : IRecommender<IKnnModel>
{
private readonly Stopwatch timer = new Stopwatch();
private CryptoRandom rng = new CryptoRandom();
private TextWriter fileWriter;

public int K { get; set; }
public ISimilarityEstimator Sim { get; set; }
public IRatingAggregator Ra { get; set; }
public List<IUser> TestUsers { get; set; }
public IKnnModel KnnModel { get; set; }
public ITrainer<IKnnModel, IUser> Trainer { get; set; }
public List<IArtist> Artists { get; set; }
public int NumberOfTests { get; set; }
public string TestName { get; set; }

public KnnTester()
{
}

#region KnnTest
public void KnnTest()
{
var recommender = (TRecommender)Activator.CreateInstance(typeof(TRecommender), new object[] { Sim, Ra, K });
TestName = string.Format("Knn-K{0}-{1}-{2}-{3}-T{4}", K, Sim, Ra, recommender, NumberOfTests);

InitializeResultWriter(String.Format(@"D:\Dataset\results\{0}.txt", TestName));
Write(string.Format("Test {0} ({1})", TestName, DateTime.Now));
Write("------------------------------------------------------", false);

var rs = new KnnRecommendationSystem(Trainer, recommender);
timer.Restart();
var rv = TestRecommendationSystem(rs);
timer.Stop();

Write("------------------------------------------------------", false);
Write(string.Format("RMSE({0}) = {1} ({2}ms).", TestName, rv, timer.ElapsedMilliseconds));

fileWriter.Close();
}
#endregion

#region TestRecommendationSystem
private RmseAndVariance TestRecommendationSystem<TModel, TUser>(IRecommendationSystem<TModel, TUser, ITrainer<TModel, TUser>, IRecommender<TModel>> rs)
where TModel : IModel
where TUser : IUser
{
var onePrecent = NumberOfTests / 10;
var rmseList = new List<float>();
while (rmseList.Count < NumberOfTests)
{
var userIndex = rng.Next(TestUsers.Count);
var user = TestUsers[userIndex];
lock (user)
{
//if true we'll remove the only rating
if (user.Ratings.Count < 2)
continue;

var ratingIndex = rng.Next(user.Ratings.Count);
var rating = user.Ratings[ratingIndex];

var error = GerPredictionError(rs, rating, user);

rmseList.Add((float)Math.Sqrt(error * error));
}

if (rmseList.Count % onePrecent != 0)
continue;

Write(string.Format("Test {0} at {1} in {2}ms with RMSE {3} ({4})", TestName, rmseList.Count, timer.ElapsedMilliseconds, rmseList.Sum() / rmseList.Count, DateTime.Now));
}

var rv = new RmseAndVariance(rmseList);
return rv;
}
#endregion

#region GerPredictionError
private float GerPredictionError<TModel, TUser>(IRecommendationSystem<TModel, TUser, ITrainer<TModel, TUser>, IRecommender<TModel>> rs, IRating rating, IUser user)
where TModel : IModel
where TUser : IUser
{
var originalRatings = user.Ratings;
user.Ratings = user.Ratings.Where(r => r != rating).ToList();

var error = rating.Value - rs.Recommender.PredictRatingForArtist(user, (TModel)KnnModel, Artists, rating.ArtistIndex);

user.Ratings = originalRatings;
return error;
}
#endregion

#region SavingResults
private void InitializeResultWriter(string filename)
{
var dir = Path.GetDirectoryName(filename);
if (dir != null && !Directory.Exists(dir))
Directory.CreateDirectory(dir);

fileWriter = new StreamWriter(filename);
}

private void Write(string text, bool toConsole = true, bool toFile = true)
{
if (toConsole)
Console.WriteLine(text);

if (!toFile)
return;

fileWriter.WriteLine(text);
fileWriter.Flush();
}
#endregion
}
}
135 changes: 54 additions & 81 deletions RecommendationSystem.QualityTesting/Program.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using RecommendationSystem.Data;
using RecommendationSystem.Entities;
using RecommendationSystem.Knn;
using RecommendationSystem.Knn.Models;
using RecommendationSystem.Knn.RatingAggregation;
using RecommendationSystem.Knn.Recommendations;
using RecommendationSystem.Knn.Similarity;
Expand All @@ -17,14 +14,13 @@
using RecommendationSystem.Simple.MedianRating;
using RecommendationSystem.Simple.MostCommonRating;
using RecommendationSystem.Training;
using Amib.Threading;

namespace RecommendationSystem.QualityTesting
{
public static class Program
{
private static readonly Stopwatch timer = new Stopwatch();
private static CryptoRandom rng = new CryptoRandom();
private static TextWriter fileWriter;

public static void Main(string[] args)
{
Expand Down Expand Up @@ -80,13 +76,13 @@ public static void Main(string[] args)
#region Knn RS'
LoadData(out trainUsers, out trainRatings, out testUsers, out testRatings, out artists);

var ks = argList[argList.IndexOf("-k") + 1].ToLower().Split(new[] {','}, StringSplitOptions.RemoveEmptyEntries).Select(int.Parse).ToList();
var ks = argList[argList.IndexOf("-k") + 1].ToLower().Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(int.Parse).ToList();
var numberOfTests = int.Parse(argList[argList.IndexOf("-t") + 1].ToLower());
var performNoContentKnnTests = argList.IndexOf("-noco") >= 0;
var performContentKnnTests = argList.IndexOf("-co") >= 0;

var sims = new List<ISimilarityEstimator>();
var argSims = argList[argList.IndexOf("-sim") + 1].ToLower().Split(new[] {','}, StringSplitOptions.RemoveEmptyEntries);
var argSims = argList[argList.IndexOf("-sim") + 1].ToLower().Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
foreach (var argSim in argSims)
{
switch (argSim)
Expand All @@ -101,7 +97,7 @@ public static void Main(string[] args)
}

var ras = new List<IRatingAggregator>();
var argRas = argList[argList.IndexOf("-ra") + 1].ToLower().Split(new[] {','}, StringSplitOptions.RemoveEmptyEntries);
var argRas = argList[argList.IndexOf("-ra") + 1].ToLower().Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
foreach (var argRa in argRas)
{
switch (argRa)
Expand All @@ -124,22 +120,54 @@ public static void Main(string[] args)
timer.Stop();
Console.WriteLine("Model trained in {0}ms.", timer.ElapsedMilliseconds);

var stp = new SmartThreadPool();
//stp.MaxThreads = Environment.ProcessorCount;
foreach (var k in ks)
{
foreach (var sim in sims)
{
foreach (var ra in ras)
{
if (performNoContentKnnTests)
KnnTest<KnnRecommender>(k, sim, ra, testUsers, knnModel, trainer, artists, numberOfTests);
{
var tester = new KnnTester<KnnRecommender>
{
K = k,
Sim = sim,
Ra = ra,
TestUsers = testUsers,
KnnModel = knnModel,
Trainer = trainer,
Artists = artists,
NumberOfTests = numberOfTests
};
stp.QueueWorkItem(tester.KnnTest);
}

if (performContentKnnTests)
KnnTest<ContentKnnRecommender>(k, sim, ra, testUsers, knnModel, trainer, artists, numberOfTests);
{
var tester = new KnnTester<ContentKnnRecommender>
{
K = k,
Sim = sim,
Ra = ra,
TestUsers = testUsers,
KnnModel = knnModel,
Trainer = trainer,
Artists = artists,
NumberOfTests = numberOfTests
};
stp.QueueWorkItem(tester.KnnTest);
}

}
}
}

stp.Start();

break;
#endregion
#endregion

case "svd":
case "mf":
Expand All @@ -161,61 +189,6 @@ public static void Main(string[] args)
}
}

#region KnnTest
private static void KnnTest<TRecommender>(int k, ISimilarityEstimator sim, IRatingAggregator ra, List<IUser> testUsers, IKnnModel knnModel, ITrainer<IKnnModel, IUser> trainer, List<IArtist> artists, int numberOfTests)
where TRecommender : IRecommender<IKnnModel>
{
var recommender = (TRecommender)Activator.CreateInstance(typeof(TRecommender), new object[] {sim, ra, k});

InitializeResultWriter(String.Format(@"D:\Dataset\results\Knn-{0}-{1}-{2}.txt", sim, ra, recommender));
Write("------------------------------------------------------", false);
Write(string.Format("Test Knn-{0}-{1}-{2} ({3})", sim, ra, recommender, DateTime.Now));
Write("------------------------------------------------------");

var rs = new KnnRecommendationSystem(trainer, recommender);
timer.Restart();
var rv = TestRecommendationSystem(artists, testUsers, knnModel, rs, numberOfTests);
timer.Stop();

Write(string.Format("RMSE(Knn-{0}-{1}-{2}) = {3} ({4}ms).", sim, ra, recommender, rv, timer.ElapsedMilliseconds));
fileWriter.Close();
}
#endregion

#region TestRecommendationSystem
private static RmseAndVariance TestRecommendationSystem<TModel, TUser>(List<IArtist> artists, List<TUser> testUsers, TModel model, IRecommendationSystem<TModel, TUser, ITrainer<TModel, TUser>, IRecommender<TModel>> rs, int numberOfTests = 100000)
where TModel : IModel
where TUser : IUser
{
var tenPrecent = numberOfTests / 10;
var rmseList = new List<float>();
while (rmseList.Count < numberOfTests)
{
var userIndex = rng.Next(testUsers.Count);
var user = testUsers[userIndex];

//if true we'll remove the only rating
if (user.Ratings.Count < 2)
continue;

var ratingIndex = rng.Next(user.Ratings.Count);
var rating = user.Ratings[ratingIndex];

var error = GerPredictionError(artists, model, rs, rating, user);

rmseList.Add((float)Math.Sqrt(error * error));

if (rmseList.Count % tenPrecent != 0)
continue;

Write(string.Format("Test at {0} in {1}ms with RMSE {2}", rmseList.Count, timer.ElapsedMilliseconds, rmseList.Sum() / rmseList.Count));
}

var rv = new RmseAndVariance(rmseList);
return rv;
}
#endregion

#region CompleteTestRecommendationSystem
private static RmseAndVariance CompleteTestRecommendationSystem<TModel, TUser>(List<IArtist> artists, List<TUser> testUsers, TModel model, IRecommendationSystem<TModel, TUser, ITrainer<TModel, TUser>, IRecommender<TModel>> rs)
where TModel : IModel
Expand Down Expand Up @@ -264,25 +237,25 @@ private static void LoadData(out List<IUser> trainUsers, out List<IRating> train
#endregion

#region SavingResults
private static void InitializeResultWriter(string filename)
{
var dir = Path.GetDirectoryName(filename);
if (dir != null && !Directory.Exists(dir))
Directory.CreateDirectory(dir);
//private static void InitializeResultWriter(string filename)
//{
// var dir = Path.GetDirectoryName(filename);
// if (dir != null && !Directory.Exists(dir))
// Directory.CreateDirectory(dir);

fileWriter = new StreamWriter(filename);
}
// fileWriter = new StreamWriter(filename);
//}

private static void Write(string text, bool toFile = true)
{
Console.WriteLine(text);
//private static void Write(string text, bool toFile = true)
//{
// Console.WriteLine(text);

if (!toFile)
return;
// if (!toFile)
// return;

fileWriter.WriteLine(text);
fileWriter.Flush();
}
// fileWriter.WriteLine(text);
// fileWriter.Flush();
//}
#endregion
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
<RootNamespace>RecommendationSystem.QualityTesting</RootNamespace>
<AssemblyName>rsqt</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<TargetFrameworkProfile>Client</TargetFrameworkProfile>
<TargetFrameworkProfile>
</TargetFrameworkProfile>
<FileAlignment>512</FileAlignment>
<SccProjectName>&lt;Project Location In Database&gt;</SccProjectName>
<SccLocalPath>&lt;Local Binding Root of Project&gt;</SccLocalPath>
Expand All @@ -38,6 +39,9 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="SmartThreadPool, Version=2.2.1.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\lib\SmartThreadPool.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
Expand All @@ -48,6 +52,7 @@
</ItemGroup>
<ItemGroup>
<Compile Include="CryptoRandom.cs" />
<Compile Include="KnnTester.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="RatingByArtistComparer.cs" />
Expand Down Expand Up @@ -75,6 +80,9 @@
<Name>RecommendationSystem</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="app.config" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
Expand Down
3 changes: 3 additions & 0 deletions RecommendationSystem.QualityTesting/app.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<?xml version="1.0"?>
<configuration>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/></startup></configuration>
Binary file added lib/SmartThreadPool.dll
Binary file not shown.

0 comments on commit 2208b2e

Please sign in to comment.