cmfrec Ruby

:fire: Recommendations for Ruby, powered by cmfrec

  • Supports side information :tada:
  • Works with explicit and implicit feedback
  • Uses high-performance matrix factorization

Build Status

Installation

Add this line to your application’s Gemfile:

gem "cmfrec"

For Windows, also follow these instructions.

Getting Started

Create a recommender

recommender = Cmfrec::Recommender.new

If users rate items directly, this is known as explicit feedback. Fit the recommender with:

recommender.fit([
  {user_id: 1, item_id: 1, rating: 5},
  {user_id: 2, item_id: 1, rating: 3}
])

IDs can be integers, strings, or any other data type

If users don’t rate items directly (for instance, they’re purchasing items or reading posts), this is known as implicit feedback. Leave out the rating, or use a value like number of purchases, number of page views, or time spent on page:

recommender.fit([
  {user_id: 1, item_id: 1, value: 1},
  {user_id: 2, item_id: 1, value: 1}
])

Use value instead of rating for implicit feedback

Get recommendations for a user in the training data

recommender.user_recs(user_id)

Get recommendations for a new user

recommender.new_user_recs([
  {item_id: 1, rating: 5},
  {item_id: 2, rating: 3}
])

Use the count option to specify the number of recommendations (default is 5)

recommender.user_recs(user_id, count: 3)

Get predicted ratings for specific users and items

recommender.predict([{user_id: 1, item_id: 2}, {user_id: 2, item_id: 4}])

Side Information

Add side information about users, items, or both

 = [
  {user_id: 1, cats: 1, dogs: 0},
  {user_id: 2, cats: 2, dogs: 1}
]
item_info = [
  {item_id: 1, genre_comedy: 1, genre_drama: 0},
  {item_id: 2, genre_comedy: 0, genre_drama: 1}
]
recommender.fit(ratings, user_info: , item_info: item_info)

Get recommendations for a new user with ratings and side information

ratings = [
  {item_id: 1, rating: 5},
  {item_id: 2, rating: 3}
]
recommender.new_user_recs(ratings, user_info: {cats: 0, dogs: 2})

Get recommendations with only side information

recommender.new_user_recs([], user_info: {cats: 0, dogs: 2})

Similarity

Add this line to your application’s Gemfile:

gem "ngt"

Get similar users

recommender.similar_users(user_id)

Get similar items - “users who liked this item also liked”

recommender.similar_items(item_id)

Examples

MovieLens

Load the data

ratings, , item_info = Cmfrec.load_movielens

Create a recommender and get predictions

recommender = Cmfrec::Recommender.new(factors: 20)
recommender.fit(ratings.first(80000), user_info: , item_info: item_info)
recommender.predict(ratings.last(20000))

Ahoy

Ahoy is a great source for implicit feedback

views = Ahoy::Event.where(name: "Viewed post").group(:user_id).group_prop(:post_id).count

data =
  views.map do |(user_id, post_id), count|
    {
      user_id: user_id,
      item_id: post_id,
      value: count
    }
  end

Create a recommender and get recommended posts for a user

recommender = Cmfrec::Recommender.new
recommender.fit(data)
recommender.user_recs(current_user.id)

Options

Specify the number of factors and epochs

Cmfrec::Recommender.new(factors: 8, epochs: 20)

If recommendations look off, trying changing factors. The default is 8, but 3 could be good for some applications and 300 good for others.

Explicit Feedback

Add implicit features

Cmfrec::Recommender.new(add_implicit_features: true)

Disable bias

Cmfrec::Recommender.new(user_bias: false, item_bias: false)

Data

Data can be an array of hashes

[{user_id: 1, item_id: 1, rating: 5}, {user_id: 2, item_id: 1, rating: 3}]

Or a Rover data frame

Rover.read_csv("ratings.csv")

Storing Recommenders

Store the recommender

json = recommender.to_json
File.write("recommender.json", json)

The serialized recommender includes user activity from the training data (to avoid recommending previously rated items), so be sure to protect it. You can save it to a file, database, or any other storage system, or use a tool like Trove. Also, user and item IDs should be integers or strings for this.

Load a recommender

json = File.read("recommender.json")
recommender = Cmfrec::Recommender.load_json(json)

Alternatively, you can store only the factors and use a library like Neighbor. See the examples for Disco, which has a similar API. For explicit feedback, you should disable the bias with this approach.

Reference

Get ids

recommender.user_ids
recommender.item_ids

Get the global mean

recommender.global_mean

Get the factors

recommender.user_factors
recommender.item_factors

Get the bias

recommender.user_bias
recommender.item_bias

Windows Installation

On Windows, build the cmfrec C shared library and set:

Cmfrec.ffi_lib = "path/to/cmfrec.dll"

History

View the changelog

Contributing

Everyone is encouraged to help improve this project. Here are a few ways you can help:

To get started with development:

git clone https://github.com/ankane/cmfrec-ruby.git
cd cmfrec-ruby
bundle install
bundle exec rake vendor:all
bundle exec rake test