Disco
:fire: 使用协同过滤为Ruby和Rails提供推荐
- 支持基于用户和基于项目的推荐
- 适用于显式和隐式反馈
- 使用高性能矩阵分解
安装
在应用程序的Gemfile中添加以下行:
gem "disco"
入门
创建一个推荐器
recommender = Disco::Recommender.new
如果用户直接对项目进行评分,这被称为显式反馈。使用以下方式训练推荐器:
recommender.fit([
{user_id: 1, item_id: 1, rating: 5},
{user_id: 2, item_id: 1, rating: 3}
])
ID可以是整数、字符串或任何其他数据类型
如果用户不直接对项目进行评分(例如,他们在购买商品或阅读文章),这被称为隐式反馈。此时不需要包含评分。
recommender.fit([
{user_id: 1, item_id: 1},
{user_id: 2, item_id: 1}
])
每个
user_id
/item_id
组合应该只出现一次
获取基于用户的推荐 - "与你相似的用户也喜欢"
recommender.user_recs(user_id)
获取基于项目的推荐 - "喜欢这个项目的用户也喜欢"
recommender.item_recs(item_id)
使用count
选项指定推荐数量(默认为5)
recommender.user_recs(user_id, count: 3)
获取特定用户和项目的预测评分
recommender.predict([{user_id: 1, item_id: 2}, {user_id: 2, item_id: 4}])
获取相似用户
recommender.similar_users(user_id)
示例
MovieLens
加载数据
data = Disco.load_movielens
创建推荐器并获取相似电影
recommender = Disco::Recommender.new(factors: 20)
recommender.fit(data)
recommender.item_recs("Star Wars (1977)")
Ahoy
Ahoy是一个很好的隐式反馈来源
views = Ahoy::Event.where(name: "Viewed post").group(:user_id).group_prop(:post_id).count
data =
views.map do |(user_id, post_id), _|
{
user_id: user_id,
item_id: post_id
}
end
创建推荐器并为用户获取推荐文章
recommender = Disco::Recommender.new
recommender.fit(data)
recommender.user_recs(current_user.id)
存储推荐
Disco使在Rails中存储推荐变得简单。
rails generate disco:recommendation
rails db:migrate
对于基于用户的推荐,使用:
class User < ApplicationRecord
has_recommended :products
end
将
:products
更改为与您要推荐的模型相匹配
保存推荐
User.find_each do |user|
recs = recommender.user_recs(user.id)
user.update_recommended_products(recs)
end
获取推荐
user.recommended_products
对于基于项目的推荐,使用:
class Product < ApplicationRecord
has_recommended :products
end
为一个模型指定多种类型的推荐:
class User < ApplicationRecord
has_recommended :products
has_recommended :products_v2, class_name: "Product"
end
并使用相应的方法:
user.update_recommended_products_v2(recs)
user.recommended_products_v2
存储推荐器
如果您更喜欢即时进行推荐,可以存储推荐器
json = recommender.to_json
File.write("recommender.json", json)
序列化的推荐器包含训练数据中的用户活动(以避免推荐之前已评分的项目),所以请确保保护它。您可以将其保存到文件、数据库或任何其他存储系统,或使用Trove等工具。此外,用户和项目ID应为整数或字符串。
加载推荐器
json = File.read("recommender.json")
recommender = Disco::Recommender.load_json(json)
另外,您也可以只存储因子,并使用像Neighbor这样的库。请参阅示例。
算法
Disco使用高性能矩阵分解。
指定因子数和迭代次数
Disco::Recommender.new(factors: 8, epochs: 20)
如果推荐看起来不太理想,可以尝试修改"factors"参数。默认值是8,但对某些应用来说3可能更好,对其他应用300可能更合适。
验证
通过以下方式传入验证集:
recommender.fit(data, validation_set: validation_set)
冷启动
协同过滤存在冷启动问题。在没有用户或项目数据的情况下,它无法做出好的推荐,这对新用户和新项目来说是个问题。
recommender.user_recs(new_user_id) # 返回空数组
有多种方法可以解决这个问题,以下是一些常见的方法:
- 对于基于用户的推荐,向新用户展示最受欢迎的项目
- 对于基于项目的推荐,使用像tf-idf-similarity这样的gem进行基于内容的推荐
获取热门项目:
recommender = Disco::Recommender.new(top_items: true)
recommender.fit(data)
recommender.top_items
这对显式反馈使用Wilson分数,对隐式反馈使用项目频率。
数据
数据可以是哈希数组
[{user_id: 1, item_id: 1, rating: 5}, {user_id: 2, item_id: 1, rating: 3}]
或Rover数据框
Rover.read_csv("ratings.csv")
或Daru数据框
Daru::DataFrame.from_csv("ratings.csv")
性能
如果你有大量用户或项目,可以使用近似最近邻库如Faiss来提高某些方法的性能。
在应用的Gemfile中添加以下行:
gem "faiss"
优化user_recs
方法:
recommender.optimize_user_recs
优化item_recs
方法:
recommender.optimize_item_recs
优化similar_users
方法:
recommender.optimize_similar_users
这应在拟合或加载推荐器后调用。
参考
获取ID
recommender.user_ids
recommender.item_ids
获取全局平均值
recommender.global_mean
获取因子
recommender.user_factors
recommender.item_factors
获取特定用户和项目的因子
recommender.user_factors(user_id)
recommender.item_factors(item_id)
致谢
感谢:
历史
查看更新日志
贡献
我们鼓励每个人帮助改进这个项目。以下是一些你可以帮助的方式:
开始开发:
git clone https://github.com/ankane/disco.git
cd disco
bundle install
bundle exec rake test