From 1b50cbdaf3789d8ab8b7be2fe7ecd75aedbdc4ba Mon Sep 17 00:00:00 2001 From: hieuddo Date: Mon, 22 Apr 2024 08:36:23 +0000 Subject: [PATCH] fix .rank() method for multiple models --- cornac/models/comparer/recom_comparer_obj.pyx | 20 +++++++++---------- cornac/models/comparer/recom_comparer_sub.pyx | 2 +- cornac/models/efm/recom_efm.pyx | 2 +- cornac/models/lrppm/recom_lrppm.pyx | 2 +- cornac/models/recommender.py | 6 ++++-- 5 files changed, 17 insertions(+), 15 deletions(-) diff --git a/cornac/models/comparer/recom_comparer_obj.pyx b/cornac/models/comparer/recom_comparer_obj.pyx index 1c522b6f9..bd64945c0 100644 --- a/cornac/models/comparer/recom_comparer_obj.pyx +++ b/cornac/models/comparer/recom_comparer_obj.pyx @@ -663,39 +663,39 @@ class ComparERObj(Recommender): item_score = self.U2[item_id, :].dot(self.U1[user_id, :]) + self.H2[item_id, :].dot(self.H1[user_id, :]) return item_score - def rank(self, user_id, item_ids=None): + def rank(self, user_idx, item_indices=None, **kwargs): """Rank all test items for a given user. Parameters ---------- - user_id: int, required + user_idx: int, required The index of the user for whom to perform item raking. - item_ids: 1d array, optional, default: None + item_indices: 1d array, optional, default: None A list of candidate item indices to be ranked by the user. If `None`, list of ranked known item indices and their scores will be returned Returns ------- Tuple of `item_rank`, and `item_scores`. The order of values - in item_scores are corresponding to the order of their ids in item_ids + in item_scores are corresponding to the order of their ids in item_indices """ - X_ = self.U1[user_id, :].dot(self.V.T) + X_ = self.U1[user_idx, :].dot(self.V.T) most_cared_aspects_indices = (-X_).argsort()[:self.num_most_cared_aspects] most_cared_X_ = X_[most_cared_aspects_indices] most_cared_Y_ = self.U2.dot(self.V[most_cared_aspects_indices, :].T) explicit_scores = most_cared_X_.dot(most_cared_Y_.T) / (self.num_most_cared_aspects * self.rating_scale) - item_scores = self.alpha * explicit_scores + (1 - self.alpha) * self.score(user_id) + item_scores = self.alpha * explicit_scores + (1 - self.alpha) * self.score(user_idx) - if item_ids is None: + if item_indices is None: item_scores = item_scores item_rank = item_scores.argsort()[::-1] else: - num_items = max(self.num_items, max(item_ids) + 1) + num_items = max(self.num_items, max(item_indices) + 1) item_scores = np.ones(num_items) * np.min(item_scores) item_scores[:self.num_items] = item_scores item_rank = item_scores.argsort()[::-1] - item_rank = intersects(item_rank, item_ids, assume_unique=True) - item_scores = item_scores[item_ids] + item_rank = intersects(item_rank, item_indices, assume_unique=True) + item_scores = item_scores[item_indices] return item_rank, item_scores diff --git a/cornac/models/comparer/recom_comparer_sub.pyx b/cornac/models/comparer/recom_comparer_sub.pyx index e1eec1c77..be6593afe 100644 --- a/cornac/models/comparer/recom_comparer_sub.pyx +++ b/cornac/models/comparer/recom_comparer_sub.pyx @@ -759,7 +759,7 @@ class ComparERSub(MTER): return correct, skipped, loss, bpr_loss - def rank(self, user_idx, item_indices=None): + def rank(self, user_idx, item_indices=None, **kwargs): if self.alpha > 0 and self.n_top_aspects > 0: n_top_aspects = min(self.n_top_aspects, self.num_aspects) ts1 = np.einsum("abc,a->bc", self.G1, self.U[user_idx]) diff --git a/cornac/models/efm/recom_efm.pyx b/cornac/models/efm/recom_efm.pyx index 5d6dd582c..2fcc3406b 100644 --- a/cornac/models/efm/recom_efm.pyx +++ b/cornac/models/efm/recom_efm.pyx @@ -468,7 +468,7 @@ class EFM(Recommender): item_score = self.U2[item_idx, :].dot(self.U1[user_idx, :]) + self.H2[item_idx, :].dot(self.H1[user_idx, :]) return item_score - def rank(self, user_idx, item_indices=None): + def rank(self, user_idx, item_indices=None, **kwargs): """Rank all test items for a given user. Parameters diff --git a/cornac/models/lrppm/recom_lrppm.pyx b/cornac/models/lrppm/recom_lrppm.pyx index 2c8ec5475..71085a90d 100644 --- a/cornac/models/lrppm/recom_lrppm.pyx +++ b/cornac/models/lrppm/recom_lrppm.pyx @@ -516,7 +516,7 @@ class LRPPM(Recommender): item_score = self.I[i_idx].dot(self.U[u_idx]) return item_score - def rank(self, user_idx, item_indices=None): + def rank(self, user_idx, item_indices=None, **kwargs): if self.alpha > 0 and self.num_top_aspects > 0: n_items = self.num_items num_top_aspects = min(self.num_top_aspects, self.num_aspects) diff --git a/cornac/models/recommender.py b/cornac/models/recommender.py index c7080a4b2..ff6c69cd5 100644 --- a/cornac/models/recommender.py +++ b/cornac/models/recommender.py @@ -473,7 +473,7 @@ def rate(self, user_idx, item_idx, clipping=True): return rating_pred - def rank(self, user_idx, item_indices=None, k=-1, **kwargs): + def rank(self, user_idx, item_indices=None, **kwargs): """Rank all test items for a given user. Parameters @@ -485,7 +485,7 @@ def rank(self, user_idx, item_indices=None, k=-1, **kwargs): A list of candidate item indices to be ranked by the user. If `None`, list of ranked known item indices and their scores will be returned. - k: int, required + k: int, optional Cut-off length for recommendations, k=-1 will return ranked list of all items. This is more important for ANN to know the limit to avoid exhaustive ranking. @@ -502,6 +502,8 @@ def rank(self, user_idx, item_indices=None, k=-1, **kwargs): except ScoreException: known_item_scores = np.ones(self.total_items) * self.default_score() + k = kwargs.get("k", -1) + # check if the returned scores also cover unknown items # if not, all unknown items will be given the MIN score if len(known_item_scores) == self.total_items: