Railsでアプリを作成した際に、ユーザーベースの協調フィルタリングに基づく、独自アルゴリズムでレコメンド機能を実装しましたので、学習メモとして紹介させていただこうと思います。
簡単な処理の流れとしまして、
「ユーザーがブックマークしたクイズAと、他ユーザーがブックマークしたクイズAが一致したとき、他ユーザーがブックマークしている他のクイズをレコメンドする」という流れになります
現在、初学者で学習中のため、内容に誤りがある場合がございます!
その際は、コメント等で教えていただけると幸いです!
レコメンド機能
レコメンド
投稿されたまたは事前に用意されたコンテンツと類似したコンテンツを推奨する機能をレコメンドと呼びます。代表的なアルゴリズムとして、協調フィルタリングとコンテンツフィルタリングがあります。
協調フィルタリング
協調フィルタリングは、多くのユーザの嗜好情報を蓄積し、あるユーザと嗜好の類似した他のユーザの情報を用いて自動的に推論を行う方法論です。趣味の似た人からの意見を参考にするという口コミの原理に例えられることが多いです。
コンテンツフィルタリング(内容ベース)
ユーザーではなく商品側に何かしらの特徴量を付与し、特徴が似ている商品を推薦するレコメンドアルゴリズムです。 対象ユーザーのデータさえあれば推薦を行うことができるので、コールドスタート問題を回避することが出来るという特徴があります。
協調フィルタリング
今回の実装は、ユーザーベースの協調フィルタリングに基づくように実装していきます
参考:
前提条件
今回は、Webアプリで作成した際の実装になっております
このようなテーブル構成となっており、必要なテーブルは赤枠になります
主となるテーブルは、Usersテーブル・Resultsテーブル・Bookmarksテーブル・Questionsテーブルです
モデル(必要コードのみ記載)
class User < ApplicationRecord
authenticates_with_sorcery!
has_many :results, dependent: :destroy
has_many :questions, through: :results
has_many :bookmarks, dependent: :destroy
has_many :bookmarks_questions, through: :bookmarks, source: :question
has_one_attached :avatar
end
class Result < ApplicationRecord
belongs_to :question
belongs_to :user
end
class Question < ApplicationRecord
has_many :results, dependent: :destroy
has_many :bookmarks, dependent: :destroy
has_many :users, through: :bookmarks
end
class Bookmark < ApplicationRecord
belongs_to :user
belongs_to :question
validates :user_id, uniqueness: { scope: :question_id }
end
コード
実際にコードを見ていきます
before_action :set_recommend_questions, only: %i[index recommend_explanation]
def index; end
def recommend_explanation; end
private
# レコメンドクイズを設定します
def set_recommend_questions
@recommend_questions = recommend_questions
end
#類似ユーザーを探す
def similar_users
bookmarked_question_ids = current_user.bookmarks_questions.pluck(:question_id)
similar_user_ids = Bookmark.where(question_id: bookmarked_question_ids)
.where.not(user_id: current_user.id)
.distinct.pluck(:user_id)
User.where(id: similar_user_ids)
end
#類似ユーザーのブックマークからクイズを提案する
def recommend_questions
bookmarked_question_ids = current_user.bookmarks_questions.pluck(:question_id)
similar_users = similar_users
similar_user_question_ids = Bookmark.where(user_id: similar_users.ids)
.where.not(question_id: bookmarked_question_ids)
.distinct.pluck(:question_id)
recommended_question_ids = Question.where(id: similar_user_question_ids)
.where.not(id: bookmarked_question_ids)
.where.not(id: Result.where(user_id: current_user)
.distinct.pluck(:question_id)).pluck(:id)
Question.where(id: recommended_question_ids).sample(3)
コードを紐解く
まず、類似しているユーザーを探します
def find_similar_users
#current_userがブックマークしたクイズのquestion_idをbookmarked_question_idsに定義
bookmarked_question_ids = current_user.bookmarks_questions.pluck(:question_id)
#ブックマークテーブルから、ブックマークしたquestion_idから、そのquestion_idを同様にブックマークしたユーザーを取得します(この時、自分のuser_idが入らないようにしています)
similar_user_ids = Bookmark.where(question_id: bookmarked_question_ids)
.where.not(user_id: current_user.id)
.distinct.pluck(:user_id)
#類似しているユーザーを取得します
User.where(id: similar_user_ids)
end
次に、類似しているユーザーから、おすすめのクイズを探して提供します
#近しいユーザーのブックマークからクイズを提案する
def recommend_questions
bookmarked_question_ids = current_user.bookmarks_questions.pluck(:question_id)
#find_similar_usersで取得したユーザーをsimilar_usersで定義します
similar_users = find_similar_users
#定義したユーザーがブックマークしているクイズを取得します
similar_user_question_ids = Bookmark.where(user_id: similar_users.ids)
#ユーザーAがブックマークした以外のクイズを取得
.where.not(question_id: bookmarked_question_ids)
.distinct.pluck(:question_id)
#取得したクイズの中で、今までクイズに挑戦していないクイズのみ取得します
recommended_question_ids = Question.where(id: similar_user_question_ids)
.where.not(id: bookmarked_question_ids)
.where.not(id: Result.where(user_id: current_user)
.distinct.pluck(:question_id))
.pluck(:id)
#取得したクイズ3問を出力します
Question.where(id: recommended_question_ids).sample(3)
最後に
今回のレコメンド機能は、高度なアルゴリズムではなく、簡単なアルゴリズムで書いています。
これから、高度なアルゴリズムでレコメンド機能を実装できるように頑張ろうと思います。
最後まで読んでいただき、ありがとうございます!