cofi_cost 0.0.7 → 0.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|