Source code for cornac.explainer.exp_narre

import numpy as np
from .explainer import Explainer
# from tensorflow.python.keras.preprocessing.sequence import pad_sequences
from keras.utils import pad_sequences
import tensorflow as tf

[docs] class Exp_NARRE(Explainer): """Explainer for Neural Attentional Rating Regression with Review-level Explanations (NARRE). Explains the recommendation by the attention scores of the reviews. Parameters ---------- rec_model: object, recommender model The recommender model to be explained. dataset: object, dataset The dataset object that is used to explain. name: string, optional, default: 'Exp_NARRE' References ---------- [1] hen, C., Zhang, M., Liu, Y., & Ma, S. (2018, April). Neural attentional rating regression with review-level explanations. In Proceedings of the 2018 World Wide Web Conference (pp. 1583-1592). """ def __init__(self, rec_model, dataset, name="Exp_NARRE"): super().__init__(name=name, rec_model=rec_model, dataset=dataset) self.all_item_attention = [] self.all_item_review_ids = []
[docs] def explain_one_recommendation_to_user(self, user_id, item_id, **kwargs): """Provide explanation for one user and one item Parameters ---------- user_id: str One user's id. item_id: str One item's id. feature_k: int, optional, default:10 Number of features in explanations created by explainer. Returns ------- explanations: dict dictionary with review text as key and attention score as value. """ explanation = {} if self.model is None: raise NotImplementedError("The model is None.") if not hasattr(self.model, "model"): raise AttributeError("The explainer does not support this recommender.") if self.model.model.graph is None: raise NotImplementedError("The model is not trained yet.") feature_k = kwargs.get('feature_k', 10) narre = self.model.model train_set = self.dataset if hasattr(train_set, 'train_set'): train_set = train_set.train_set if len(self.all_item_attention) == 0: all_item_review_ids = [] all_item_attention = [] for batch_items in train_set.item_iter(64): i_item_review, i_item_uid_review, i_item_num_reviews, i_item_review_ids = _get_data(batch_items, train_set, narre.max_text_length, by='item', max_num_review=narre.max_num_review) all_item_review_ids.extend(i_item_review_ids) item_review_embedding = narre.graph.l_item_review_embedding(i_item_review) item_review_h = narre.graph.item_text_processor(item_review_embedding, training=False) a_item = narre.graph.a_item(tf.concat([item_review_h, narre.graph.l_item_uid_embedding(i_item_uid_review)], axis=-1)) a_item_masking = tf.expand_dims(tf.sequence_mask(tf.reshape(i_item_num_reviews, [-1]), maxlen=i_item_review.shape[1]), -1) item_attention = narre.graph.item_attention(a_item, a_item_masking) item_attention = item_attention.numpy() all_item_attention.extend(item_attention) all_item_attention = np.array(all_item_attention) all_item_review_ids = np.array(all_item_review_ids) all_item_attention = np.squeeze(all_item_attention, axis=-1) self.all_item_attention = all_item_attention self.all_item_review_ids = all_item_review_ids item_idx = self.dataset.iid_map[item_id] if self.model.is_unknown_item(item_idx): return explanation attention = self.all_item_attention[item_idx] review_ids = self.all_item_review_ids[item_idx] top_k = np.argsort(attention)[::-1][:feature_k] for idx in top_k: # explanation.append((attention[idx], train_set.review_text.reviews[review_ids[idx]])) text = train_set.review_text.reviews[review_ids[idx]] explanation[text] = attention[idx] return explanation
def _get_data(batch_ids, train_set, max_text_length, by='user', max_num_review=None): batch_reviews, batch_id_reviews, batch_num_reviews = [], [], [] batch_reviews_ids = [] review_group = train_set.review_text.user_review if by == 'user' else train_set.review_text.item_review for idx in batch_ids: ids, review_ids = [], [] for inc, (jdx, review_idx) in enumerate(review_group[idx].items()): if max_num_review is not None and inc == max_num_review: break ids.append(jdx) review_ids.append(review_idx) batch_id_reviews.append(ids) batch_reviews_ids.append(review_ids) reviews = train_set.review_text.batch_seq(review_ids, max_length=max_text_length) batch_reviews.append(reviews) batch_num_reviews.append(len(reviews)) batch_reviews = pad_sequences(batch_reviews, maxlen=max_num_review, padding="post") batch_id_reviews = pad_sequences(batch_id_reviews, maxlen=max_num_review, padding="post") batch_num_reviews = np.array(batch_num_reviews) batch_reviews_ids = pad_sequences(batch_reviews_ids, maxlen=max_num_review, padding="post") return batch_reviews, batch_id_reviews, batch_num_reviews, batch_reviews_ids