cofi_cost 0.0.7 → 0.0.8
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +27 -0
- data/Rakefile +5 -0
- data/cofi_cost.gemspec +16 -0
- data/lib/cofi_cost.rb +12 -4
- data/spec/cofi_cost_spec.rb +77 -0
- data/spec/spec_helper.rb +4 -0
- metadata +10 -4
- data/test/unit/test_cofi_cost.rb +0 -41
data/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
*.txt
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
cofi_cost (0.0.8)
|
5
|
+
gsl
|
6
|
+
narray
|
7
|
+
|
8
|
+
GEM
|
9
|
+
remote: https://rubygems.org/
|
10
|
+
specs:
|
11
|
+
columnize (0.3.6)
|
12
|
+
debugger (1.6.2)
|
13
|
+
columnize (>= 0.3.1)
|
14
|
+
debugger-linecache (~> 1.2.0)
|
15
|
+
debugger-ruby_core_source (~> 1.2.3)
|
16
|
+
debugger-linecache (1.2.0)
|
17
|
+
debugger-ruby_core_source (1.2.3)
|
18
|
+
gsl (1.15.3)
|
19
|
+
narray (>= 0.5.9)
|
20
|
+
narray (0.6.0.8)
|
21
|
+
|
22
|
+
PLATFORMS
|
23
|
+
ruby
|
24
|
+
|
25
|
+
DEPENDENCIES
|
26
|
+
cofi_cost!
|
27
|
+
debugger
|
data/Rakefile
ADDED
data/cofi_cost.gemspec
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = 'cofi_cost'
|
3
|
+
s.version = '0.0.8'
|
4
|
+
s.date = '2013-11-18'
|
5
|
+
s.summary = "Collaborative filtering"
|
6
|
+
s.description = "Playground for collaborative filtering in Ruby using NArray and rb-gsl."
|
7
|
+
s.authors = ["Thomas Wolfe"]
|
8
|
+
s.email = 'tomwolfe@gmail.com'
|
9
|
+
s.files = `git ls-files`.split("\n")
|
10
|
+
s.homepage = 'http://github.com/tomwolfe/cofi_cost'
|
11
|
+
s.add_runtime_dependency 'gsl'
|
12
|
+
s.add_runtime_dependency 'narray'
|
13
|
+
s.license = 'MIT'
|
14
|
+
s.required_ruby_version = '>= 1.9.2'
|
15
|
+
s.requirements << 'libgsl0-dev'
|
16
|
+
end
|
data/lib/cofi_cost.rb
CHANGED
@@ -6,13 +6,14 @@ include GSL::MultiMin
|
|
6
6
|
|
7
7
|
class CofiCost
|
8
8
|
|
9
|
-
attr_accessor :ratings, :num_features, :
|
10
|
-
attr_reader :boolean_rated, :num_tracks, :num_users, :ratings_mean, :ratings_norm, :predictions
|
9
|
+
attr_accessor :ratings, :num_features, :regularization, :iterations, :features, :theta, :max_rating
|
10
|
+
attr_reader :boolean_rated, :num_tracks, :num_users, :ratings_mean, :ratings_norm, :predictions, :cost
|
11
11
|
|
12
|
-
def initialize(ratings, num_features = 2, regularization = 1, iterations = 10, features = nil, theta = nil)
|
12
|
+
def initialize(ratings, num_features = 2, regularization = 1, iterations = 10, max_rating = 5, features = nil, theta = nil)
|
13
13
|
@ratings = ratings.to_f # make sure it's a float for correct normalization
|
14
14
|
@num_features = num_features
|
15
15
|
@cost = 0
|
16
|
+
@max_rating = max_rating
|
16
17
|
@boolean_rated = @ratings > 0 # return 0 for all rated and 1 for all unrated
|
17
18
|
@boolean_unrated = @boolean_rated.eq 0 # return 1 for all unrated and 0 for all unrated
|
18
19
|
@num_tracks = @ratings.shape[1] # @ratings is users x tracks
|
@@ -122,7 +123,14 @@ class CofiCost
|
|
122
123
|
end
|
123
124
|
|
124
125
|
def calc_predictions
|
125
|
-
NArray.ref(NMatrix.ref(@features) * NMatrix.ref(@theta.transpose(1,0))) + @ratings_mean
|
126
|
+
predicts = NArray.ref(NMatrix.ref(@features) * NMatrix.ref(@theta.transpose(1,0))) + @ratings_mean
|
127
|
+
set_max_predictions(predicts)
|
128
|
+
end
|
129
|
+
|
130
|
+
def set_max_predictions(predicts)
|
131
|
+
a = predicts > @max_rating
|
132
|
+
predicts[a] = @max_rating
|
133
|
+
predicts
|
126
134
|
end
|
127
135
|
|
128
136
|
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require_relative 'spec_helper'
|
2
|
+
|
3
|
+
describe CofiCost do
|
4
|
+
before :each do
|
5
|
+
ratings = NArray[[5.0,4.0,0.0,0.0],[3.0,0.0,0.0,0.0],[4.0,0.0,0.0,0.0],[3.0,0.0,0.0,0.0],[3.0,0.0,0.0,0.0]]
|
6
|
+
num_features = 3
|
7
|
+
regularization = 1
|
8
|
+
iterations = 10
|
9
|
+
max_rating = 5
|
10
|
+
theta = NArray[[0.28544,-1.68427,0.26294],[0.50501,-0.45465,0.31746],[-0.43192,-0.47880,0.84671],[0.72860,-0.27189,0.32684]]
|
11
|
+
features = NArray[[1.048686,-0.400232,1.194119],[0.780851,-0.385626,0.521198],[0.641509,-0.547854,-0.083796],[0.453618,-0.800218,0.680481],[0.937538,0.106090,0.361953]]
|
12
|
+
@cofi = CofiCost.new(ratings, num_features, regularization, iterations, max_rating, features, theta)
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "#normalize_ratings" do
|
16
|
+
it "subtracts the mean rating of a track from each of the tracks ratings" do
|
17
|
+
# called in initilization
|
18
|
+
@cofi.ratings_mean.should == NArray[[4.5],[3.0],[4.0],[3.0],[3.0]]
|
19
|
+
@cofi.ratings_norm.should == NArray[[0.5,-0.5,0.0,0.0],[0.0,0.0,0.0,0.0],[0.0,0.0,0.0,0.0],[0.0,0.0,0.0,0.0],[0.0,0.0,0.0,0.0]]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe "#partial_cost_calc(theta,features)" do
|
24
|
+
it "calculates part of the cost" do
|
25
|
+
@cofi.partial_cost_calc.to_a.should == [[0.78741733234,1.5906474134,0.0,0.0],[1.00942821458,0.0,0.0,0.0],[1.0838130652999998,0.0,-0.0,0.0],[1.65618956692,0.0,0.0,0.0],[0.18409856424000004,0.0,-0.0,0.0]]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe "#roll_up_theta_and_features" do
|
30
|
+
it "returns a vector of @theta + @features" do
|
31
|
+
@cofi.roll_up_theta_and_features.to_a.should == [0.28544,-1.68427,0.26294,0.50501,-0.45465,0.31746,-0.43192,-0.4788,0.84671,0.7286,-0.27189,0.32684,1.048686,-0.400232,1.194119,0.780851,-0.385626,0.521198,0.641509,-0.547854,-0.083796,0.453618,-0.800218,0.680481,0.937538,0.10609,0.361953]
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe "#unroll_params_init_shape(x)" do
|
36
|
+
before :each do
|
37
|
+
@rolled = @cofi.roll_up_theta_and_features
|
38
|
+
end
|
39
|
+
it "unrolls @theta and @features back into it's original shape" do
|
40
|
+
orig_theta, orig_features = @cofi.theta, @cofi.features
|
41
|
+
@cofi.unroll_params_init_shape(@rolled)
|
42
|
+
@cofi.theta.should == orig_theta
|
43
|
+
@cofi.features.should == orig_features
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe "#min_cost" do
|
48
|
+
it "finds the lowest cost" do
|
49
|
+
@cofi.min_cost
|
50
|
+
@cofi.cost.should == 0.9885618408659724
|
51
|
+
end
|
52
|
+
it "calls #calc_predictions" do
|
53
|
+
@cofi.should_receive(:calc_predictions)
|
54
|
+
@cofi.min_cost
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe "#calc_predictions" do
|
59
|
+
it "calculates predictions" do
|
60
|
+
@cofi.calc_predictions.to_a.should == [[5.0, 5.0, 5.0, 5.0], [4.00942821458, 3.73512194149, 3.28867612346, 3.84412424606], [5.0, 4.54644840303, 3.91428101676, 4.58897159682], [4.65618956692, 3.80892623814, 3.76338775935, 3.7704857568600003], [3.18409856424, 3.54013784626, 2.85073191967, 3.77254609522]]
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
describe "#unroll_params(v)" do
|
65
|
+
it "unrolls v back to it's original @theta and @features dimensions" do
|
66
|
+
@cofi.unroll_params(@cofi.roll_up_theta_and_features)[0].to_a.should == [[0.28544,-1.68427,0.26294],[0.50501,-0.45465,0.31746],[-0.43192,-0.4788,0.84671],[0.7286,-0.27189,0.32684]]
|
67
|
+
@cofi.unroll_params(@cofi.roll_up_theta_and_features)[1].to_a.should == [[1.048686,-0.400232,1.194119],[0.780851,-0.385626,0.521198],[0.641509,-0.547854,-0.083796],[0.453618,-0.800218,0.680481],[0.937538,0.10609,0.361953]]
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
describe "#set_max_predictions(predicts)" do
|
72
|
+
it "does not allow any prediction greater than self.max_rating" do
|
73
|
+
test = NArray[[6.0,4.0,0.0,0.0],[3.0,0.0,6.5,0.0],[4.0,0.0,0.0,0.0],[3.0,0.0,0.0,0.0],[3.0,0.0,0.0,0.0]]
|
74
|
+
@cofi.set_max_predictions(test).to_a.should == [[5.0,4.0,0.0,0.0],[3.0,0.0,5.0,0.0],[4.0,0.0,0.0,0.0],[3.0,0.0,0.0,0.0],[3.0,0.0,0.0,0.0]]
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cofi_cost
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.8
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2013-11-18 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: gsl
|
@@ -49,9 +49,15 @@ executables: []
|
|
49
49
|
extensions: []
|
50
50
|
extra_rdoc_files: []
|
51
51
|
files:
|
52
|
-
-
|
52
|
+
- .gitignore
|
53
|
+
- Gemfile
|
54
|
+
- Gemfile.lock
|
53
55
|
- README.textile
|
54
|
-
-
|
56
|
+
- Rakefile
|
57
|
+
- cofi_cost.gemspec
|
58
|
+
- lib/cofi_cost.rb
|
59
|
+
- spec/cofi_cost_spec.rb
|
60
|
+
- spec/spec_helper.rb
|
55
61
|
homepage: http://github.com/tomwolfe/cofi_cost
|
56
62
|
licenses:
|
57
63
|
- MIT
|
data/test/unit/test_cofi_cost.rb
DELETED
@@ -1,41 +0,0 @@
|
|
1
|
-
require 'test/unit'
|
2
|
-
require_relative '../../lib/cofi_cost.rb'
|
3
|
-
require 'narray'
|
4
|
-
require 'gsl'
|
5
|
-
require 'matrix'
|
6
|
-
|
7
|
-
class CofiCostTest < Test::Unit::TestCase
|
8
|
-
|
9
|
-
def setup
|
10
|
-
ratings = NArray[[5.0,4.0,0.0,0.0],[3.0,0.0,0.0,0.0],[4.0,0.0,0.0,0.0],[3.0,0.0,0.0,0.0],[3.0,0.0,0.0,0.0]]
|
11
|
-
num_features = 2
|
12
|
-
lambda = 1
|
13
|
-
iterations = 10
|
14
|
-
features = NArray[[0.139489,1.804804],[-0.501808,1.050885],[0.354079,-0.518884],[-0.015370,0.096253],[1.147623,-0.745562]]
|
15
|
-
theta = NArray[[-0.079641,1.211386],[-0.130688,0.444762],[-0.789258,1.222232],[0.212132,-1.174545]]
|
16
|
-
@c = CofiCost.new(ratings, num_features, lambda, iterations, features, theta)
|
17
|
-
end
|
18
|
-
|
19
|
-
def teardown
|
20
|
-
@c = nil
|
21
|
-
end
|
22
|
-
|
23
|
-
def test_happy_case
|
24
|
-
@c.min_cost
|
25
|
-
assert_equal 0.07964723302994943, @c.cost
|
26
|
-
# oddly the following fails, even though they are equal (not enough decimal places me thinks)
|
27
|
-
# assert_equal NArray[[4.62547,3.91302,8.30084,1.59081],[2.96361,3.17939,1.88322,3.88434],[3.92356,4.32263,1.739,5.6172],[2.98132,3.06219,2.47359,3.3213],[2.93724,3.14111,1.33728,3.77855]], @c.predictions
|
28
|
-
assert_equal 4.625468057637709, @c.predictions[0,0]
|
29
|
-
end
|
30
|
-
|
31
|
-
def test_normalize_ratings
|
32
|
-
assert_equal NArray[[4.5],[3.0],[4.0],[3.0],[3.0]], @c.ratings_mean
|
33
|
-
assert_equal NArray[[0.5,-0.5,0.0,0.0],[0.0,0.0,0.0,0.0],[0.0,0.0,0.0,0.0],[0.0,0.0,0.0,0.0],[0.0,0.0,0.0,0.0]], @c.ratings_norm
|
34
|
-
end
|
35
|
-
|
36
|
-
def test_roll_up_theta_and_features
|
37
|
-
rolled = @c.roll_up_theta_and_features
|
38
|
-
assert_equal GSL:: Vector.alloc([-0.079641, 1.211386, -0.130688, 0.444762, -0.789258, 1.222232, 0.212132, -1.174545, 0.139489, 1.804804, -0.501808, 1.050885, 0.354079, -0.518884, -0.01537, 0.096253, 1.147623, -0.745562]), rolled
|
39
|
-
end
|
40
|
-
|
41
|
-
end
|