Coverage for hopwise/model/general_recommender/slimelastic.py: 90%
50 statements
« prev ^ index » next coverage.py v7.16.2, created at 2026-09-30 13:25 +0000
« prev ^ index » next coverage.py v7.16.2, created at 2026-09-30 13:25 +0000
1r"""SLIMElastic
2################################################
3Reference:
4 Xia Ning et al. "SLIM: Sparse Linear Methods for Top-N Recommender Systems." in ICDM 2011.
5Reference code:
6 https://github.com/KarypisLab/SLIM
7 https://github.com/MaurizioFD/RecSys2019_DeepLearning_Evaluation/blob/master/SLIM_ElasticNet/SLIMElasticNetRecommender.py
8"""
10import warnings
12import numpy as np
13import scipy.sparse as sp
14import torch
15from sklearn.exceptions import ConvergenceWarning
16from sklearn.linear_model import ElasticNet
18from hopwise.model.abstract_recommender import GeneralRecommender
19from hopwise.utils import InputType, ModelType
22class SLIMElastic(GeneralRecommender):
23 r"""SLIMElastic is a sparse linear method for top-K recommendation, which learns
24 a sparse aggregation coefficient matrix by solving an L1-norm and L2-norm
25 regularized optimization problem.
27 """
29 input_type = InputType.POINTWISE
30 type = ModelType.TRADITIONAL
32 def __init__(self, config, dataset):
33 super().__init__(config, dataset)
35 # load parameters info
36 self.hide_item = config["hide_item"]
37 self.alpha = config["alpha"]
38 self.l1_ratio = config["l1_ratio"]
39 self.positive_only = config["positive_only"]
41 # need at least one param
42 self.dummy_param = torch.nn.Parameter(torch.zeros(1))
44 X = dataset.inter_matrix(form="csr").astype(np.float32)
45 X = X.tolil()
46 self.interaction_matrix = X
48 model = ElasticNet(
49 alpha=self.alpha,
50 l1_ratio=self.l1_ratio,
51 positive=self.positive_only,
52 fit_intercept=False,
53 copy_X=False,
54 precompute=True,
55 selection="random",
56 max_iter=100,
57 tol=1e-4,
58 )
59 item_coeffs = []
61 # ignore ConvergenceWarnings
62 with warnings.catch_warnings():
63 warnings.simplefilter("ignore", category=ConvergenceWarning)
65 for j in range(X.shape[1]):
66 # target column
67 r = X[:, j]
69 if self.hide_item:
70 # set item column to 0
71 X[:, j] = 0
73 # fit the model
74 model.fit(X, r.todense().getA1())
76 # store the coefficients
77 coeffs = model.sparse_coef_
79 item_coeffs.append(coeffs)
81 if self.hide_item:
82 # reattach column if removed
83 X[:, j] = r
85 self.item_similarity = sp.vstack(item_coeffs).T
86 self.other_parameter_name = ["interaction_matrix", "item_similarity"]
88 def forward(self):
89 pass
91 def calculate_loss(self, interaction):
92 return torch.nn.Parameter(torch.zeros(1))
94 def predict(self, interaction):
95 user = interaction[self.USER_ID].cpu().numpy()
96 item = interaction[self.ITEM_ID].cpu().numpy()
98 r = torch.from_numpy(
99 (self.interaction_matrix[user, :].multiply(self.item_similarity[:, item].T)).sum(axis=1).getA1()
100 ).to(self.device)
102 return r
104 def full_sort_predict(self, interaction):
105 user = interaction[self.USER_ID].cpu().numpy()
107 r = self.interaction_matrix[user, :] @ self.item_similarity
108 r = torch.from_numpy(r.todense().getA1())
110 return r