class Libmf::Model
Public Class Methods
new(**options)
click to toggle source
# File lib/libmf/model.rb, line 3 def initialize(**options) @options = options end
Public Instance Methods
bias()
click to toggle source
# File lib/libmf/model.rb, line 50 def bias model[:b] end
columns()
click to toggle source
# File lib/libmf/model.rb, line 42 def columns model[:n] end
cv(data, folds: 5)
click to toggle source
# File lib/libmf/model.rb, line 25 def cv(data, folds: 5) problem = create_problem(data) FFI.mf_cross_validation(problem, folds, param) end
factors()
click to toggle source
# File lib/libmf/model.rb, line 46 def factors model[:k] end
fit(data, eval_set: nil)
click to toggle source
# File lib/libmf/model.rb, line 7 def fit(data, eval_set: nil) train_set = create_problem(data) @model = if eval_set eval_set = create_problem(eval_set) FFI.mf_train_with_validation(train_set, eval_set, param) else FFI.mf_train(train_set, param) end nil end
load_model(path)
click to toggle source
# File lib/libmf/model.rb, line 34 def load_model(path) @model = FFI.mf_load_model(path) end
p_factors(format: nil)
click to toggle source
# File lib/libmf/model.rb, line 54 def p_factors(format: nil) _factors(model[:p], rows, format) end
predict(row, column)
click to toggle source
# File lib/libmf/model.rb, line 21 def predict(row, column) FFI.mf_predict(model, row, column) end
q_factors(format: nil)
click to toggle source
# File lib/libmf/model.rb, line 58 def q_factors(format: nil) _factors(model[:q], columns, format) end
rows()
click to toggle source
# File lib/libmf/model.rb, line 38 def rows model[:m] end
save_model(path)
click to toggle source
# File lib/libmf/model.rb, line 30 def save_model(path) FFI.mf_save_model(model, path) end
Private Instance Methods
_factors(ptr, n, format)
click to toggle source
# File lib/libmf/model.rb, line 64 def _factors(ptr, n, format) case format when :numo Numo::SFloat.from_string(ptr.read_bytes(n * factors * 4)).reshape(n, factors) when nil ptr.read_array_of_float(n * factors).each_slice(factors).to_a else raise ArgumentError, "Invalid format" end end
create_problem(data)
click to toggle source
# File lib/libmf/model.rb, line 104 def create_problem(data) if data.is_a?(String) # need to expand path so it's absolute return FFI.mf_read_problem(File.expand_path(data)) end raise Error, "No data" if data.empty? # TODO do in C for better performance # can use FIX2INT() and RFLOAT_VALUE() instead of pack # and write directly to C string buffer = String.new pack_format = "iif" data.each do |row| row.pack(pack_format, buffer: buffer) end r = ::FFI::MemoryPointer.new(FFI::Node, data.size) r.write_bytes(buffer) # double check size is what we expect # FFI will throw an error above if too long raise Error, "Bad buffer size" if r.size != buffer.bytesize m = data.max_by { |r| r[0] }[0] + 1 n = data.max_by { |r| r[1] }[1] + 1 prob = FFI::Problem.new prob[:m] = m prob[:n] = n prob[:nnz] = data.size prob[:r] = r prob end
model()
click to toggle source
# File lib/libmf/model.rb, line 75 def model raise Error, "Not fit" unless @model @model end
param()
click to toggle source
# File lib/libmf/model.rb, line 80 def param param = FFI.mf_get_default_param options = @options.dup # silence insufficient blocks warning with default params options[:bins] ||= 25 unless options[:nr_bins] options[:copy_data] = false unless options.key?(:copy_data) options_map = { :loss => :fun, :factors => :k, :threads => :nr_threads, :bins => :nr_bins, :iterations => :nr_iters, :learning_rate => :eta, :nmf => :do_nmf } options.each do |k, v| k = options_map[k] if options_map[k] param[k] = v end # do_nmf must be true for generalized KL-divergence param[:do_nmf] = true if param[:fun] == 2 param end