libmf 0.2.2 → 0.2.6
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +18 -0
- data/README.md +22 -23
- data/lib/libmf/ffi.rb +11 -4
- data/lib/libmf/matrix.rb +13 -0
- data/lib/libmf/model.rb +28 -2
- data/lib/libmf/version.rb +1 -1
- data/lib/libmf.rb +7 -2
- data/vendor/libmf.arm64.so +0 -0
- metadata +6 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 33b8ba1c7cf9de8e19fa9775ecdb445bc0f2f4323401cccbf016ff6a81a04a00
|
4
|
+
data.tar.gz: 9ff88bdafac8bbfe0c99ccc0c0b7dfb7a9092246de1105c7f99ccade29b9c88b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 021eca76e96ceca2cd484b7f7796a649529f3b20c80d494143cfaf43e617d23ac55ae50bdcd345808e277b31d304ea6e6a53c937b3e9a200f47ae226f27e4f2b
|
7
|
+
data.tar.gz: 916b78a5e62d19e4d6a9e1433350bb26982d43393029e4e883ed803f89a03f6db766ab10718120a73db1f0ad7ac80607d475a10a7c203d117ca1706e80618345
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,21 @@
|
|
1
|
+
## 0.2.6 (2021-12-02)
|
2
|
+
|
3
|
+
- Improved ARM detection
|
4
|
+
|
5
|
+
## 0.2.5 (2021-10-18)
|
6
|
+
|
7
|
+
- Added named loss functions
|
8
|
+
- Added `Matrix` class
|
9
|
+
- Improved error checking for `fit`, `cv`, `save_model`, and `load_model`
|
10
|
+
|
11
|
+
## 0.2.4 (2021-08-05)
|
12
|
+
|
13
|
+
- Fixed memory leak
|
14
|
+
|
15
|
+
## 0.2.3 (2021-03-14)
|
16
|
+
|
17
|
+
- Added ARM shared library for Linux
|
18
|
+
|
1
19
|
## 0.2.2 (2021-02-04)
|
2
20
|
|
3
21
|
- Reduced allocations
|
data/README.md
CHANGED
@@ -1,10 +1,10 @@
|
|
1
|
-
# LIBMF
|
1
|
+
# LIBMF Ruby
|
2
2
|
|
3
3
|
[LIBMF](https://github.com/cjlin1/libmf) - large-scale sparse matrix factorization - for Ruby
|
4
4
|
|
5
5
|
Check out [Disco](https://github.com/ankane/disco) for higher-level collaborative filtering
|
6
6
|
|
7
|
-
[](https://github.com/ankane/libmf/actions)
|
7
|
+
[](https://github.com/ankane/libmf-ruby/actions)
|
8
8
|
|
9
9
|
## Installation
|
10
10
|
|
@@ -16,14 +16,13 @@ gem 'libmf'
|
|
16
16
|
|
17
17
|
## Getting Started
|
18
18
|
|
19
|
-
Prep your data in the format `
|
19
|
+
Prep your data in the format `row_index, column_index, value`
|
20
20
|
|
21
21
|
```ruby
|
22
|
-
data =
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
]
|
22
|
+
data = Libmf::Matrix.new
|
23
|
+
data.push(0, 0, 5.0)
|
24
|
+
data.push(0, 2, 3.5)
|
25
|
+
data.push(1, 1, 4.0)
|
27
26
|
```
|
28
27
|
|
29
28
|
Create a model
|
@@ -90,7 +89,7 @@ Pass parameters - default values below
|
|
90
89
|
|
91
90
|
```ruby
|
92
91
|
Libmf::Model.new(
|
93
|
-
loss:
|
92
|
+
loss: :real_l2, # loss function
|
94
93
|
factors: 8, # number of latent factors
|
95
94
|
threads: 12, # number of threads used
|
96
95
|
bins: 25, # number of bins
|
@@ -111,21 +110,21 @@ Libmf::Model.new(
|
|
111
110
|
|
112
111
|
For real-valued matrix factorization
|
113
112
|
|
114
|
-
-
|
115
|
-
-
|
116
|
-
-
|
113
|
+
- `:real_l2` - squared error (L2-norm)
|
114
|
+
- `:real_l1` - absolute error (L1-norm)
|
115
|
+
- `:real_kl` - generalized KL-divergence
|
117
116
|
|
118
117
|
For binary matrix factorization
|
119
118
|
|
120
|
-
-
|
121
|
-
-
|
122
|
-
-
|
119
|
+
- `:binary_log` - logarithmic error
|
120
|
+
- `:binary_l2` - squared hinge loss
|
121
|
+
- `:binary_l1` - hinge loss
|
123
122
|
|
124
123
|
For one-class matrix factorization
|
125
124
|
|
126
|
-
-
|
127
|
-
-
|
128
|
-
-
|
125
|
+
- `:one_class_row` - row-oriented pair-wise logarithmic loss
|
126
|
+
- `:one_class_col` - column-oriented pair-wise logarithmic loss
|
127
|
+
- `:one_class_l2` - squared error (L2-norm)
|
129
128
|
|
130
129
|
## Performance
|
131
130
|
|
@@ -159,22 +158,22 @@ model.q_factors(format: :numo)
|
|
159
158
|
|
160
159
|
## History
|
161
160
|
|
162
|
-
View the [changelog](https://github.com/ankane/libmf/blob/master/CHANGELOG.md)
|
161
|
+
View the [changelog](https://github.com/ankane/libmf-ruby/blob/master/CHANGELOG.md)
|
163
162
|
|
164
163
|
## Contributing
|
165
164
|
|
166
165
|
Everyone is encouraged to help improve this project. Here are a few ways you can help:
|
167
166
|
|
168
|
-
- [Report bugs](https://github.com/ankane/libmf/issues)
|
169
|
-
- Fix bugs and [submit pull requests](https://github.com/ankane/libmf/pulls)
|
167
|
+
- [Report bugs](https://github.com/ankane/libmf-ruby/issues)
|
168
|
+
- Fix bugs and [submit pull requests](https://github.com/ankane/libmf-ruby/pulls)
|
170
169
|
- Write, clarify, or fix documentation
|
171
170
|
- Suggest or add new features
|
172
171
|
|
173
172
|
To get started with development:
|
174
173
|
|
175
174
|
```sh
|
176
|
-
git clone --recursive https://github.com/ankane/libmf.git
|
177
|
-
cd libmf
|
175
|
+
git clone --recursive https://github.com/ankane/libmf-ruby.git
|
176
|
+
cd libmf-ruby
|
178
177
|
bundle install
|
179
178
|
bundle exec rake vendor:all
|
180
179
|
bundle exec rake test
|
data/lib/libmf/ffi.rb
CHANGED
@@ -43,15 +43,22 @@ module Libmf
|
|
43
43
|
:b, :float,
|
44
44
|
:p, :pointer,
|
45
45
|
:q, :pointer
|
46
|
+
|
47
|
+
def self.release(pointer)
|
48
|
+
unless pointer.null?
|
49
|
+
ref = ::FFI::MemoryPointer.new(:pointer).write_pointer(pointer)
|
50
|
+
FFI.mf_destroy_model(ref)
|
51
|
+
end
|
52
|
+
end
|
46
53
|
end
|
47
54
|
|
48
55
|
attach_function :mf_get_default_param, [], Parameter.by_value
|
49
56
|
attach_function :mf_read_problem, [:string], Problem.by_value
|
50
57
|
attach_function :mf_save_model, [Model.by_ref, :string], :int
|
51
|
-
attach_function :mf_load_model, [:string], Model.
|
52
|
-
attach_function :mf_destroy_model, [
|
53
|
-
attach_function :mf_train, [Problem.by_ref, Parameter.by_value], Model.
|
54
|
-
attach_function :mf_train_with_validation, [Problem.by_ref, Problem.by_ref, Parameter.by_value], Model.
|
58
|
+
attach_function :mf_load_model, [:string], Model.auto_ptr
|
59
|
+
attach_function :mf_destroy_model, [:pointer], :void
|
60
|
+
attach_function :mf_train, [Problem.by_ref, Parameter.by_value], Model.auto_ptr
|
61
|
+
attach_function :mf_train_with_validation, [Problem.by_ref, Problem.by_ref, Parameter.by_value], Model.auto_ptr
|
55
62
|
attach_function :mf_predict, [Model.by_ref, :int, :int], :float
|
56
63
|
attach_function :mf_cross_validation, [Problem.by_ref, :int, Parameter.by_value], :double
|
57
64
|
end
|
data/lib/libmf/matrix.rb
ADDED
data/lib/libmf/model.rb
CHANGED
@@ -14,6 +14,7 @@ module Libmf
|
|
14
14
|
else
|
15
15
|
FFI.mf_train(train_set, param)
|
16
16
|
end
|
17
|
+
raise Error, "fit failed" if @model.null?
|
17
18
|
|
18
19
|
nil
|
19
20
|
end
|
@@ -24,15 +25,20 @@ module Libmf
|
|
24
25
|
|
25
26
|
def cv(data, folds: 5)
|
26
27
|
problem = create_problem(data)
|
27
|
-
|
28
|
+
# TODO update fork to differentiate between bad parameters and zero error
|
29
|
+
res = FFI.mf_cross_validation(problem, folds, param)
|
30
|
+
raise Error, "cv failed" if res == 0
|
31
|
+
res
|
28
32
|
end
|
29
33
|
|
30
34
|
def save_model(path)
|
31
|
-
FFI.mf_save_model(model, path)
|
35
|
+
status = FFI.mf_save_model(model, path)
|
36
|
+
raise Error, "Cannot save model" if status != 0
|
32
37
|
end
|
33
38
|
|
34
39
|
def load_model(path)
|
35
40
|
@model = FFI.mf_load_model(path)
|
41
|
+
raise Error, "Cannot open model" if @model.null?
|
36
42
|
end
|
37
43
|
|
38
44
|
def rows
|
@@ -80,6 +86,22 @@ module Libmf
|
|
80
86
|
def param
|
81
87
|
param = FFI.mf_get_default_param
|
82
88
|
options = @options.dup
|
89
|
+
|
90
|
+
if options[:loss].is_a?(Symbol)
|
91
|
+
loss_map = {
|
92
|
+
real_l2: 0,
|
93
|
+
real_l1: 1,
|
94
|
+
real_kl: 2,
|
95
|
+
binary_log: 5,
|
96
|
+
binary_l2: 6,
|
97
|
+
binary_l1: 7,
|
98
|
+
one_class_row: 10,
|
99
|
+
one_class_col: 11,
|
100
|
+
one_class_l2: 12
|
101
|
+
}
|
102
|
+
options[:loss] = loss_map[options[:loss]] || (raise ArgumentError, "Unknown loss")
|
103
|
+
end
|
104
|
+
|
83
105
|
# silence insufficient blocks warning with default params
|
84
106
|
options[:bins] ||= 25 unless options[:nr_bins]
|
85
107
|
options[:copy_data] = false unless options.key?(:copy_data)
|
@@ -107,6 +129,10 @@ module Libmf
|
|
107
129
|
return FFI.mf_read_problem(File.expand_path(data))
|
108
130
|
end
|
109
131
|
|
132
|
+
if data.is_a?(Matrix)
|
133
|
+
data = data.data
|
134
|
+
end
|
135
|
+
|
110
136
|
raise Error, "No data" if data.empty?
|
111
137
|
|
112
138
|
# TODO do in C for better performance
|
data/lib/libmf/version.rb
CHANGED
data/lib/libmf.rb
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
require "ffi"
|
3
3
|
|
4
4
|
# modules
|
5
|
+
require "libmf/matrix"
|
5
6
|
require "libmf/model"
|
6
7
|
require "libmf/version"
|
7
8
|
|
@@ -15,13 +16,17 @@ module Libmf
|
|
15
16
|
if Gem.win_platform?
|
16
17
|
"mf.dll"
|
17
18
|
elsif RbConfig::CONFIG["host_os"] =~ /darwin/i
|
18
|
-
if RbConfig::CONFIG["host_cpu"] =~ /arm/i
|
19
|
+
if RbConfig::CONFIG["host_cpu"] =~ /arm|aarch64/i
|
19
20
|
"libmf.arm64.dylib"
|
20
21
|
else
|
21
22
|
"libmf.dylib"
|
22
23
|
end
|
23
24
|
else
|
24
|
-
"
|
25
|
+
if RbConfig::CONFIG["host_cpu"] =~ /arm|aarch64/i
|
26
|
+
"libmf.arm64.so"
|
27
|
+
else
|
28
|
+
"libmf.so"
|
29
|
+
end
|
25
30
|
end
|
26
31
|
vendor_lib = File.expand_path("../vendor/#{lib_name}", __dir__)
|
27
32
|
self.ffi_lib = [vendor_lib]
|
Binary file
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: libmf
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrew Kane
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-02
|
11
|
+
date: 2021-12-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ffi
|
@@ -35,16 +35,18 @@ files:
|
|
35
35
|
- README.md
|
36
36
|
- lib/libmf.rb
|
37
37
|
- lib/libmf/ffi.rb
|
38
|
+
- lib/libmf/matrix.rb
|
38
39
|
- lib/libmf/model.rb
|
39
40
|
- lib/libmf/version.rb
|
40
41
|
- vendor/COPYRIGHT
|
41
42
|
- vendor/demo/real_matrix.te.txt
|
42
43
|
- vendor/demo/real_matrix.tr.txt
|
43
44
|
- vendor/libmf.arm64.dylib
|
45
|
+
- vendor/libmf.arm64.so
|
44
46
|
- vendor/libmf.dylib
|
45
47
|
- vendor/libmf.so
|
46
48
|
- vendor/mf.dll
|
47
|
-
homepage: https://github.com/ankane/libmf
|
49
|
+
homepage: https://github.com/ankane/libmf-ruby
|
48
50
|
licenses:
|
49
51
|
- BSD-3-Clause
|
50
52
|
metadata: {}
|
@@ -63,7 +65,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
63
65
|
- !ruby/object:Gem::Version
|
64
66
|
version: '0'
|
65
67
|
requirements: []
|
66
|
-
rubygems_version: 3.2.
|
68
|
+
rubygems_version: 3.2.32
|
67
69
|
signing_key:
|
68
70
|
specification_version: 4
|
69
71
|
summary: Large-scale sparse matrix factorization for Ruby
|