Ruby的协同过滤
支持基于用户和基于项目的建议 使用显式和隐式反馈 使用高性能矩阵分解
Ruby 机器学习
共134Star
详细介绍
Disco
- Supports user-based and item-based recommendations
- Works with explicit and implicit feedback
- Uses high-performance matrix factorization
Installation
Add this line to your application’s Gemfile:
gem 'disco'
Getting Started
Create a recommender
recommender = Disco::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 user-based (user-item) recommendations - “users like you also liked”
recommender.user_recs(user_id)
Get item-based (item-item) recommendations - “users who liked this item also liked”
recommender.item_recs(item_id)
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 items
recommender.user_recs(user_id, item_ids: [1, 2, 3])
Get similar users
recommender.similar_users(user_id)
Examples
MovieLens
Load the data
data = Disco.load_movielens
Create a recommender and get similar movies
recommender = Disco::Recommender.new(factors: 20)
recommender.fit(data)
recommender.item_recs("Star Wars (1977)")
Ahoy
Ahoy is a great source for implicit feedback
views = Ahoy::Event.
where(name: "Viewed post").
group(:user_id, "properties->>'post_id'") # postgres syntax
count
data =
views.map do |(user_id, post_id), count|
{
user_id: user_id,
post_id: post_id,
value: count
}
end
Create a recommender and get recommended posts for a user
recommender = Disco::Recommender.new
recommender.fit(data)
recommender.user_recs(current_user.id)
Storing Recommendations
Disco makes it easy to store recommendations in Rails.
rails generate disco:recommendation
rails db:migrate
For user-based recommendations, use:
class User < ApplicationRecord
has_recommended :products
end
Change
:products
to match the model you’re recommending
Save recommendations
User.find_each do |user|
recs = recommender.user_recs(user.id)
user.update_recommended_products(recs)
end
Get recommendations
user.recommended_products
For item-based recommendations, use:
class Product < ApplicationRecord
has_recommended :products
end
Specify multiple types of recommendations for a model with:
class User < ApplicationRecord
has_recommended :products
has_recommended :products_v2, class_name: "Product"
end
And use the appropriate methods:
user.update_recommended_products_v2(recs)
user.recommended_products_v2
For Rails < 6, speed up inserts by adding activerecord-import to your app.
Storing Recommenders
If you’d prefer to perform recommendations on-the-fly, store the recommender
bin = Marshal.dump(recommender)
File.binwrite("recommender.bin", bin)
You can save it to a file, database, or any other storage system
Load a recommender
bin = File.binread("recommender.bin")
recommender = Marshal.load(bin)
Algorithms
Disco uses high-performance matrix factorization.
- For explicit feedback, it uses stochastic gradient descent
- For implicit feedback, it uses coordinate descent
Specify the number of factors and epochs
Disco::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.
Validation
Pass a validation set with:
recommender.fit(data, validation_set: validation_set)
Cold Start
Collaborative filtering suffers from the cold start problem. It’s unable to make good recommendations without data on a user or item, which is problematic for new users and items.
recommender.user_recs(new_user_id) # returns empty array
There are a number of ways to deal with this, but here are some common ones:
- For user-based recommendations, show new users the most popular items.
- For item-based recommendations, make content-based recommendations with a gem like tf-idf-similarity.
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 Daru data frame
Daru::DataFrame.from_csv("ratings.csv")
Reference
Get the global mean
recommender.global_mean
Get the factors
recommender.user_factors
recommender.item_factors
Credits
Thanks to:
- LIBMF for providing high performance matrix factorization
- Implicit for serving as an initial reference for user and item similarity
- @dasch for the gem name
History
View the changelog
Contributing
Everyone is encouraged to help improve this project. Here are a few ways you can help:
- Report bugs
- Fix bugs and submit pull requests
- Write, clarify, or fix documentation
- Suggest or add new features