Source code for elliot.recommender.knowledge_aware.kaHFM.kahfm

import time
import numpy as np
import pickle
import typing as t
from tqdm import tqdm


from elliot.dataset.samplers import custom_sampler as cs
from elliot.recommender.base_recommender_model import BaseRecommenderModel
from elliot.recommender.recommender_utils_mixin import RecMixin
from elliot.utils.write import store_recommendation
from elliot.recommender.knowledge_aware.kaHFM.tfidf_utils import TFIDF
from elliot.recommender.knowledge_aware.kaHFM.kahfm_model import KAHFMModel
from elliot.recommender.base_recommender_model import init_charger


[docs]class KaHFM(RecMixin, BaseRecommenderModel): r""" Knowledge-aware Hybrid Factorization Machines Vito Walter Anelli and Tommaso Di Noia and Eugenio Di Sciascio and Azzurra Ragone and Joseph Trotta "How to Make Latent Factors Interpretable by Feeding Factorization Machines with Knowledge Graphs", ISWC 2019 Best student Research Paper For further details, please refer to the `paper <https://doi.org/10.1007/978-3-030-30793-6_3>`_ Vito Walter Anelli and Tommaso Di Noia and Eugenio Di Sciascio and Azzurra Ragone and Joseph Trotta "Semantic Interpretation of Top-N Recommendations", IEEE TKDE 2020 For further details, please refer to the `paper <https://doi.org/10.1109/TKDE.2020.3010215>`_ Args: lr: learning rate (default: 0.05) bias_regularization: Bias regularization (default: 0) user_regularization: User regularization (default: 0.0025) positive_item_regularization: regularization for positive (experienced) items (default: 0.0025) negative_item_regularization: regularization for unknown items (default: 0.00025) update_negative_item_factors: Boolean to update negative item factors (default: True) update_users: Boolean to update user factors (default: True) update_items: Boolean to update item factors (default: True) update_bias: Boolean to update bias value (default: True) To include the recommendation model, add it to the config file adopting the following pattern: .. code:: yaml models: KaHFM: meta: hyper_max_evals: 20 hyper_opt_alg: tpe validation_rate: 1 verbose: True save_weights: True save_recs: True validation_metric: nDCG@10 epochs: 100 batch_size: -1 lr: 0.05 bias_regularization: 0 user_regularization: 0.0025 positive_item_regularization: 0.0025 negative_item_regularization: 0.00025 update_negative_item_factors: True update_users: True update_items: True update_bias: True """ @init_charger def __init__(self, data, config, params, *args, **kwargs): self._params_list = [ ("_learning_rate", "lr", "lr", 0.05, None, None), ("_bias_regularization", "bias_regularization", "b_reg", 0, None, None), ("_user_regularization", "user_regularization", "u_reg", 0.0025, None, None), ("_positive_item_regularization", "positive_item_regularization", "pos_i_reg", 0.0025, None, None), ("_negative_item_regularization", "negative_item_regularization", "neg_it_reg", 0.00025, None, None), # ("_update_negative_item_factors", "update_negative_item_factors", "update_neg_item_factors",True, # None, None), # ("_update_users", "update_users", "update_users", True, None, None), # ("_update_items", "update_items", "update_items", True, None, None), # ("_update_bias", "update_bias", "update_bias", True, None, None), ("_loader", "loader", "load", "ChainedKG", None, None), ] self.autoset_params() self._sample_negative_items_empirically = True self._ratings = self._data.train_dict self._side = getattr(self._data.side_information, self._loader, None) self._tfidf_obj = TFIDF(self._side.feature_map) self._tfidf = self._tfidf_obj.tfidf() self._user_profiles = self._tfidf_obj.get_profiles(self._ratings) self._model = KAHFMModel(self._data, self._side, self._tfidf, self._user_profiles, self._learning_rate, self._user_regularization, self._bias_regularization, self._positive_item_regularization, self._negative_item_regularization) self._embed_k = self._model.get_factors() self._sampler = cs.Sampler(self._data.i_train_dict) self._batch_size = 10000
[docs] def get_recommendations(self, k: int = 10): self._model.prepare_predictions() predictions_top_k_val = {} predictions_top_k_test = {} recs_val, recs_test = self.process_protocol(k) predictions_top_k_val.update(recs_val) predictions_top_k_test.update(recs_test) return predictions_top_k_val, predictions_top_k_test
[docs] def get_single_recommendation(self, mask, k, *args): return {u: self._model.get_user_predictions(u, mask, k) for u in self._ratings.keys()}
# def get_recommendations(self, k: int = 100): # return {u: self._model.get_user_recs(u, k) for u in self._ratings.keys()} # def predict(self, u: int, i: int): # """ # Get prediction on the user item pair. # # Returns: # A single float vaue. # """ # return self._model.predict(u, i) @property def name(self): return "KaHFM" \ + f"_{self.get_base_params_shortcut()}" \ + f"_{self.get_params_shortcut()}" # def train_step(self): # if self._restore: # return self.restore_weights() # # start_it = time.perf_counter() # print() # print("Sampling...") # samples = self._sampler.step(self._data.transactions) # start = time.perf_counter() # print(f"Sampled in {round(start-start_it, 2)} seconds") # start = time.perf_counter() # print("Computing..") # for u, i, j in samples: # self.update_factors(u, i, j) # t2 = time.perf_counter() # print(f"Computed and updated in {round(t2-start, 2)} seconds")
[docs] def train(self): if self._restore: return self.restore_weights() print(f"Transactions: {self._data.transactions}") for it in self.iterate(self._epochs): print(f"\n********** Iteration: {it + 1}") loss = 0 steps = 0 with tqdm(total=int(self._data.transactions // self._batch_size), disable=not self._verbose) as t: for batch in self._sampler.step(self._data.transactions, self._batch_size): steps += 1 self._model.train_step(batch) t.update() self.evaluate(it)
# def update_factors(self, u: int, i: int, j: int): # user_factors = self._model.get_user_factors(u) # item_factors_i = self._model.get_item_factors(i) # item_factors_j = self._model.get_item_factors(j) # item_bias_i = self._model.get_item_bias(i) # item_bias_j = self._model.get_item_bias(j) # # z = 1 / (1 + np.exp(self.predict(u, i) - self.predict(u, j))) # # update bias i # d_bi = (z - self._bias_regularization * item_bias_i) # self._model.set_item_bias(i, item_bias_i + (self._learning_rate * d_bi)) # # # update bias j # d_bj = (-z - self._bias_regularization * item_bias_j) # self._model.set_item_bias(j, item_bias_j + (self._learning_rate * d_bj)) # # # update user factors # d_u = ((item_factors_i - item_factors_j) * z - self._user_regularization * user_factors) # self._model.set_user_factors(u, user_factors + (self._learning_rate * d_u)) # # # update item i factors # d_i = (user_factors * z - self._positive_item_regularization * item_factors_i) # self._model.set_item_factors(i, item_factors_i + (self._learning_rate * d_i)) # # # update item j factors # d_j = (-user_factors * z - self._negative_item_regularization * item_factors_j) # self._model.set_item_factors(j, item_factors_j + (self._learning_rate * d_j))