class XLearn::Model

Public Class Methods

finalize(pointer) click to toggle source
# File lib/xlearn/model.rb, line 112
def self.finalize(pointer)
  # must use proc instead of stabby lambda
  proc { FFI.XLearnHandleFree(pointer) }
end
finalize_file(file) click to toggle source
# File lib/xlearn/model.rb, line 117
def self.finalize_file(file)
  # must use proc instead of stabby lambda
  proc do
    file.close
    file.unlink
  end
end
new(**options) click to toggle source
# File lib/xlearn/model.rb, line 5
def initialize(**options)
  @handle = ::FFI::MemoryPointer.new(:pointer)
  check_call FFI.XLearnCreate(@model_type, @handle)
  ObjectSpace.define_finalizer(self, self.class.finalize(@handle))

  options = {
    task: "binary",
    quiet: true,
    bin_out: false
  }.merge(options)

  if options[:task] == "binary" && !options.key?(:sigmoid)
    options[:sigmoid] = true
  end

  set_params(options)
end

Public Instance Methods

bias_term() click to toggle source
# File lib/xlearn/model.rb, line 94
def bias_term
  read_txt do |line|
    return line.split(":").last.to_f if line.start_with?("bias:")
  end
end
cv(x, y = nil, folds: nil) click to toggle source
# File lib/xlearn/model.rb, line 72
def cv(x, y = nil, folds: nil)
  set_params(fold: folds) if folds
  set_train_set(x, y)
  check_call FFI.XLearnCV(@handle)
end
fit(x, y = nil, eval_set: nil) click to toggle source
# File lib/xlearn/model.rb, line 23
def fit(x, y = nil, eval_set: nil)
  @model_path = nil
  partial_fit(x, y, eval_set: eval_set)
end
linear_term() click to toggle source
# File lib/xlearn/model.rb, line 100
def linear_term
  term = []
  read_txt do |line|
    if line.start_with?("i_")
      term << line.split(":").last.to_f
    elsif line.start_with?("v_")
      break
    end
  end
  term
end
load_model(path) click to toggle source
# File lib/xlearn/model.rb, line 88
def load_model(path)
  @model_file ||= create_tempfile
  # TODO ensure tempfile is still cleaned up
  FileUtils.cp(path, @model_file.path)
end
partial_fit(x, y = nil, eval_set: nil) click to toggle source
# File lib/xlearn/model.rb, line 28
def partial_fit(x, y = nil, eval_set: nil)
  check_call FFI.XLearnSetPreModel(@handle, @model_path || "")

  set_train_set(x, y)

  if eval_set
    if eval_set.is_a?(String)
      check_call FFI.XLearnSetValidate(@handle, eval_set)
    else
      valid_set = DMatrix.new(x, label: y)
      check_call FFI.XLearnSetDMatrix(@handle, "validate", valid_set)
    end
  end

  @txt_file ||= create_tempfile
  check_call FFI.XLearnSetTXTModel(@handle, @txt_file.path)

  @model_file ||= create_tempfile
  check_call FFI.XLearnFit(@handle, @model_file.path)
  @model_path = @model_file.path
end
predict(x, out_path: nil) click to toggle source
# File lib/xlearn/model.rb, line 50
def predict(x, out_path: nil)
  raise Error, "Not trained" unless @model_file

  if x.is_a?(String)
    check_call FFI.XLearnSetTest(@handle, x)
    check_call FFI.XLearnSetBool(@handle, "from_file", true)
  else
    test_set = DMatrix.new(x)
    check_call FFI.XLearnSetDMatrix(@handle, "test", test_set)
    check_call FFI.XLearnSetBool(@handle, "from_file", false)
  end

  if out_path
    check_call FFI.XLearnPredictForFile(@handle, @model_file.path, out_path)
  else
    length = ::FFI::MemoryPointer.new(:uint64)
    out_arr = ::FFI::MemoryPointer.new(:pointer)
    check_call FFI.XLearnPredictForMat(@handle, @model_file.path, length, out_arr)
    out_arr.read_pointer.read_array_of_float(length.read_uint64)
  end
end
save_model(path) click to toggle source
# File lib/xlearn/model.rb, line 78
def save_model(path)
  raise Error, "Not trained" unless @model_file
  FileUtils.cp(@model_file.path, path)
end
save_txt(path) click to toggle source
# File lib/xlearn/model.rb, line 83
def save_txt(path)
  raise Error, "Not trained" unless @txt_file
  FileUtils.cp(@txt_file.path, path)
end

Private Instance Methods

create_tempfile() click to toggle source
# File lib/xlearn/model.rb, line 166
def create_tempfile
  file = Tempfile.new("xlearn")
  ObjectSpace.define_finalizer(self, self.class.finalize_file(file))
  file
end
read_txt() { |line| ... } click to toggle source
# File lib/xlearn/model.rb, line 158
def read_txt
  if @txt_file
    File.foreach(@txt_file.path) do |line|
      yield line
    end
  end
end
set_params(params) click to toggle source
# File lib/xlearn/model.rb, line 138
def set_params(params)
  params.each do |k, v|
    k = k.to_s
    ret =
      case k
      when "task", "metric", "opt", "log"
        FFI.XLearnSetStr(@handle, k, v)
      when "lr", "lambda", "init", "alpha", "beta", "lambda_1", "lambda_2"
        FFI.XLearnSetFloat(@handle, k, v)
      when "k", "epoch", "fold", "nthread", "block_size", "stop_window", "seed"
        FFI.XLearnSetInt(@handle, k, v)
      when "quiet", "on_disk", "bin_out", "norm", "lock_free", "early_stop", "sign", "sigmoid"
        FFI.XLearnSetBool(@handle, k, v)
      else
        raise ArgumentError, "Invalid parameter: #{k}"
      end
    check_call ret
  end
end
set_train_set(x, y) click to toggle source
# File lib/xlearn/model.rb, line 127
def set_train_set(x, y)
  if x.is_a?(String)
    check_call FFI.XLearnSetTrain(@handle, x)
    check_call FFI.XLearnSetBool(@handle, "from_file", true)
  else
    train_set = DMatrix.new(x, label: y)
    check_call FFI.XLearnSetDMatrix(@handle, "train", train_set)
    check_call FFI.XLearnSetBool(@handle, "from_file", false)
  end
end