diff --git a/0.11.0 b/0.11.0 new file mode 100644 index 000000000..7d491e8ea --- /dev/null +++ b/0.11.0 @@ -0,0 +1,36 @@ +Collecting torch + Downloading torch-2.5.1-cp312-cp312-win_amd64.whl.metadata (28 kB) +Collecting torchvision + Downloading torchvision-0.20.1-cp312-cp312-win_amd64.whl.metadata (6.2 kB) +Collecting filelock (from torch) + Downloading filelock-3.16.1-py3-none-any.whl.metadata (2.9 kB) +Requirement already satisfied: typing-extensions>=4.8.0 in c:\python312\lib\site-packages\setuptools\_vendor (from torch) (4.12.2) +Collecting networkx (from torch) + Downloading networkx-3.4.2-py3-none-any.whl.metadata (6.3 kB) +Collecting jinja2 (from torch) + Downloading jinja2-3.1.4-py3-none-any.whl.metadata (2.6 kB) +Collecting fsspec (from torch) + Downloading fsspec-2024.10.0-py3-none-any.whl.metadata (11 kB) +Requirement already satisfied: setuptools in c:\python312\lib\site-packages (from torch) (75.3.0) +Collecting sympy==1.13.1 (from torch) + Downloading sympy-1.13.1-py3-none-any.whl.metadata (12 kB) +Requirement already satisfied: mpmath<1.4,>=1.1.0 in c:\python312\lib\site-packages (from sympy==1.13.1->torch) (1.3.0) +Requirement already satisfied: numpy in c:\python312\lib\site-packages (from torchvision) (1.26.4) +Requirement already satisfied: pillow!=8.3.*,>=5.3.0 in c:\python312\lib\site-packages (from torchvision) (10.2.0) +Collecting MarkupSafe>=2.0 (from jinja2->torch) + Downloading MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl.metadata (4.1 kB) +Downloading torch-2.5.1-cp312-cp312-win_amd64.whl (203.0 MB) + -------------------------------------- 203.0/203.0 MB 652.6 kB/s eta 0:00:00 +Downloading sympy-1.13.1-py3-none-any.whl (6.2 MB) + ---------------------------------------- 6.2/6.2 MB 781.5 kB/s eta 0:00:00 +Downloading torchvision-0.20.1-cp312-cp312-win_amd64.whl (1.6 MB) + ---------------------------------------- 1.6/1.6 MB 937.3 kB/s eta 0:00:00 +Downloading filelock-3.16.1-py3-none-any.whl (16 kB) +Downloading fsspec-2024.10.0-py3-none-any.whl (179 kB) + ---------------------------------------- 179.6/179.6 kB 2.7 MB/s eta 0:00:00 +Downloading jinja2-3.1.4-py3-none-any.whl (133 kB) + ---------------------------------------- 133.3/133.3 kB 2.0 MB/s eta 0:00:00 +Downloading networkx-3.4.2-py3-none-any.whl (1.7 MB) + ---------------------------------------- 1.7/1.7 MB 1.8 MB/s eta 0:00:00 +Downloading MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl (15 kB) +Installing collected packages: sympy, networkx, MarkupSafe, fsspec, filelock, jinja2, torch, torchvision diff --git a/1.10.0 b/1.10.0 new file mode 100644 index 000000000..c395d3772 --- /dev/null +++ b/1.10.0 @@ -0,0 +1,23 @@ +Collecting torch + Using cached torch-2.5.1-cp312-cp312-win_amd64.whl.metadata (28 kB) +Collecting filelock (from torch) + Using cached filelock-3.16.1-py3-none-any.whl.metadata (2.9 kB) +Requirement already satisfied: typing-extensions>=4.8.0 in c:\python312\lib\site-packages\setuptools\_vendor (from torch) (4.12.2) +Collecting networkx (from torch) + Using cached networkx-3.4.2-py3-none-any.whl.metadata (6.3 kB) +Collecting jinja2 (from torch) + Using cached jinja2-3.1.4-py3-none-any.whl.metadata (2.6 kB) +Collecting fsspec (from torch) + Using cached fsspec-2024.10.0-py3-none-any.whl.metadata (11 kB) +Requirement already satisfied: setuptools in c:\python312\lib\site-packages (from torch) (75.3.0) +Requirement already satisfied: sympy==1.13.1 in c:\python312\lib\site-packages (from torch) (1.13.1) +Requirement already satisfied: mpmath<1.4,>=1.1.0 in c:\python312\lib\site-packages (from sympy==1.13.1->torch) (1.3.0) +Collecting MarkupSafe>=2.0 (from jinja2->torch) + Using cached MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl.metadata (4.1 kB) +Using cached torch-2.5.1-cp312-cp312-win_amd64.whl (203.0 MB) +Using cached filelock-3.16.1-py3-none-any.whl (16 kB) +Using cached fsspec-2024.10.0-py3-none-any.whl (179 kB) +Using cached jinja2-3.1.4-py3-none-any.whl (133 kB) +Using cached networkx-3.4.2-py3-none-any.whl (1.7 MB) +Using cached MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl (15 kB) +Installing collected packages: networkx, MarkupSafe, fsspec, filelock, jinja2, torch diff --git a/cornac/datasets/movielens.py b/cornac/datasets/movielens.py index 99aa92b4d..b2f0ddc8a 100644 --- a/cornac/datasets/movielens.py +++ b/cornac/datasets/movielens.py @@ -77,7 +77,7 @@ def load_feedback(fmt="UIR", variant="100K", reader=None): data: array-like Data in the form of a list of tuples depending on the given data format. """ - + fmt = validate_format(fmt, VALID_DATA_FORMATS) ml = ML_DATASETS.get(variant.upper(), None) diff --git a/cornac/experiment/experiment.py b/cornac/experiment/experiment.py index 139417908..d4d8bfd01 100644 --- a/cornac/experiment/experiment.py +++ b/cornac/experiment/experiment.py @@ -157,7 +157,7 @@ def run(self): if self.val_result is not None: output += "\nVALIDATION:\n...\n{}".format(self.val_result) output += "\nTEST:\n...\n{}".format(self.result) - + # print('helllooooooooooooow') print(output) timestamp = datetime.now().strftime("%Y-%m-%d_%H-%M-%S-%f") diff --git a/cornac/metrics/rating.py b/cornac/metrics/rating.py index de68bc053..4ff428aa7 100644 --- a/cornac/metrics/rating.py +++ b/cornac/metrics/rating.py @@ -110,7 +110,7 @@ def compute(self, gt_ratings, pd_ratings, weights=None, **kwargs): ------- mse: A scalar. Mean Squared Error. - + """ mse = np.average((gt_ratings - pd_ratings) ** 2, axis=0, weights=weights) return mse diff --git a/cornac/models/__init__.py b/cornac/models/__init__.py index 9507dbc50..386a98879 100644 --- a/cornac/models/__init__.py +++ b/cornac/models/__init__.py @@ -84,3 +84,4 @@ from .vbpr import VBPR from .vmf import VMF from .wmf import WMF +from .globallocalkernel import GlobalLocalKernel diff --git a/cornac/models/globallocalkernel/__init__.py b/cornac/models/globallocalkernel/__init__.py new file mode 100644 index 000000000..d960142e7 --- /dev/null +++ b/cornac/models/globallocalkernel/__init__.py @@ -0,0 +1 @@ +from .recom_globallocalkernel import GlobalLocalKernel \ No newline at end of file diff --git a/cornac/models/globallocalkernel/recom_globallocalkernel.py b/cornac/models/globallocalkernel/recom_globallocalkernel.py new file mode 100644 index 000000000..cbc8b4849 --- /dev/null +++ b/cornac/models/globallocalkernel/recom_globallocalkernel.py @@ -0,0 +1,491 @@ +import torch +import torch.nn as nn +import torch.nn.functional as F +import numpy as np +import time +import sys +import logging + + +from ..recommender import Recommender + +from tqdm import tqdm + + +# Configure logging +# logging.basicConfig(level=logging.INFO, format="%(message)s") +# =========================== +# Define your model layers +# =========================== + +def local_kernel(u, v): + dist = torch.norm(u - v, p=2, dim=2) + hat = torch.clamp(1. - dist**2, min=0.) + return hat + +class KernelLayer(nn.Module): + def __init__(self, n_in, n_hid, n_dim, lambda_s, lambda_2, activation=nn.Sigmoid()): + super().__init__() + self.W = nn.Parameter(torch.randn(n_in, n_hid)) + self.u = nn.Parameter(torch.randn(n_in, 1, n_dim)) + self.v = nn.Parameter(torch.randn(1, n_hid, n_dim)) + self.b = nn.Parameter(torch.randn(n_hid)) + + self.lambda_s = lambda_s + self.lambda_2 = lambda_2 + + # nn.init.xavier_uniform_(self.W, gain=torch.nn.init.calculate_gain("sigmoid")) + # nn.init.xavier_uniform_(self.u, gain=torch.nn.init.calculate_gain("sigmoid")) + # nn.init.xavier_uniform_(self.v, gain=torch.nn.init.calculate_gain("sigmoid")) + + nn.init.kaiming_uniform_(self.W,nonlinearity = "sigmoid") + nn.init.kaiming_uniform_(self.u, nonlinearity = "sigmoid") + nn.init.kaiming_uniform_(self.v,nonlinearity = "sigmoid") + + nn.init.zeros_(self.b) + self.activation = activation + + def forward(self, x): + w_hat = local_kernel(self.u, self.v) + sparse_reg = torch.nn.functional.mse_loss(w_hat, torch.zeros_like(w_hat)) + sparse_reg_term = self.lambda_s * sparse_reg + l2_reg = torch.nn.functional.mse_loss(self.W, torch.zeros_like(self.W)) + l2_reg_term = self.lambda_2 * l2_reg + + W_eff = self.W * w_hat # Local kernelised weight matrix + y = torch.matmul(x, W_eff) + self.b + y = self.activation(y) + + return y, sparse_reg_term + l2_reg_term + +class KernelNet(nn.Module): + def __init__(self, n_u, n_hid, n_dim, n_layers, lambda_s, lambda_2): + super().__init__() + layers = [] + for i in range(n_layers): + if i == 0: + + layers.append(KernelLayer(n_u, n_hid, n_dim, lambda_s, lambda_2)) + else: + + layers.append(KernelLayer(n_hid, n_hid, n_dim, lambda_s, lambda_2)) + # Output layer + layers.append(KernelLayer(n_hid, n_u, n_dim, lambda_s, lambda_2, activation=nn.Identity())) + self.layers = nn.ModuleList(layers) + self.dropout = nn.Dropout(0.33) + + def forward(self, x): + total_reg = None + for i, layer in enumerate(self.layers): + x, reg = layer(x) + if i < len(self.layers)-1: + x = self.dropout(x) + total_reg = reg if total_reg is None else total_reg + reg + return x, total_reg + +class CompleteNet(nn.Module): + def __init__(self, kernel_net, n_u, n_m, n_hid, n_dim, n_layers, lambda_s, lambda_2, gk_size, dot_scale): + super().__init__() + self.gk_size = gk_size + self.dot_scale = dot_scale + self.local_kernel_net = kernel_net + self.conv_kernel = torch.nn.Parameter(torch.randn(n_m, gk_size**2) * 0.1) + nn.init.xavier_uniform_(self.conv_kernel, gain=torch.nn.init.calculate_gain("relu")) + + def forward(self, x, x_local): + gk = self.global_kernel(x_local, self.gk_size, self.dot_scale) + x = self.global_conv(x, gk) + x, global_reg_loss = self.local_kernel_net(x) + return x, global_reg_loss + + def global_kernel(self, input, gk_size, dot_scale): + avg_pooling = torch.mean(input, dim=1) # Item based average pooling (axis=1) + avg_pooling = avg_pooling.view(1, -1) + gk = torch.matmul(avg_pooling, self.conv_kernel) * dot_scale + gk = gk.view(1, 1, gk_size, gk_size) + return gk + + def global_conv(self, input, W): + input = input.unsqueeze(0).unsqueeze(0) + conv2d = nn.LeakyReLU()(F.conv2d(input, W, stride=1, padding=1)) + return conv2d.squeeze(0).squeeze(0) + +class Loss(nn.Module): + def forward(self, pred_p, reg_loss, train_m, train_r): + diff = train_m * (train_r - pred_p) + sqE = torch.nn.functional.mse_loss(diff, torch.zeros_like(diff)) + loss_p = sqE + reg_loss + return loss_p + +# =========================== +# Cornac Recommender Wrapper +# =========================== + +class GlobalLocalKernel(Recommender): + """Global-Local Kernel Recommender. + + Parameters + ---------- + n_hid: int, default: 64 + Size of the hidden dimension. + n_dim: int, default: 10 + Kernel dimension. + n_layers: int, default: 2 + Number of kernel layers (not counting the final output layer). + lambda_s: float, default: 0.001 + Sparsity regularization term. + lambda_2: float, default: 0.001 + L2 regularization term. + gk_size: int, default: 3 + Size of the global kernel. + dot_scale: float, default: 0.1 + Scaling factor for the global kernel. + max_epoch_p: int, default: 100 + Max epochs for pre-training phase. + max_epoch_f: int, default: 100 + Max epochs for fine-tuning phase. + tol_p: float, default: 1e-4 + Tolerance for early stopping in pre-training. + tol_f: float, default: 1e-4 + Tolerance for early stopping in fine-tuning. + patience_p: int, default: 10 + Patience for early stopping in pre-training. + patience_f: int, default: 10 + Patience for early stopping in fine-tuning. + lr: float, default: 0.001 + Learning rate. + verbose: bool, default: False + Whether to print training progress. + device: str, default: 'auto' + 'cpu', 'cuda', or 'auto' to automatically detect GPU. + """ + + def __init__( + self, + n_hid=1, # size of hidden layers + n_dim=2, # inner AE embedding size + n_layers=2, # number of hidden layers + + + # lambda_s=0.0001, # regularization of sparsity of the final matrix + # lambda_2=0.0001, # regularization of number of parameters + + lambda_s=0.006, # regularization of sparsity of the final matrix + lambda_2=0.001, # regularization of number of parameters + + gk_size=3, # width=height of kernel for convolution + dot_scale=1, # dot product weight for global kernel + max_epoch_p=10, # max number of epochs for pretraining + max_epoch_f=10, # max number of epochs for finetuning + tol_p=1e-4, # min threshold for difference between consecutive values of train rmse for pretraining + tol_f=1e-5, # min threshold for difference for finetuning + patience_p=10, # early stopping patience for pretraining + patience_f=10, # early stopping patience for finetuning + lr_p=0.01, + lr_f=0.001, + verbose=False, + name="GlobalLocalKernel", + trainable=True + ): + super().__init__(name=name, trainable=trainable, verbose=verbose) + self.n_hid = n_hid + self.n_dim = n_dim + self.n_layers = n_layers + self.lambda_s = lambda_s + self.lambda_2 = lambda_2 + self.gk_size = gk_size + self.dot_scale = dot_scale + self.max_epoch_p = max_epoch_p + self.max_epoch_f = max_epoch_f + self.tol_p = tol_p + self.tol_f = tol_f + self.patience_p = patience_p + self.patience_f = patience_f + self.lr_p = lr_p + self.lr_f = lr_f + self.verbose = verbose + + # Device + if torch.cuda.is_available(): + self.device = torch.device("cuda") + else: + self.device = torch.device("cpu") + + self.verbose = verbose + self.model = None + self.train_r_local = None + + def fit(self, train_set, val_set=None): + # Prepare training data + # train_r = train_set.csr_matrix.toarray().astype(np.float32) + # print('train : ', train_r.shape) # (943, 1656) + + # n_u, n_m = train_r.shape + # train_mask = (train_r > 0).astype(np.float32) + # # Initialize models + # kernel_net = KernelNet(n_u, self.n_hid, self.n_dim, self.n_layers, self.lambda_s, self.lambda_2).double().to(self.device) + # complete_model = CompleteNet(kernel_net, n_u, n_m, self.n_hid, self.n_dim, self.n_layers, self.lambda_s, self.lambda_2, self.gk_size, self.dot_scale).double().to(self.device) + + self.min_rating = 1.0 + self.max_rating = 4.0 + + # Extract user-item-rating tuples + train_users, train_items, train_ratings = train_set.uir_tuple + test_users, test_items, test_ratings = ([], [], []) # For now, if val_set is None + + # Get total numbers of users and items + n_u = train_set.num_users + n_m = train_set.num_items + + # Construct train_r as (n_m, n_u), same as in your notebook + train_r = np.zeros((n_m, n_u), dtype='float32') + train_r[train_items, train_users] = train_ratings + + # Now train_r is shaped (n_m, n_u) and aligned with your model expectations. + train_mask = (train_r > 0).astype(np.float32) + + + self._train_r = train_r + self._train_mask = train_mask + + + + # Update variables accordingly + # After this, n_m = train_r.shape[0], n_u = train_r.shape[1] + n_m, n_u = train_r.shape + + # Now initialize models with n_u and n_m that match the shape of train_r + kernel_net = KernelNet(n_u, self.n_hid, self.n_dim, self.n_layers, self.lambda_s, self.lambda_2).double().to(self.device) + complete_model = CompleteNet(kernel_net, n_u, n_m, self.n_hid, self.n_dim, self.n_layers, self.lambda_s, self.lambda_2, self.gk_size, self.dot_scale).double().to(self.device) + + + # Pre-Training (KernelNet only) + optimizer = torch.optim.AdamW(complete_model.local_kernel_net.parameters(), lr=self.lr_p) + best_rmse = np.inf + last_rmse = np.inf + counter = 0 + + tic = time.time() + + #Pre-training process with tqdm for every group of 10 epochs + for group in range(0, self.max_epoch_p, 10): # Split epochs into groups of 10 + start_epoch = group + end_epoch = min(group + 10, self.max_epoch_p) # Handle the last group + + # Initialize the progress bar for the group + with tqdm(total=end_epoch - start_epoch, desc=f"Epochs {start_epoch + 1}-{end_epoch} (Pre-Training)", leave=True) as pbar: + for epoch in range(start_epoch, end_epoch): + + # Define the closure function + def closure(): + optimizer.zero_grad() + x = torch.tensor(train_r, dtype=torch.double, device=self.device) + m = torch.tensor(train_mask, dtype=torch.double, device=self.device) + complete_model.local_kernel_net.train() + pred, reg = complete_model.local_kernel_net(x) + loss = Loss().to(self.device)(pred, reg, m, x) + loss.backward() + return loss + + optimizer.step(closure) + + complete_model.local_kernel_net.eval() + with torch.no_grad(): + x = torch.tensor(train_r, dtype=torch.double, device=self.device) + pred, _ = kernel_net(x) + + pre = pred.float().cpu().numpy() + + # Compute training RMSE + train_rmse = np.sqrt(((train_mask * (np.clip(pre, 1., 5.) - train_r))**2).sum() / train_mask.sum()) + + # Update the current progress bar + pbar.set_postfix({"Train RMSE": f"{train_rmse:.4f}"}) + pbar.update(1) + + # Check for early stopping + if last_rmse - train_rmse < self.tol_p: + counter += 1 + else: + counter = 0 + last_rmse = train_rmse + + if counter >= self.patience_p: + tqdm.write(f"Early stopping pre-training at epoch: {epoch + 1}") + break + + # Log at the current epoch + if self.verbose: + logging.info(f"Pre-Training Epoch {epoch + 1}/{self.max_epoch_p}, Train RMSE: {train_rmse:.4f}") + + # After pre-training + self.train_r_local = np.clip(pre, 1., 5.) + + # Fine-Tuning + optimizer = torch.optim.AdamW(complete_model.parameters(), lr=self.lr_f) + last_rmse = np.inf + counter = 0 + + for group in range(0, self.max_epoch_f, 10): # Split epochs into groups of 10 + start_epoch = group + end_epoch = min(group + 10, self.max_epoch_f) # Handle the last group + + + # Initialize the progress bar for the group + with tqdm(total=end_epoch - start_epoch, desc=f"Epochs {start_epoch + 1}-{end_epoch} (Fine-Tuning)", leave=True) as pbar: + for epoch in range(start_epoch, end_epoch): + # Define the closure function + def closure(): + optimizer.zero_grad() + x = torch.tensor(train_r, dtype=torch.double, device=self.device) + x_local = torch.tensor(self.train_r_local, dtype=torch.double, device=self.device) + m = torch.tensor(train_mask, dtype=torch.double, device=self.device) + complete_model.train() + pred, reg = complete_model(x, x_local) + loss = Loss().to(self.device)(pred, reg, m, x) + loss.backward() + return loss + + optimizer.step(closure) + + complete_model.eval() + with torch.no_grad(): + x = torch.tensor(train_r, dtype=torch.double, device=self.device) + x_local = torch.tensor(self.train_r_local, dtype=torch.double, device=self.device) + pred, _ = complete_model(x, x_local) + + pre = pred.float().cpu().numpy() + + # Compute training RMSE + train_rmse = np.sqrt(((train_mask * (np.clip(pre, 1., 5.) - train_r))**2).sum() / train_mask.sum()) + + # Update the current progress bar + pbar.set_postfix({"Train RMSE": f"{train_rmse:.4f}"}) + pbar.update(1) + + # Check for early stopping + if last_rmse - train_rmse < self.tol_f: + counter += 1 + else: + counter = 0 + last_rmse = train_rmse + + if counter >= self.patience_f: + tqdm.write(f"Early stopping fine-tuning at epoch: {epoch + 1}") + break + + # Log at the current epoch + if self.verbose: + logging.info(f"Fine-Tuning Epoch {epoch + 1}/{self.max_epoch_f}, Train RMSE: {train_rmse:.4f}") + + # Store the trained model + self.model = complete_model + return self + + + def score(self, user_idx, item_idx=None): + """Predict the scores/ratings of a user for an item or batch of items. + + Parameters + ---------- + user_idx: int, required + The index of the user for whom to perform score prediction. + item_idx: int, optional + The index (or indices) of the item(s) for which to perform score prediction. + If None, scores for all items will be returned. + + Returns + ------- + res: A scalar or Numpy array + A scalar for a specific item, or a Numpy array of scores for all items. + """ + if self.model is None: + raise RuntimeError("You must train the model before calling score()!") + + with torch.no_grad(): + # Perform model predictions (full prediction matrix) + input_mat = torch.tensor(self.train_r_local, dtype=torch.double, device=self.device) + x_global = torch.tensor(self._train_r, dtype=torch.double, device=self.device) + pred, _ = self.model(x_global, input_mat) + pred = pred.cpu().numpy() # Convert to NumPy array + + if item_idx is None: + # Return scores for all items for the specified user + return pred[:, user_idx] # NumPy array of scores + + elif isinstance(item_idx, list): + # Return scores for a list of items + return np.array([pred[i, user_idx] for i in item_idx]) # NumPy array + + else: + # Return score for a single item (scalar) + return pred[item_idx, user_idx] + + + + + + # def get_vector_measure(self): + # from cornac.utils import MEASURE_DOT + # return MEASURE_DOT + + + # def get_user_vectors(self): + # # Assuming self.U stores the user embeddings + # return self.U.cpu().detach().numpy() + + + # def get_item_vectors(self): + # # Assuming self.V stores the item embeddings + # return self.V.cpu().detach().numpy() + + + # def rank(self, user_idx, item_indices=None, k=None): + # """ + # Rank items for a given user based on predicted scores. + + # Parameters + # ---------- + # user_idx : int + # The index of the user for whom to rank items. + + # item_indices : array-like, optional, default: None + # Indices of items to be ranked. If None, rank all items. + + # k : int, optional, default: None + # Number of top items to return. If None, return all ranked items. + + # Returns + # ------- + # item_rank : np.ndarray + # Indices of items ranked in descending order of predicted scores. + + # item_scores : np.ndarray + # Predicted scores for the ranked items. + # """ + # with torch.no_grad(): + # # Get user embeddings (row from self.U) + # user_embedding = self.U[user_idx].cpu().numpy() + + # # Compute scores for all items or a subset + # if item_indices is None: + # item_embeddings = self.V.cpu().numpy() # All item embeddings + # else: + # item_embeddings = self.V[item_indices].cpu().numpy() # Subset of items + + # # Compute scores (dot product or similarity) + # scores = np.dot(item_embeddings, user_embedding) + + # # Get the ranked indices + # ranked_indices = np.argsort(-scores) # Descending order + # if k is not None: + # ranked_indices = ranked_indices[:k] + + # # Get the corresponding scores for ranked items + # ranked_scores = scores[ranked_indices] + + # # Map back to original item indices if item_indices is provided + # if item_indices is not None: + # ranked_indices = np.array(item_indices)[ranked_indices] + + # return ranked_indices, ranked_scores diff --git a/cornac/models/globallocalkernel/requirements.txt b/cornac/models/globallocalkernel/requirements.txt new file mode 100644 index 000000000..13be8e9f2 --- /dev/null +++ b/cornac/models/globallocalkernel/requirements.txt @@ -0,0 +1,2 @@ +torch>=1.10.0 +torchvision>=0.11.0 diff --git a/cornac/models/recommender.py b/cornac/models/recommender.py index c7080a4b2..f9b2ab688 100644 --- a/cornac/models/recommender.py +++ b/cornac/models/recommender.py @@ -21,7 +21,7 @@ from datetime import datetime from glob import glob import json - +import logging import numpy as np from ..exception import ScoreException @@ -328,6 +328,7 @@ def fit(self, train_set, val_set=None): if val_set is not None: val_set.reset() + # get some useful information for prediction self.num_users = train_set.num_users self.num_items = train_set.num_items @@ -496,20 +497,33 @@ def rank(self, user_idx, item_indices=None, k=-1, **kwargs): `item_scores` contains scores of items corresponding to index in `item_indices` input. """ - # obtain item scores from the model + + # logging.info(f"Calling `score` for user {user_idx} with `item_indices={item_indices}`") try: known_item_scores = self.score(user_idx, **kwargs) + except ScoreException: known_item_scores = np.ones(self.total_items) * self.default_score() - # check if the returned scores also cover unknown items - # if not, all unknown items will be given the MIN score + # logging.info(f"`known_item_scores` returned by `score`: {type(known_item_scores)}") + + if self.num_items is None: + self.num_items = len(known_item_scores) # Fallback if undefined + # logging.warning("`self.num_items` was None. Setting it to the length of `known_item_scores`.") + + + # Check if the returned scores cover unknown items if len(known_item_scores) == self.total_items: + # logging.info("num_items : " + str(self.num_items)) + all_item_scores = known_item_scores else: all_item_scores = np.ones(self.total_items) * np.min(known_item_scores) + # logging.info("num_items : " + str(self.num_items)) all_item_scores[: self.num_items] = known_item_scores + # logging.info(f"`all_item_scores`: {all_item_scores}") + # rank items based on their scores item_indices = ( np.arange(self.num_items) diff --git a/cornac/utils/external/eigen/Eigen/src/QR/CompleteOrthogonalDecomposition.h b/cornac/utils/external/eigen/Eigen/src/QR/CompleteOrthogonalDecomposition.h index 34c637b70..09ea650b9 100644 --- a/cornac/utils/external/eigen/Eigen/src/QR/CompleteOrthogonalDecomposition.h +++ b/cornac/utils/external/eigen/Eigen/src/QR/CompleteOrthogonalDecomposition.h @@ -161,7 +161,6 @@ class CompleteOrthogonalDecomposition { applyZAdjointOnTheLeftInPlace(Z); return Z.adjoint(); } - /** \returns a reference to the matrix where the complete orthogonal * decomposition is stored */ diff --git a/experiment_log_1.txt b/experiment_log_1.txt new file mode 100644 index 000000000..2528dfdb2 --- /dev/null +++ b/experiment_log_1.txt @@ -0,0 +1,86 @@ + +======================================== +Experiment conducted on: 2024-12-14 22:16:07.572951 + +Hyperparameters: +name: GlobalLocalKernel +trainable: True +verbose: False +is_fitted: False +ignored_attrs: ['train_set', 'val_set', 'test_set'] +num_users: None +num_items: 1656 +uid_map: None +iid_map: None +max_rating: 4.0 +min_rating: 1.0 +global_mean: None +_Recommender__user_ids: None +_Recommender__item_ids: None +n_hid: 10 +n_dim: 2 +n_layers: 2 +lambda_s: 0.006 +lambda_2: 0.001 +gk_size: 3 +dot_scale: 1 +max_epoch_p: 500 +max_epoch_f: 500 +tol_p: 0.0001 +tol_f: 1e-05 +patience_p: 10 +patience_f: 10 +lr_p: 0.1 +lr_f: 0.01 +device: cuda +model: CompleteNet( + (local_kernel_net): KernelNet( + (layers): ModuleList( + (0-1): 2 x KernelLayer( + (activation): Sigmoid() + ) + (2): KernelLayer( + (activation): Identity() + ) + ) + (dropout): Dropout(p=0.33, inplace=False) + ) +) +train_r_local: [[4.18834 3.834389 4.190889 ... 3.719673 4.9786563 4.025386 ] + [4.1866603 3.833935 4.1901083 ... 3.7193959 4.9786563 4.0248137] + [3.643859 3.643947 3.9684842 ... 3.4783204 4.9786563 3.7472897] + ... + [3.0270095 3.4925659 3.7013142 ... 3.32962 4.9786563 3.496717 ] + [3.030718 3.493905 3.7029653 ... 3.3311675 4.9786563 3.4985132] + [3.030718 3.493905 3.7029653 ... 3.3311675 4.9786563 3.4985132]] +_train_r: [[4. 0. 0. ... 0. 0. 0.] + [0. 5. 0. ... 0. 0. 0.] + [0. 0. 5. ... 0. 0. 0.] + ... + [0. 0. 0. ... 0. 0. 0.] + [0. 0. 0. ... 0. 0. 0.] + [0. 0. 0. ... 0. 0. 0.]] +_train_mask: [[1. 0. 0. ... 0. 0. 0.] + [0. 1. 0. ... 0. 0. 0.] + [0. 0. 1. ... 0. 0. 0.] + ... + [0. 0. 0. ... 0. 0. 0.] + [0. 0. 0. ... 0. 0. 0.] + [0. 0. 0. ... 0. 0. 0.]] + +Test Results: +Early stopping fine-tuning at epoch: 380 +Early stopping fine-tuning at epoch: 381 +Early stopping fine-tuning at epoch: 391 +Early stopping fine-tuning at epoch: 401 +Early stopping fine-tuning at epoch: 411 +Early stopping fine-tuning at epoch: 432 + +TEST: +... + | MAE | RMSE | AUC | MAP | NDCG@10 | Precision@10 | Recall@10 | Train (s) | Test (s) +----------------- + ------ + ------ + ------ + ------ + ------- + ------------ + --------- + --------- + -------- +GlobalLocalKernel | 0.8034 | 0.9727 | 0.5107 | 0.0189 | 0.0217 | 0.0192 | 0.0133 | 28.9218 | 190.2286 + + +======================================== diff --git a/experiment_log_2.txt b/experiment_log_2.txt new file mode 100644 index 000000000..58d6e2824 --- /dev/null +++ b/experiment_log_2.txt @@ -0,0 +1,83 @@ + +======================================== +Experiment conducted on: 2024-12-14 22:22:56.926776 + +Hyperparameters: +name: GlobalLocalKernel +trainable: True +verbose: False +is_fitted: False +ignored_attrs: ['train_set', 'val_set', 'test_set'] +num_users: None +num_items: 1656 +uid_map: None +iid_map: None +max_rating: 4.0 +min_rating: 1.0 +global_mean: None +_Recommender__user_ids: None +_Recommender__item_ids: None +n_hid: 10 +n_dim: 2 +n_layers: 2 +lambda_s: 0.006 +lambda_2: 0.001 +gk_size: 3 +dot_scale: 1 +max_epoch_p: 500 +max_epoch_f: 500 +tol_p: 0.0001 +tol_f: 1e-05 +patience_p: 10 +patience_f: 10 +lr_p: 0.1 +lr_f: 0.01 +device: cuda +model: CompleteNet( + (local_kernel_net): KernelNet( + (layers): ModuleList( + (0-1): 2 x KernelLayer( + (activation): Sigmoid() + ) + (2): KernelLayer( + (activation): Identity() + ) + ) + (dropout): Dropout(p=0.33, inplace=False) + ) +) +train_r_local: [[4.074561 3.7919703 4.1602736 ... 3.5502026 4.9746857 3.943836 ] + [4.0745606 3.79197 4.1602736 ... 3.5502021 4.9746857 3.9438357] + [3.7859447 3.7108083 4.024741 ... 3.356383 4.9746857 3.8038127] + ... + [3.1270785 3.4874184 3.683483 ... 3.1386068 4.9746857 3.4953837] + [3.1272159 3.4874575 3.6835485 ... 3.1387024 4.9746857 3.4954498] + [3.1272159 3.4874575 3.6835485 ... 3.1387024 4.9746857 3.4954498]] +_train_r: [[4. 0. 0. ... 0. 0. 0.] + [0. 5. 0. ... 0. 0. 0.] + [0. 0. 5. ... 0. 0. 0.] + ... + [0. 0. 0. ... 0. 0. 0.] + [0. 0. 0. ... 0. 0. 0.] + [0. 0. 0. ... 0. 0. 0.]] +_train_mask: [[1. 0. 0. ... 0. 0. 0.] + [0. 1. 0. ... 0. 0. 0.] + [0. 0. 1. ... 0. 0. 0.] + ... + [0. 0. 0. ... 0. 0. 0.] + [0. 0. 0. ... 0. 0. 0.] + [0. 0. 0. ... 0. 0. 0.]] + +Test Results: + +TEST: +... + | MAE | RMSE | AUC | MAP | NDCG@10 | Precision@10 | Recall@10 | Train (s) | Test (s) +----------------- + ------ + ------ + ------ + ------ + ------- + ------------ + --------- + --------- + -------- +GlobalLocalKernel | 0.8051 | 0.9771 | 0.5428 | 0.0232 | 0.0261 | 0.0254 | 0.0169 | 30.8466 | 191.1777 +MF | 0.7430 | 0.8998 | 0.7445 | 0.0548 | 0.0761 | 0.0675 | 0.0463 | 0.0290 | 0.7707 +PMF | 0.7534 | 0.9138 | 0.7744 | 0.0671 | 0.0969 | 0.0813 | 0.0639 | 1.4337 | 1.1157 +BPR | 2.0143 | 2.2267 | 0.8695 | 0.1042 | 0.1500 | 0.1110 | 0.1195 | 1.2896 | 0.7294 + + +======================================== diff --git a/experiment_log_3.txt b/experiment_log_3.txt new file mode 100644 index 000000000..0eebadbe5 --- /dev/null +++ b/experiment_log_3.txt @@ -0,0 +1,92 @@ + +======================================== +Experiment conducted on: 2024-12-15 14:35:04.892244 + +Hyperparameters: +name: GlobalLocalKernel +trainable: True +verbose: False +is_fitted: False +ignored_attrs: ['train_set', 'val_set', 'test_set'] +num_users: None +num_items: 1656 +uid_map: None +iid_map: None +max_rating: 4.0 +min_rating: 1.0 +global_mean: None +_Recommender__user_ids: None +_Recommender__item_ids: None +n_hid: 10 +n_dim: 2 +n_layers: 2 +lambda_s: 0.006 +lambda_2: 0.001 +gk_size: 3 +dot_scale: 1 +max_epoch_p: 500 +max_epoch_f: 1000 +tol_p: 0.0001 +tol_f: 1e-05 +patience_p: 10 +patience_f: 10 +lr_p: 0.1 +lr_f: 0.01 +device: cuda +model: CompleteNet( + (local_kernel_net): KernelNet( + (layers): ModuleList( + (0-1): 2 x KernelLayer( + (activation): Sigmoid() + ) + (2): KernelLayer( + (activation): Identity() + ) + ) + (dropout): Dropout(p=0.33, inplace=False) + ) +) +train_r_local: [[4.13143 3.8251498 4.2022805 ... 3.46096 4.9777036 3.8819532] + [4.1326137 3.825405 4.202767 ... 3.4611 4.9777055 3.8823788] + [3.8330228 3.6725302 4.1033254 ... 3.3236573 4.9774 3.8047073] + ... + [3.1168575 3.5055826 3.8102179 ... 3.2242472 4.9762387 3.5493011] + [3.1168575 3.5055826 3.8102179 ... 3.2242472 4.9762387 3.5493011] + [3.1168575 3.5055826 3.8102179 ... 3.2242472 4.9762387 3.5493011]] +_train_r: [[4. 0. 0. ... 0. 0. 0.] + [0. 5. 0. ... 0. 0. 0.] + [0. 0. 5. ... 0. 0. 0.] + ... + [0. 0. 0. ... 0. 0. 0.] + [0. 0. 0. ... 0. 0. 0.] + [0. 0. 0. ... 0. 0. 0.]] +_train_mask: [[1. 0. 0. ... 0. 0. 0.] + [0. 1. 0. ... 0. 0. 0.] + [0. 0. 1. ... 0. 0. 0.] + ... + [0. 0. 0. ... 0. 0. 0.] + [0. 0. 0. ... 0. 0. 0.] + [0. 0. 0. ... 0. 0. 0.]] + +Test Results: +Early stopping fine-tuning at epoch: 237 +Early stopping fine-tuning at epoch: 241 +Early stopping fine-tuning at epoch: 251 +Early stopping fine-tuning at epoch: 261 +Early stopping fine-tuning at epoch: 271 +Early stopping fine-tuning at epoch: 281 +Early stopping fine-tuning at epoch: 420 +Early stopping fine-tuning at epoch: 421 +Early stopping fine-tuning at epoch: 900 + +TEST: +... + | MAE | RMSE | AUC | MAP | NDCG@10 | Precision@10 | Recall@10 | Train (s) | Test (s) +----------------- + ------ + ------ + ------ + ------ + ------- + ------------ + --------- + --------- + -------- +GlobalLocalKernel | 0.8029 | 0.9731 | 0.4371 | 0.0158 | 0.0168 | 0.0137 | 0.0110 | 55.5029 | 232.3715 +MF | 0.7430 | 0.8998 | 0.7445 | 0.0548 | 0.0761 | 0.0675 | 0.0463 | 0.0302 | 0.8123 +PMF | 0.7534 | 0.9138 | 0.7744 | 0.0671 | 0.0969 | 0.0813 | 0.0639 | 1.4669 | 1.2117 +BPR | 2.0143 | 2.2267 | 0.8695 | 0.1042 | 0.1500 | 0.1110 | 0.1195 | 1.3102 | 0.7666 + + +======================================== diff --git a/experiment_log_4.txt b/experiment_log_4.txt new file mode 100644 index 000000000..116758bdf --- /dev/null +++ b/experiment_log_4.txt @@ -0,0 +1,81 @@ + +======================================== +Experiment conducted on: 2024-12-14 21:04:28.481690 + +Hyperparameters: +name: GlobalLocalKernel +trainable: True +verbose: False +is_fitted: False +ignored_attrs: ['train_set', 'val_set', 'test_set'] +num_users: None +num_items: 1656 +uid_map: None +iid_map: None +max_rating: 4.0 +min_rating: 1.0 +global_mean: None +_Recommender__user_ids: None +_Recommender__item_ids: None +n_hid: 10 +n_dim: 2 +n_layers: 2 +lambda_s: 0.006 +lambda_2: 0.001 +gk_size: 3 +dot_scale: 1 +max_epoch_p: 30 +max_epoch_f: 100 +tol_p: 0.0001 +tol_f: 1e-05 +patience_p: 10 +patience_f: 10 +lr_p: 0.1 +lr_f: 0.01 +device: cpu +model: CompleteNet( + (local_kernel_net): KernelNet( + (layers): ModuleList( + (0-1): 2 x KernelLayer( + (activation): Sigmoid() + ) + (2): KernelLayer( + (activation): Identity() + ) + ) + (dropout): Dropout(p=0.33, inplace=False) + ) +) +train_r_local: [[3.4896953 3.300755 4.0347724 ... 3.2298357 4.5382867 3.3844583] + [3.683822 3.484483 4.2810183 ... 3.384981 4.898886 3.6059442] + [3.5933316 3.3985095 4.1668615 ... 3.3124022 4.7337956 3.5070415] + ... + [3.546636 3.3489509 4.1108484 ... 3.2751315 4.650442 3.4729054] + [3.5501003 3.3515632 4.1113515 ... 3.276139 4.656549 3.4755447] + [3.5533752 3.3547118 4.115988 ... 3.2788885 4.6626425 3.479602 ]] +_train_r: [[4. 0. 0. ... 0. 0. 0.] + [0. 5. 0. ... 0. 0. 0.] + [0. 0. 5. ... 0. 0. 0.] + ... + [0. 0. 0. ... 0. 0. 0.] + [0. 0. 0. ... 0. 0. 0.] + [0. 0. 0. ... 0. 0. 0.]] +_train_mask: [[1. 0. 0. ... 0. 0. 0.] + [0. 1. 0. ... 0. 0. 0.] + [0. 0. 1. ... 0. 0. 0.] + ... + [0. 0. 0. ... 0. 0. 0.] + [0. 0. 0. ... 0. 0. 0.] + [0. 0. 0. ... 0. 0. 0.]] + +Test Results: +helllooooooooooooow + +TEST: +... + | MAE | RMSE | AUC | MAP | NDCG@10 | Precision@10 | Recall@10 | Train (s) | Test (s) +----------------- + ------ + ------ + ------ + ------ + ------- + ------------ + --------- + --------- + -------- +GlobalLocalKernel | 0.8233 | 0.9861 | 0.6304 | 0.0210 | 0.0238 | 0.0214 | 0.0144 | 20.5121 | 841.4456 + + +======================================== diff --git a/others.py b/others.py new file mode 100644 index 000000000..d10210436 --- /dev/null +++ b/others.py @@ -0,0 +1,27 @@ + +import os +from datetime import datetime +import numpy as np +import cornac +from cornac.models import MF, PMF, BPR,GlobalLocalKernel + + +from cornac.eval_methods import RatioSplit +from cornac.metrics import MAE, RMSE, Precision, Recall, NDCG, AUC, MAP + +ml_100k = cornac.datasets.movielens.load_feedback() +ml_100k = ml_100k[:500] + +rs = RatioSplit(data=ml_100k, test_size=0.2, rating_threshold=4.0, seed=123) + +# initialize models, here we are comparing: Biased MF, PMF, and BPR +mf = MF(k=10, max_iter=25, learning_rate=0.01, lambda_reg=0.02, use_bias=True, seed=123) +pmf = PMF(k=10, max_iter=100, learning_rate=0.001, lambda_reg=0.001, seed=123) +bpr = BPR(k=10, max_iter=200, learning_rate=0.001, lambda_reg=0.01, seed=123) +models = [bpr] + +# define metrics to evaluate the models +metrics = [MAE(), RMSE(), Precision(k=10), Recall(k=10), NDCG(k=10), AUC(), MAP()] + +# put it together in an experiment, voilà! +cornac.Experiment(eval_method=rs, models=models, metrics=metrics, user_based=True).run() \ No newline at end of file diff --git a/project_notebook.ipynb b/project_notebook.ipynb new file mode 100644 index 000000000..d8ec55550 --- /dev/null +++ b/project_notebook.ipynb @@ -0,0 +1,421 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Recommendation Systems Project\n", + "\n", + "By : ADJAL Mehdi Zakaria - BENBETKA Rachid - YAMANI Mohammed Kamel - Rami Boukaroura\n", + "\n", + "You will find the report in the following link : " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## How to run this notebook :" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/mehdiz/miniconda3/lib/python3.12/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n", + " from .autonotebook import tqdm as notebook_tqdm\n" + ] + } + ], + "source": [ + "import os\n", + "from datetime import datetime\n", + "import numpy as np\n", + "import cornac\n", + "from cornac.models import GlobalLocalKernel\n", + "from cornac.eval_methods import RatioSplit\n", + "from cornac.metrics import MAE, RMSE, Precision, Recall, NDCG, AUC, MAP\n", + "from cornac.models import MF, PMF, BPR" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "# Function to find the next available log file name\n", + "def get_next_log_file(base_name=\"experiment_log\", ext=\".txt\"):\n", + " counter = 1\n", + " while os.path.exists(f\"{base_name}_{counter}{ext}\"):\n", + " counter += 1\n", + " return f\"{base_name}_{counter}{ext}\"" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "# Function to log results\n", + "def log_results(log_file, test_results, model_instance):\n", + " with open(log_file, \"a\") as f:\n", + " f.write(\"\\n\" + \"=\" * 40 + \"\\n\")\n", + " f.write(f\"Experiment conducted on: {datetime.now()}\\n\")\n", + " f.write(\"\\nHyperparameters:\\n\")\n", + " for attr, value in vars(model_instance).items():\n", + " f.write(f\"{attr}: {value}\\n\")\n", + " f.write(\"\\nTest Results:\\n\")\n", + " f.write(test_results)\n", + " f.write(\"\\n\" + \"=\" * 40 + \"\\n\")" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "# Load the MovieLens 100K dataset\n", + "ml_100k = cornac.datasets.movielens.load_feedback()" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "# Split the data\n", + "rs = RatioSplit(data=ml_100k, test_size=0.2, rating_threshold=4.0, seed=123)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Data matrix loaded\n", + "Number of users: 943\n", + "Number of movies: 1656\n", + "Number of training ratings: 80000\n", + "Number of test ratings: 19971\n" + ] + } + ], + "source": [ + "# Get the total number of users and items in the subset\n", + "n_u = rs.total_users\n", + "n_m = rs.total_items\n", + "\n", + "print('Data matrix loaded')\n", + "print('Number of users: {}'.format(n_u))\n", + "print('Number of movies: {}'.format(n_m))\n", + "print('Number of training ratings: {}'.format(len(rs.train_set.uir_tuple[2])))\n", + "print('Number of test ratings: {}'.format(len(rs.test_set.uir_tuple[2])))" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "# Initialize your model\n", + "my_model = GlobalLocalKernel(\n", + " # Example hyperparameters\n", + " n_hid=10, \n", + " n_dim=2, \n", + " max_epoch_p=500, \n", + " max_epoch_f=1000,\n", + " lr_p=0.1,\n", + " lr_f=0.01, \n", + " verbose=False\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [], + "source": [ + "# Models to compare with\n", + "mf = MF(k=10, max_iter=25, learning_rate=0.01, lambda_reg=0.02, use_bias=True, seed=123)\n", + "pmf = PMF(k=10, max_iter=100, learning_rate=0.001, lambda_reg=0.001, seed=123)\n", + "bpr = BPR(k=10, max_iter=200, learning_rate=0.001, lambda_reg=0.01, seed=123)" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "# Define some basic metrics\n", + "metrics = [MAE(), RMSE(), Precision(k=10), Recall(k=10), NDCG(k=10), AUC(), MAP()]" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Early stopping fine-tuning at epoch: 237\n", + "Early stopping fine-tuning at epoch: 241\n", + "Early stopping fine-tuning at epoch: 251\n", + "Early stopping fine-tuning at epoch: 261\n", + "Early stopping fine-tuning at epoch: 271\n", + "Early stopping fine-tuning at epoch: 281\n", + "Early stopping fine-tuning at epoch: 420\n", + "Early stopping fine-tuning at epoch: 421\n", + "Early stopping fine-tuning at epoch: 900\n", + "\n", + "TEST:\n", + "...\n", + " | MAE | RMSE | AUC | MAP | NDCG@10 | Precision@10 | Recall@10 | Train (s) | Test (s)\n", + "----------------- + ------ + ------ + ------ + ------ + ------- + ------------ + --------- + --------- + --------\n", + "GlobalLocalKernel | 0.8029 | 0.9731 | 0.4371 | 0.0158 | 0.0168 | 0.0137 | 0.0110 | 55.5029 | 232.3715\n", + "MF | 0.7430 | 0.8998 | 0.7445 | 0.0548 | 0.0761 | 0.0675 | 0.0463 | 0.0302 | 0.8123\n", + "PMF | 0.7534 | 0.9138 | 0.7744 | 0.0671 | 0.0969 | 0.0813 | 0.0639 | 1.4669 | 1.2117\n", + "BPR | 2.0143 | 2.2267 | 0.8695 | 0.1042 | 0.1500 | 0.1110 | 0.1195 | 1.3102 | 0.7666\n", + "\n", + "\n", + "Experiment results and hyperparameters saved to experiment_log_3.txt\n" + ] + } + ], + "source": [ + "# Redirect Cornac output to capture experiment results\n", + "from io import StringIO\n", + "import sys\n", + "\n", + "# Get the next available log file name\n", + "log_file = get_next_log_file()\n", + "temp = sys.stdout # Store original stdout object for later\n", + "sys.stdout = StringIO() # Redirect stdout to capture results" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epochs 1-10 (Pre-Training): 100%|██████████| 10/10 [00:01<00:00, 7.78it/s, Train RMSE=1.0664]\n", + "Epochs 11-20 (Pre-Training): 100%|██████████| 10/10 [00:00<00:00, 32.31it/s, Train RMSE=1.1418]\n", + "Epochs 21-30 (Pre-Training): 100%|██████████| 10/10 [00:00<00:00, 31.31it/s, Train RMSE=1.0929]\n", + "Epochs 31-40 (Pre-Training): 100%|██████████| 10/10 [00:00<00:00, 34.31it/s, Train RMSE=1.0234]\n", + "Epochs 41-50 (Pre-Training): 100%|██████████| 10/10 [00:00<00:00, 33.04it/s, Train RMSE=1.0055]\n", + "Epochs 51-60 (Pre-Training): 100%|██████████| 10/10 [00:00<00:00, 33.87it/s, Train RMSE=0.9866]\n", + "Epochs 61-70 (Pre-Training): 100%|██████████| 10/10 [00:00<00:00, 30.84it/s, Train RMSE=0.9950]\n", + "Epochs 71-80 (Pre-Training): 100%|██████████| 10/10 [00:00<00:00, 27.34it/s, Train RMSE=0.9840]\n", + "Epochs 81-90 (Pre-Training): 100%|██████████| 10/10 [00:00<00:00, 30.68it/s, Train RMSE=0.9846]\n", + "Epochs 91-100 (Pre-Training): 100%|██████████| 10/10 [00:00<00:00, 32.19it/s, Train RMSE=0.9751]\n", + "Epochs 101-110 (Pre-Training): 100%|██████████| 10/10 [00:00<00:00, 34.04it/s, Train RMSE=0.9751]\n", + "Epochs 111-120 (Pre-Training): 100%|██████████| 10/10 [00:00<00:00, 30.87it/s, Train RMSE=0.9689]\n", + "Epochs 121-130 (Pre-Training): 100%|██████████| 10/10 [00:00<00:00, 34.90it/s, Train RMSE=0.9731]\n", + "Epochs 131-140 (Pre-Training): 100%|██████████| 10/10 [00:00<00:00, 35.47it/s, Train RMSE=0.9749]\n", + "Epochs 141-150 (Pre-Training): 100%|██████████| 10/10 [00:00<00:00, 33.76it/s, Train RMSE=0.9686]\n", + "Epochs 151-160 (Pre-Training): 100%|██████████| 10/10 [00:00<00:00, 27.36it/s, Train RMSE=0.9663]\n", + "Epochs 161-170 (Pre-Training): 100%|██████████| 10/10 [00:00<00:00, 27.76it/s, Train RMSE=0.9661]\n", + "Epochs 171-180 (Pre-Training): 100%|██████████| 10/10 [00:00<00:00, 30.29it/s, Train RMSE=0.9676]\n", + "Epochs 181-190 (Pre-Training): 100%|██████████| 10/10 [00:00<00:00, 27.21it/s, Train RMSE=0.9617]\n", + "Epochs 191-200 (Pre-Training): 100%|██████████| 10/10 [00:00<00:00, 27.10it/s, Train RMSE=0.9677]\n", + "Epochs 201-210 (Pre-Training): 100%|██████████| 10/10 [00:00<00:00, 27.40it/s, Train RMSE=0.9643]\n", + "Epochs 211-220 (Pre-Training): 100%|██████████| 10/10 [00:00<00:00, 28.24it/s, Train RMSE=0.9621]\n", + "Epochs 221-230 (Pre-Training): 100%|██████████| 10/10 [00:00<00:00, 30.32it/s, Train RMSE=0.9631]\n", + "Epochs 231-240 (Pre-Training): 100%|██████████| 10/10 [00:00<00:00, 31.95it/s, Train RMSE=0.9622]\n", + "Epochs 241-250 (Pre-Training): 100%|██████████| 10/10 [00:00<00:00, 33.13it/s, Train RMSE=0.9638]\n", + "Epochs 251-260 (Pre-Training): 100%|██████████| 10/10 [00:00<00:00, 30.76it/s, Train RMSE=0.9614]\n", + "Epochs 261-270 (Pre-Training): 100%|██████████| 10/10 [00:00<00:00, 34.07it/s, Train RMSE=0.9603]\n", + "Epochs 271-280 (Pre-Training): 100%|██████████| 10/10 [00:00<00:00, 32.32it/s, Train RMSE=0.9632]\n", + "Epochs 281-290 (Pre-Training): 100%|██████████| 10/10 [00:00<00:00, 32.24it/s, Train RMSE=0.9629]\n", + "Epochs 291-300 (Pre-Training): 100%|██████████| 10/10 [00:00<00:00, 36.41it/s, Train RMSE=0.9623]\n", + "Epochs 301-310 (Pre-Training): 100%|██████████| 10/10 [00:00<00:00, 34.49it/s, Train RMSE=0.9590]\n", + "Epochs 311-320 (Pre-Training): 100%|██████████| 10/10 [00:00<00:00, 35.98it/s, Train RMSE=0.9647]\n", + "Epochs 321-330 (Pre-Training): 100%|██████████| 10/10 [00:00<00:00, 36.35it/s, Train RMSE=0.9590]\n", + "Epochs 331-340 (Pre-Training): 100%|██████████| 10/10 [00:00<00:00, 33.30it/s, Train RMSE=0.9618]\n", + "Epochs 341-350 (Pre-Training): 100%|██████████| 10/10 [00:00<00:00, 33.56it/s, Train RMSE=0.9651]\n", + "Epochs 351-360 (Pre-Training): 100%|██████████| 10/10 [00:00<00:00, 35.91it/s, Train RMSE=0.9625]\n", + "Epochs 361-370 (Pre-Training): 100%|██████████| 10/10 [00:00<00:00, 37.15it/s, Train RMSE=0.9614]\n", + "Epochs 371-380 (Pre-Training): 100%|██████████| 10/10 [00:00<00:00, 34.79it/s, Train RMSE=0.9625]\n", + "Epochs 381-390 (Pre-Training): 100%|██████████| 10/10 [00:00<00:00, 36.42it/s, Train RMSE=0.9602]\n", + "Epochs 391-400 (Pre-Training): 100%|██████████| 10/10 [00:00<00:00, 36.12it/s, Train RMSE=0.9674]\n", + "Epochs 401-410 (Pre-Training): 100%|██████████| 10/10 [00:00<00:00, 33.22it/s, Train RMSE=0.9601]\n", + "Epochs 411-420 (Pre-Training): 100%|██████████| 10/10 [00:00<00:00, 34.75it/s, Train RMSE=0.9617]\n", + "Epochs 421-430 (Pre-Training): 100%|██████████| 10/10 [00:00<00:00, 33.84it/s, Train RMSE=0.9644]\n", + "Epochs 431-440 (Pre-Training): 100%|██████████| 10/10 [00:00<00:00, 34.14it/s, Train RMSE=0.9650]\n", + "Epochs 441-450 (Pre-Training): 100%|██████████| 10/10 [00:00<00:00, 35.13it/s, Train RMSE=0.9620]\n", + "Epochs 451-460 (Pre-Training): 100%|██████████| 10/10 [00:00<00:00, 36.43it/s, Train RMSE=0.9619]\n", + "Epochs 461-470 (Pre-Training): 100%|██████████| 10/10 [00:00<00:00, 36.61it/s, Train RMSE=0.9631]\n", + "Epochs 471-480 (Pre-Training): 100%|██████████| 10/10 [00:00<00:00, 36.99it/s, Train RMSE=0.9624]\n", + "Epochs 481-490 (Pre-Training): 100%|██████████| 10/10 [00:00<00:00, 34.93it/s, Train RMSE=0.9621]\n", + "Epochs 491-500 (Pre-Training): 100%|██████████| 10/10 [00:00<00:00, 36.42it/s, Train RMSE=0.9585]\n", + "Epochs 1-10 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 10.38it/s, Train RMSE=1.0211]\n", + "Epochs 11-20 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 25.77it/s, Train RMSE=1.0215]\n", + "Epochs 21-30 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 25.28it/s, Train RMSE=1.0187]\n", + "Epochs 31-40 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 24.56it/s, Train RMSE=1.0114]\n", + "Epochs 41-50 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 23.77it/s, Train RMSE=1.0070]\n", + "Epochs 51-60 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 24.97it/s, Train RMSE=1.0043]\n", + "Epochs 61-70 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 24.94it/s, Train RMSE=1.0020]\n", + "Epochs 71-80 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 25.79it/s, Train RMSE=0.9985]\n", + "Epochs 81-90 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 26.09it/s, Train RMSE=0.9941]\n", + "Epochs 91-100 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 26.00it/s, Train RMSE=0.9916]\n", + "Epochs 101-110 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 25.97it/s, Train RMSE=0.9878]\n", + "Epochs 111-120 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 26.22it/s, Train RMSE=0.9862]\n", + "Epochs 121-130 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 24.73it/s, Train RMSE=0.9843]\n", + "Epochs 131-140 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 25.51it/s, Train RMSE=0.9835]\n", + "Epochs 141-150 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 26.59it/s, Train RMSE=0.9822]\n", + "Epochs 151-160 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 26.25it/s, Train RMSE=0.9800]\n", + "Epochs 161-170 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 26.35it/s, Train RMSE=0.9798]\n", + "Epochs 171-180 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 24.83it/s, Train RMSE=0.9802]\n", + "Epochs 181-190 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 25.53it/s, Train RMSE=0.9809]\n", + "Epochs 191-200 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 25.09it/s, Train RMSE=0.9786]\n", + "Epochs 201-210 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 26.20it/s, Train RMSE=0.9794]\n", + "Epochs 211-220 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 26.44it/s, Train RMSE=0.9795]\n", + "Epochs 221-230 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 26.84it/s, Train RMSE=0.9793]\n", + "Epochs 231-240 (Fine-Tuning): 70%|███████ | 7/10 [00:00<00:00, 25.55it/s, Train RMSE=0.9889]\n", + "Epochs 241-250 (Fine-Tuning): 10%|█ | 1/10 [00:00<00:00, 24.93it/s, Train RMSE=0.9904]\n", + "Epochs 251-260 (Fine-Tuning): 10%|█ | 1/10 [00:00<00:00, 24.58it/s, Train RMSE=0.9919]\n", + "Epochs 261-270 (Fine-Tuning): 10%|█ | 1/10 [00:00<00:00, 25.29it/s, Train RMSE=0.9932]\n", + "Epochs 271-280 (Fine-Tuning): 10%|█ | 1/10 [00:00<00:00, 25.56it/s, Train RMSE=0.9939]\n", + "Epochs 281-290 (Fine-Tuning): 10%|█ | 1/10 [00:00<00:00, 24.34it/s, Train RMSE=0.9945]\n", + "Epochs 291-300 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 26.30it/s, Train RMSE=0.9963]\n", + "Epochs 301-310 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 24.97it/s, Train RMSE=0.9944]\n", + "Epochs 311-320 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 26.09it/s, Train RMSE=0.9927]\n", + "Epochs 321-330 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 24.74it/s, Train RMSE=0.9924]\n", + "Epochs 331-340 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 25.59it/s, Train RMSE=0.9921]\n", + "Epochs 341-350 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 25.34it/s, Train RMSE=0.9898]\n", + "Epochs 351-360 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 26.34it/s, Train RMSE=0.9880]\n", + "Epochs 361-370 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 25.82it/s, Train RMSE=0.9884]\n", + "Epochs 371-380 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 25.31it/s, Train RMSE=0.9881]\n", + "Epochs 381-390 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 25.86it/s, Train RMSE=0.9871]\n", + "Epochs 391-400 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 25.93it/s, Train RMSE=0.9845]\n", + "Epochs 401-410 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 26.54it/s, Train RMSE=0.9825]\n", + "Epochs 411-420 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 26.00it/s, Train RMSE=0.9951]\n", + "Epochs 421-430 (Fine-Tuning): 10%|█ | 1/10 [00:00<00:00, 26.12it/s, Train RMSE=0.9951]\n", + "Epochs 431-440 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 24.69it/s, Train RMSE=0.9976]\n", + "Epochs 441-450 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 25.06it/s, Train RMSE=0.9955]\n", + "Epochs 451-460 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 26.23it/s, Train RMSE=0.9955]\n", + "Epochs 461-470 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 26.02it/s, Train RMSE=0.9952]\n", + "Epochs 471-480 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 26.03it/s, Train RMSE=0.9927]\n", + "Epochs 481-490 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 26.06it/s, Train RMSE=0.9907]\n", + "Epochs 491-500 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 26.02it/s, Train RMSE=0.9886]\n", + "Epochs 501-510 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 25.28it/s, Train RMSE=0.9852]\n", + "Epochs 511-520 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 25.31it/s, Train RMSE=0.9826]\n", + "Epochs 521-530 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 26.07it/s, Train RMSE=0.9818]\n", + "Epochs 531-540 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 24.90it/s, Train RMSE=0.9806]\n", + "Epochs 541-550 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 26.27it/s, Train RMSE=0.9787]\n", + "Epochs 551-560 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 25.95it/s, Train RMSE=0.9783]\n", + "Epochs 561-570 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 26.24it/s, Train RMSE=0.9788]\n", + "Epochs 571-580 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 25.35it/s, Train RMSE=0.9777]\n", + "Epochs 581-590 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 26.06it/s, Train RMSE=0.9759]\n", + "Epochs 591-600 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 26.36it/s, Train RMSE=0.9762]\n", + "Epochs 601-610 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 25.81it/s, Train RMSE=0.9772]\n", + "Epochs 611-620 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 26.54it/s, Train RMSE=0.9769]\n", + "Epochs 621-630 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 26.09it/s, Train RMSE=0.9775]\n", + "Epochs 631-640 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 24.63it/s, Train RMSE=0.9789]\n", + "Epochs 641-650 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 25.54it/s, Train RMSE=0.9786]\n", + "Epochs 651-660 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 26.02it/s, Train RMSE=0.9775]\n", + "Epochs 661-670 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 26.20it/s, Train RMSE=0.9778]\n", + "Epochs 671-680 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 26.15it/s, Train RMSE=0.9762]\n", + "Epochs 681-690 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 26.07it/s, Train RMSE=0.9761]\n", + "Epochs 691-700 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 26.43it/s, Train RMSE=0.9769]\n", + "Epochs 701-710 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 25.48it/s, Train RMSE=0.9753]\n", + "Epochs 711-720 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 25.60it/s, Train RMSE=0.9756]\n", + "Epochs 721-730 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 23.90it/s, Train RMSE=0.9748]\n", + "Epochs 731-740 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 25.89it/s, Train RMSE=0.9752]\n", + "Epochs 741-750 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 26.83it/s, Train RMSE=0.9761]\n", + "Epochs 751-760 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 26.44it/s, Train RMSE=0.9745]\n", + "Epochs 761-770 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 25.75it/s, Train RMSE=0.9765]\n", + "Epochs 771-780 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 25.28it/s, Train RMSE=0.9768]\n", + "Epochs 781-790 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 26.24it/s, Train RMSE=0.9758]\n", + "Epochs 791-800 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 26.44it/s, Train RMSE=0.9742]\n", + "Epochs 801-810 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 24.71it/s, Train RMSE=0.9744]\n", + "Epochs 811-820 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 25.86it/s, Train RMSE=0.9743]\n", + "Epochs 821-830 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 26.00it/s, Train RMSE=0.9756]\n", + "Epochs 831-840 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 24.98it/s, Train RMSE=0.9761]\n", + "Epochs 841-850 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 25.81it/s, Train RMSE=0.9756]\n", + "Epochs 851-860 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 26.08it/s, Train RMSE=0.9757]\n", + "Epochs 861-870 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 25.10it/s, Train RMSE=0.9746]\n", + "Epochs 871-880 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 25.47it/s, Train RMSE=0.9723]\n", + "Epochs 881-890 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 26.26it/s, Train RMSE=0.9714]\n", + "Epochs 891-900 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 24.00it/s, Train RMSE=0.9745]\n", + "Epochs 901-910 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 25.13it/s, Train RMSE=0.9725]\n", + "Epochs 911-920 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 25.75it/s, Train RMSE=0.9722]\n", + "Epochs 921-930 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 25.83it/s, Train RMSE=0.9725]\n", + "Epochs 931-940 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 26.21it/s, Train RMSE=0.9710]\n", + "Epochs 941-950 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 26.10it/s, Train RMSE=0.9738]\n", + "Epochs 951-960 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 26.54it/s, Train RMSE=0.9738]\n", + "Epochs 961-970 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 23.58it/s, Train RMSE=0.9715]\n", + "Epochs 971-980 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 24.55it/s, Train RMSE=0.9731]\n", + "Epochs 981-990 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 25.77it/s, Train RMSE=0.9722]\n", + "Epochs 991-1000 (Fine-Tuning): 100%|██████████| 10/10 [00:00<00:00, 26.22it/s, Train RMSE=0.9720]\n" + ] + } + ], + "source": [ + "# Run the experiment on the smaller subset\n", + "cornac.Experiment(eval_method=rs, models=[my_model, mf, pmf, bpr], metrics=metrics, user_based=True).run()\n", + "\n", + "# Retrieve experiment results\n", + "experiment_results = sys.stdout.getvalue()\n", + "sys.stdout = temp # Restore stdout to original state" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [], + "source": [ + "# Print the results to the console\n", + "print(experiment_results)\n", + "\n", + "# Log results to file\n", + "log_results(log_file, experiment_results, my_model)\n", + "\n", + "print(f\"Experiment results and hyperparameters saved to {log_file}\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "base", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.2" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/try.py b/try.py new file mode 100644 index 000000000..7fa0cbd58 --- /dev/null +++ b/try.py @@ -0,0 +1,88 @@ +import os +from datetime import datetime +import numpy as np +import cornac +from cornac.models import GlobalLocalKernel +from cornac.eval_methods import RatioSplit +from cornac.metrics import MAE, RMSE, Precision, Recall, NDCG, AUC, MAP +from cornac.models import MF, PMF, BPR + +# Function to find the next available log file name +def get_next_log_file(base_name="experiment_log", ext=".txt"): + counter = 1 + while os.path.exists(f"{base_name}_{counter}{ext}"): + counter += 1 + return f"{base_name}_{counter}{ext}" + +# Function to log results +def log_results(log_file, test_results, model_instance): + with open(log_file, "a") as f: + f.write("\n" + "=" * 40 + "\n") + f.write(f"Experiment conducted on: {datetime.now()}\n") + f.write("\nHyperparameters:\n") + for attr, value in vars(model_instance).items(): + f.write(f"{attr}: {value}\n") + f.write("\nTest Results:\n") + f.write(test_results) + f.write("\n" + "=" * 40 + "\n") + +# Load the MovieLens 100K dataset +ml_100k = cornac.datasets.movielens.load_feedback() + +# Take only a subset of the data, e.g., first 5000 interactions for quicker tests +# ml_100k = ml_100k[:500] + +# Split the data +rs = RatioSplit(data=ml_100k, test_size=0.2, rating_threshold=4.0, seed=123) + +# Get the total number of users and items in the subset +n_u = rs.total_users +n_m = rs.total_items + +print('Data matrix loaded') +print('Number of users: {}'.format(n_u)) +print('Number of movies: {}'.format(n_m)) +print('Number of training ratings: {}'.format(len(rs.train_set.uir_tuple[2]))) +print('Number of test ratings: {}'.format(len(rs.test_set.uir_tuple[2]))) + +# Initialize your model +my_model = GlobalLocalKernel( + # Example hyperparameters + n_hid=10, + n_dim=2, + max_epoch_p=500, + max_epoch_f=500, + lr_p=0.1, + lr_f=0.01, + verbose=False +) + +mf = MF(k=10, max_iter=25, learning_rate=0.01, lambda_reg=0.02, use_bias=True, seed=123) +pmf = PMF(k=10, max_iter=100, learning_rate=0.001, lambda_reg=0.001, seed=123) +bpr = BPR(k=10, max_iter=200, learning_rate=0.001, lambda_reg=0.01, seed=123) + +# Define some basic metrics +metrics = [MAE(), RMSE(), Precision(k=10), Recall(k=10), NDCG(k=10), AUC(), MAP()] + +# Redirect Cornac output to capture experiment results +from io import StringIO +import sys + +# Get the next available log file name +log_file = get_next_log_file() +sys.stdout = StringIO() # Redirect stdout to capture results + +# Run the experiment on the smaller subset +cornac.Experiment(eval_method=rs, models=[my_model, mf, pmf, bpr], metrics=metrics, user_based=True).run() + +# Retrieve experiment results +experiment_results = sys.stdout.getvalue() +sys.stdout = sys.__stdout__ # Restore stdout to original state + +# Print the results to the console +print(experiment_results) + +# Log results to file +log_results(log_file, experiment_results, my_model) + +print(f"Experiment results and hyperparameters saved to {log_file}") diff --git a/tutorials/vbpr_text.ipynb b/tutorials/vbpr_text.ipynb index 22ab5dc73..a2a541c89 100644 --- a/tutorials/vbpr_text.ipynb +++ b/tutorials/vbpr_text.ipynb @@ -1,35 +1,10 @@ { - "nbformat": 4, - "nbformat_minor": 0, - "metadata": { - "kernelspec": { - "name": "python3", - "display_name": "Python 3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.5.2" - }, - "colab": { - "name": "vbpr_text.ipynb", - "provenance": [] - }, - "accelerator": "GPU" - }, "cells": [ { "cell_type": "markdown", "metadata": { - "id": "uxfPR6vG5GP0", - "colab_type": "text" + "colab_type": "text", + "id": "uxfPR6vG5GP0" }, "source": [ "*Copyright (c) Cornac Authors. All rights reserved.*\n", @@ -42,8 +17,8 @@ { "cell_type": "markdown", "metadata": { - "id": "G0R8gyyt5GP4", - "colab_type": "text" + "colab_type": "text", + "id": "G0R8gyyt5GP4" }, "source": [ "\n", @@ -59,8 +34,8 @@ { "cell_type": "markdown", "metadata": { - "id": "TURsVngV5GP5", - "colab_type": "text" + "colab_type": "text", + "id": "TURsVngV5GP5" }, "source": [ "## Overview\n", @@ -72,29 +47,47 @@ }, { "cell_type": "code", + "execution_count": null, "metadata": { - "id": "d7087AnL5Jte", + "colab": {}, "colab_type": "code", - "colab": {} + "id": "d7087AnL5Jte" }, + "outputs": [], "source": [ "# install Cornac and PyTorch (VBPR model implementation uses PyTorch)\n", "!pip3 install cornac torch>=0.4.1" - ], - "execution_count": 0, - "outputs": [] + ] }, { "cell_type": "code", + "execution_count": 1, "metadata": { - "id": "8e1edON-5GP7", - "colab_type": "code", "colab": { "base_uri": "https://localhost:8080/", "height": 34 }, + "colab_type": "code", + "id": "8e1edON-5GP7", "outputId": "f609c795-cadb-45d5-fab1-25a0879853ff" }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "d:\\ProgramFiles\\Anaconda\\envs\\cornac\\Lib\\site-packages\\tqdm\\auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n", + " from .autonotebook import tqdm as notebook_tqdm\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Cornac version: 2.2.2\n" + ] + } + ], "source": [ "import cornac\n", "from cornac.data import Reader\n", @@ -104,23 +97,13 @@ "from cornac.data.text import BaseTokenizer\n", "\n", "print(\"Cornac version: {}\".format(cornac.__version__))" - ], - "execution_count": 2, - "outputs": [ - { - "output_type": "stream", - "text": [ - "Cornac version: 1.4.0\n" - ], - "name": "stdout" - } ] }, { "cell_type": "markdown", "metadata": { - "id": "H8OadBhB5GQE", - "colab_type": "text" + "colab_type": "text", + "id": "H8OadBhB5GQE" }, "source": [ "## Prepare data\n", @@ -129,25 +112,97 @@ }, { "cell_type": "code", + "execution_count": 2, "metadata": { - "id": "bP9jY6dl5GQF", + "colab": {}, "colab_type": "code", - "colab": {} + "id": "bP9jY6dl5GQF" }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Data from https://static.preferred.ai/cornac/datasets/movielens/ml_plot.zip\n", + "will be cached into C:\\Users\\Rachid\\.cornac\\movielens/ml_plot.dat\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "3.60MB [00:05, 680kB/s] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Unzipping ...\n", + "File cached!\n" + ] + } + ], "source": [ "plots, movie_ids = movielens.load_plot()\n", "\n", "# movies without plots are filtered out by `cornac.data.Reader`\n", "ml_100k = movielens.load_feedback(reader=Reader(item_set=movie_ids))" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[('196', '242', 3.0),\n", + " ('186', '302', 3.0),\n", + " ('22', '377', 1.0),\n", + " ('244', '51', 2.0),\n", + " ('166', '346', 1.0)]" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } ], - "execution_count": 0, - "outputs": [] + "source": [ + "ml_100k[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['a\\tlittle\\tboy\\tname\\tandy\\tlove\\tto\\tbe\\tin\\the\\troom\\tplay\\twith\\the\\ttoy\\tespecially\\the\\tdoll\\tname\\twoody\\tbut\\twhat\\tdo\\tthe\\ttoy\\tdo\\twhen\\tandy\\tbe\\tnot\\twith\\tthey\\tthey\\tcome\\tto\\tlife\\twoody\\tbelieve\\tthat\\the\\thave\\tlife\\tas\\ta\\ttoy\\tgood\\thowever\\the\\tmust\\tworry\\tabout\\tandy\\tfamily\\tmoving\\tand\\twhat\\twoody\\tdo\\tnot\\tknow\\tbe\\tabout\\tandy\\tbirthday\\tparty\\twoody\\tdo\\tnot\\trealize\\tthat\\tandy\\tmother\\tgive\\the\\ta\\taction\\tfigure\\tknow\\tas\\tbuzz\\tlightyear\\twho\\tdo\\tnot\\tbelieve\\tthat\\the\\tbe\\ta\\ttoy\\tand\\tquickly\\tbecome\\tandy\\tnew\\tfavorite\\ttoy\\twoody\\twho\\tbe\\tnow\\tconsume\\twith\\tjealousy\\ttry\\tto\\tget\\trid\\tof\\tbuzz\\tthen\\tboth\\twoody\\tand\\tbuzz\\tbe\\tnow\\tlose\\tthey\\tmust\\tfind\\ta\\tway\\tto\\tget\\tback\\tto\\tandy\\tbefore\\the\\tmove\\twithout\\tthey\\tbut\\tthey\\twill\\thave\\tto\\tpass\\tthrough\\ta\\truthless\\ttoy\\tkiller\\tsid\\tphillips\\t|toy\\tstory\\tbe\\tabout\\tthe\\tsecret\\tlife\\tof\\ttoy\\twhen\\tpeople\\tbe\\tnot\\taround\\twhen\\tbuzz\\tlightyear\\ta\\ttake\\twoody\\tplace\\tas\\tandy\\tfavorite\\ttoy\\twoody\\tdo\\tnot\\tlike\\tthe\\tsituation\\tand\\tget\\tinto\\ta\\tfight\\twith\\tbuzz\\taccidentaly\\tbuzz\\tfall\\tout\\tthe\\twindow\\tand\\twoody\\tbe\\taccuse\\tby\\tall\\tthe\\tother\\ttoy\\tof\\thave\\tkill\\the\\the\\thave\\tto\\tgo\\tout\\tof\\tthe\\thouse\\tto\\tlook\\tfor\\the\\tso\\tthat\\tthey\\tcan\\tboth\\treturn\\tto\\tandys\\troom\\tbut\\twhile\\ton\\tthe\\toutside\\tthey\\tget\\tinto\\tall\\tkind\\tof\\ttrouble\\twhile\\ttry\\tto\\tget\\thome\\t|imagination\\trun\\trampant\\twhen\\ttoy\\tbecome\\tmobile\\twhen\\tnot\\twatch\\ttwo\\ttoy\\twoody\\tand\\tbuzz\\tlightyear\\tdespise\\teach\\tother\\tlike\\tno\\tother\\tbut\\twhen\\tthe\\ttoy\\tbe\\tseparate\\tfrom\\tthey\\thome\\ta\\ttruce\\tbe\\tform\\tbetween\\tthey\\tall\\tin\\ta\\teffort\\tto\\tjourney\\thome\\t|a\\ttoy\\tname\\twoody\\thave\\tit\\tall\\the\\tbe\\tpractically\\tthe\\tleader\\tin\\tandy\\ttoy\\troom\\tbo\\tpeep\\thave\\tthe\\thot\\tfor\\the\\tand\\tmost\\timportantly\\the\\tbe\\tandy\\tfavorite\\ttoy\\tbut\\twhen\\tthe\\tclock\\tstrike\\tandy\\tbirthday\\ta\\tnew\\ttoy\\tarrive\\tbuzz\\tlightyear\\ta\\tspace\\tcadet\\twho\\tthink\\the\\tbe\\ta\\tspace\\tranger\\tnot\\ta\\ttoy\\tin\\ta\\troom\\tinstantly\\twin\\tover\\tandy\\tthus\\tbecome\\tandy\\tnew\\ttreasure\\tbut\\twhen\\twoody\\tbe\\taccuse\\tof\\tknock\\tbuzz\\tout\\tof\\tthe\\twindow\\the\\tmust\\tgo\\tinto\\tthe\\tworld\\tand\\tfind\\tbuzz\\twith\\tmany\\tdelay\\t|andy\\tdavis\\tbe\\ta\\tordinary\\tboy\\tor\\tso\\the\\tthink\\the\\tbaby\\tsister\\tmother\\tand\\the\\tlive\\tin\\ta\\tnice\\tlittle\\thouse\\tthe\\tonly\\tproblem\\tthat\\tandy\\tdo\\tnot\\tknow\\tabout\\tbe\\the\\ttoy\\tafter\\the\\tplay\\twith\\tthey\\tthey\\tcome\\tto\\tlife\\twoody\\tbo\\tpeep\\trex\\tslinky\\tpotato\\thead\\thamm\\tlenny\\tslinky\\tspell\\tand\\ta\\tfew\\tother\\tbe\\the\\ttoy\\tthen\\tas\\ta\\trivalry\\tbegin\\tbetween\\twoody\\tand\\tnew\\ttoy\\tbuzz\\t|',\n", + " 'after\\tbe\\ttrap\\tin\\ta\\tjungle\\tboard\\tgame\\tfor\\t26\\tyear\\ta\\twin\\the\\trelease\\tfrom\\tthe\\tgame\\tbut\\tno\\tsooner\\thave\\the\\tarrive\\tthat\\the\\tbe\\tforce\\tto\\tplay\\tagain\\tand\\tthis\\ttime\\tset\\tthe\\tcreature\\tof\\tthe\\tjungle\\tloose\\ton\\tthe\\tcity\\tnow\\tit\\tbe\\tup\\tto\\the\\tto\\tstop\\tthey\\t|alan\\tparris\\thave\\tbe\\ttrap\\tin\\ta\\tancient\\tmagical\\tboard\\tgame\\tjumanji\\tfor\\t25\\tyear\\twhen\\the\\tbe\\tfinally\\tfree\\tby\\ttwo\\tchild\\ta\\therd\\tof\\twild\\texotic\\tanimal\\thave\\taccidentally\\tbe\\trelease\\tas\\twell\\tnow\\talan\\tmust\\ttry\\tto\\tsave\\the\\thometown\\tfrom\\tdestruction\\t|old\\talan\\tparrish\\tfind\\tjumanji\\ta\\tboard\\tgame\\tin\\t1969\\the\\tand\\tsarah\\twhittle\\tplay\\tit\\tthat\\tnight\\twhen\\ta\\tquote\\tsay\\tin\\tthe\\tjungle\\tyou\\tmust\\twait\\tuntil\\tthe\\tdice\\troll\\t5\\tor\\t8\\talan\\tbe\\tsuddenly\\tpull\\tinto\\tthe\\tgame\\tyear\\tlater\\ttwo\\tmore\\tkid\\tfind\\tthis\\tgame\\tone\\tof\\tthey\\troll\\ta\\t5\\tand\\talan\\tparrish\\tcome\\tback\\tbut\\the\\tbe\\tsoon\\tforce\\tto\\tfind\\tsarah\\tand\\tfinish\\tthe\\tgame\\t|when\\tyoung\\talan\\tparrish\\tdiscover\\ta\\tmysterious\\tboard\\tgame\\the\\tdo\\tnot\\trealize\\tits\\tunimaginable\\tpower\\tuntil\\the\\tbe\\tmagically\\ttransport\\tbefore\\tthe\\tstartled\\teye\\tof\\the\\tfriend\\tsarah\\tinto\\tthe\\tuntame\\tjungle\\tof\\tjumanji\\tthere\\the\\tremain\\tfor\\t26\\tyear\\tuntil\\the\\tbe\\tfree\\tfrom\\tthe\\tgame\\tspell\\tby\\ttwo\\tunsuspecting\\tchild\\tnow\\ta\\tgrown\\tman\\talan\\treunite\\twith\\tsarah\\tand\\ttogether\\twith\\tjudy\\tand\\tpeter\\ttry\\tto\\toutwit\\tthe\\tgame\\tpowerful\\tforce\\t|',\n", + " 'thing\\tdo\\tnot\\tseem\\tto\\tchange\\tmuch\\tin\\twabasha\\tcounty\\tmax\\tand\\tjohn\\tbe\\tstill\\tfight\\tafter\\t35\\tyear\\tgrandpa\\tstill\\tdrink\\tsmoke\\tand\\tchase\\twoman\\tand\\tnobody\\tbe\\table\\tto\\tcatch\\tthe\\tfabled\\tcatfish\\thunter\\ta\\tgigantic\\tcatfish\\tthat\\tactually\\tsmile\\tat\\tfisherman\\twho\\ttry\\tto\\tsnare\\tit\\tsix\\tmonth\\tago\\tjohn\\tmarry\\tthe\\tnew\\tgirl\\tin\\ttown\\tariel\\tand\\tpeople\\tbegin\\tto\\tsuspect\\tthat\\tmax\\tmight\\tbe\\tmiss\\tsomething\\tsimilar\\tin\\the\\tlife\\tthe\\tonly\\tjoy\\tmax\\tclaim\\tbe\\tleave\\tin\\the\\tlife\\tbe\\tfishing\\tbut\\tthat\\tmight\\tchange\\twith\\tthe\\tnew\\towner\\tof\\tthe\\tbait\\tshop\\t|',\n", + " 'this\\tstory\\tbase\\ton\\tthe\\tbest\\tselling\\tnovel\\tby\\tterry\\tmcmillan\\tfollow\\tthe\\tlife\\tof\\tfour\\twoman\\tas\\tthey\\ttry\\tto\\tdeal\\twith\\tthey\\tvery\\tlive\\tfriendship\\tbecome\\tthe\\tstrongest\\tbond\\tbetween\\tthese\\twoman\\tas\\tman\\tcareer\\tand\\tfamily\\ttake\\tthey\\tin\\tdifferent\\tdirection\\toften\\tthis\\tmovie\\tspeak\\tabout\\tsome\\tof\\tthe\\tproblem\\tand\\tstruggle\\tthe\\tmodern\\twoman\\tface\\tin\\ttoday\\tworld\\t|',\n", + " 'in\\tthis\\tsequel\\tto\\tfather\\tof\\tthe\\tbride\\tgeorge\\tbanks\\tmust\\taccept\\tthe\\treality\\tof\\twhat\\the\\tdaughter\\tascension\\tfrom\\tdaughter\\tto\\twife\\tand\\tnow\\tto\\tmother\\tmean\\twhen\\tplace\\tinto\\tperspective\\tagainst\\the\\town\\tstage\\tof\\tlife\\tas\\tthe\\tcomfortable\\tfamily\\tunit\\tstart\\tto\\tunravel\\tin\\the\\tmind\\ta\\trapid\\tprogression\\tinto\\tcrisis\\tbe\\tin\\the\\tfuture\\the\\tjourney\\tto\\tregain\\the\\tyouth\\tact\\tas\\ta\\tcatalyst\\tfor\\ta\\tkind\\tof\\trebirth\\tof\\the\\tattitude\\ton\\tlife\\twhen\\the\\tand\\the\\twife\\tnina\\tfind\\thow\\tthey\\tlife\\tbe\\tabout\\tto\\tchange\\tas\\twell\\t|family\\ttrouble\\tcontinue\\tto\\tplague\\tgeorge\\tbanks\\thave\\tsurvive\\the\\tdaughter\\tmarriage\\tin\\tthe\\tfirst\\tfilm\\the\\tmust\\tnow\\tdeal\\twith\\tshe\\tpregnancy\\tto\\tcomplicate\\tmatter\\the\\twife\\tnina\\tbe\\tpregnant\\tas\\twell\\t|']" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "plots[:5]\n" + ] }, { "cell_type": "markdown", "metadata": { - "id": "43wktekd5GQJ", - "colab_type": "text" + "colab_type": "text", + "id": "43wktekd5GQJ" }, "source": [ "## Cross modality\n", @@ -157,24 +212,24 @@ }, { "cell_type": "code", + "execution_count": 8, "metadata": { - "id": "0VHNg6tm5GQK", + "colab": {}, "colab_type": "code", - "colab": {} + "id": "0VHNg6tm5GQK" }, + "outputs": [], "source": [ "item_text_modality = TextModality(corpus=plots, ids=movie_ids, \n", " tokenizer=BaseTokenizer(sep='\\t', stop_words='english'),\n", " max_vocab=5000, max_doc_freq=0.5).build()" - ], - "execution_count": 0, - "outputs": [] + ] }, { "cell_type": "markdown", "metadata": { - "id": "xGYc5L7e5GQS", - "colab_type": "text" + "colab_type": "text", + "id": "xGYc5L7e5GQS" }, "source": [ "Next step is to create an `ImageModality`, which is use by VBPR, using our text representations. In this case, we take the word-count matrix to substitute for visual features." @@ -182,23 +237,23 @@ }, { "cell_type": "code", + "execution_count": 9, "metadata": { - "id": "gUGJ5AjI5GQU", + "colab": {}, "colab_type": "code", - "colab": {} + "id": "gUGJ5AjI5GQU" }, + "outputs": [], "source": [ "features = item_text_modality.count_matrix.A\n", "item_image_modality = ImageModality(features=features, ids=movie_ids)" - ], - "execution_count": 0, - "outputs": [] + ] }, { "cell_type": "markdown", "metadata": { - "id": "Rkxw-D_m5GQY", - "colab_type": "text" + "colab_type": "text", + "id": "Rkxw-D_m5GQY" }, "source": [ "In Cornac, every model relies on the modality for which it was designed for (i.e., visual recommendation algorithms always work with `ImageModality`). This ensures consistency with models' original assumptions, and helps us avoid confusions regarding which modality to use when integrating a new recommender model.\n", @@ -210,25 +265,52 @@ }, { "cell_type": "code", + "execution_count": 17, "metadata": { - "id": "cc66mPy35GQZ", + "colab": {}, "colab_type": "code", - "colab": {} + "id": "cc66mPy35GQZ" }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "rating_threshold = 1.0\n", + "exclude_unknowns = True\n", + "---\n", + "Training data:\n", + "Number of users = 926\n", + "Number of items = 1162\n", + "Number of ratings = 9480\n", + "Max rating = 5.0\n", + "Min rating = 1.0\n", + "Global mean = 3.5\n", + "---\n", + "Test data:\n", + "Number of users = 926\n", + "Number of items = 1162\n", + "Number of ratings = 82993\n", + "Number of unknown users = 0\n", + "Number of unknown items = 0\n", + "---\n", + "Total users = 926\n", + "Total items = 1162\n" + ] + } + ], "source": [ "ratio_split = RatioSplit(data=ml_100k, test_size=0.9,\n", " item_image=item_image_modality,\n", " exclude_unknowns=True, \n", " verbose=True, seed=123)" - ], - "execution_count": 0, - "outputs": [] + ] }, { "cell_type": "markdown", "metadata": { - "id": "bKalGw5x5GQe", - "colab_type": "text" + "colab_type": "text", + "id": "bKalGw5x5GQe" }, "source": [ "We are now ready to evaluate performance of VBPR. The [BPR](https://arxiv.org/ftp/arxiv/papers/1205/1205.2618.pdf) model is also included as a baseline to examine the effectiveness of the text auxiliary data." @@ -236,54 +318,95 @@ }, { "cell_type": "code", + "execution_count": 18, "metadata": { - "id": "rczsfDrO5GQg", + "colab": {}, "colab_type": "code", - "colab": {} + "id": "rczsfDrO5GQg" }, + "outputs": [], "source": [ "vbpr = cornac.models.VBPR(k=10, k2=10, n_epochs=20, batch_size=10, learning_rate=0.001,\n", " lambda_w=1.0, lambda_b=0.0, lambda_e=100.0, use_gpu=True, seed=123)\n", "\n", "bpr = cornac.models.BPR(k=10, max_iter=100, learning_rate=0.001, lambda_reg=0.001, seed=123)" - ], - "execution_count": 0, - "outputs": [] + ] }, { "cell_type": "code", + "execution_count": 19, "metadata": { - "id": "giDcupSL5GQk", + "colab": {}, "colab_type": "code", - "colab": {} + "id": "giDcupSL5GQk" }, + "outputs": [], "source": [ "auc = cornac.metrics.AUC()\n", "rec_50 = cornac.metrics.Recall(k=50)" - ], - "execution_count": 0, - "outputs": [] + ] }, { "cell_type": "code", + "execution_count": 20, "metadata": { - "id": "yes6tv-15GQo", + "colab": {}, "colab_type": "code", - "colab": {} + "id": "yes6tv-15GQo" }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "[BPR] Training started!\n", + "\n", + "[BPR] Evaluation started!\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Ranking: 100%|██████████| 926/926 [00:01<00:00, 605.81it/s]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "[VBPR] Training started!\n" + ] + }, + { + "ename": "ModuleNotFoundError", + "evalue": "No module named 'torch'", + "output_type": "error", + "traceback": [ + "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[1;31mModuleNotFoundError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[1;32mIn[20], line 3\u001b[0m\n\u001b[0;32m 1\u001b[0m cornac\u001b[39m.\u001b[39;49mExperiment(eval_method\u001b[39m=\u001b[39;49mratio_split,\n\u001b[0;32m 2\u001b[0m models\u001b[39m=\u001b[39;49m[bpr, vbpr],\n\u001b[1;32m----> 3\u001b[0m metrics\u001b[39m=\u001b[39;49m[auc, rec_50])\u001b[39m.\u001b[39;49mrun()\n", + "File \u001b[1;32md:\\ProgramFiles\\Anaconda\\envs\\cornac\\Lib\\site-packages\\cornac\\experiment\\experiment.py:142\u001b[0m, in \u001b[0;36mExperiment.run\u001b[1;34m(self)\u001b[0m\n\u001b[0;32m 139\u001b[0m model\u001b[39m.\u001b[39mverbose \u001b[39m=\u001b[39m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39mverbose\n\u001b[0;32m 141\u001b[0m \u001b[39mfor\u001b[39;00m model \u001b[39min\u001b[39;00m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39mmodels:\n\u001b[1;32m--> 142\u001b[0m test_result, val_result \u001b[39m=\u001b[39m \u001b[39mself\u001b[39;49m\u001b[39m.\u001b[39;49meval_method\u001b[39m.\u001b[39;49mevaluate(\n\u001b[0;32m 143\u001b[0m model\u001b[39m=\u001b[39;49mmodel,\n\u001b[0;32m 144\u001b[0m metrics\u001b[39m=\u001b[39;49m\u001b[39mself\u001b[39;49m\u001b[39m.\u001b[39;49mmetrics,\n\u001b[0;32m 145\u001b[0m user_based\u001b[39m=\u001b[39;49m\u001b[39mself\u001b[39;49m\u001b[39m.\u001b[39;49muser_based,\n\u001b[0;32m 146\u001b[0m show_validation\u001b[39m=\u001b[39;49m\u001b[39mself\u001b[39;49m\u001b[39m.\u001b[39;49mshow_validation,\n\u001b[0;32m 147\u001b[0m )\n\u001b[0;32m 149\u001b[0m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39mresult\u001b[39m.\u001b[39mappend(test_result)\n\u001b[0;32m 150\u001b[0m \u001b[39mif\u001b[39;00m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39mval_result \u001b[39mis\u001b[39;00m \u001b[39mnot\u001b[39;00m \u001b[39mNone\u001b[39;00m:\n", + "File \u001b[1;32md:\\ProgramFiles\\Anaconda\\envs\\cornac\\Lib\\site-packages\\cornac\\eval_methods\\base_method.py:734\u001b[0m, in \u001b[0;36mBaseMethod.evaluate\u001b[1;34m(self, model, metrics, user_based, show_validation)\u001b[0m\n\u001b[0;32m 731\u001b[0m \u001b[39mprint\u001b[39m(\u001b[39m\"\u001b[39m\u001b[39m\\n\u001b[39;00m\u001b[39m[\u001b[39m\u001b[39m{}\u001b[39;00m\u001b[39m] Training started!\u001b[39m\u001b[39m\"\u001b[39m\u001b[39m.\u001b[39mformat(model\u001b[39m.\u001b[39mname))\n\u001b[0;32m 733\u001b[0m start \u001b[39m=\u001b[39m time\u001b[39m.\u001b[39mtime()\n\u001b[1;32m--> 734\u001b[0m model\u001b[39m.\u001b[39;49mfit(\u001b[39mself\u001b[39;49m\u001b[39m.\u001b[39;49mtrain_set, \u001b[39mself\u001b[39;49m\u001b[39m.\u001b[39;49mval_set)\n\u001b[0;32m 735\u001b[0m train_time \u001b[39m=\u001b[39m time\u001b[39m.\u001b[39mtime() \u001b[39m-\u001b[39m start\n\u001b[0;32m 737\u001b[0m \u001b[39m##############\u001b[39;00m\n\u001b[0;32m 738\u001b[0m \u001b[39m# EVALUATION #\u001b[39;00m\n\u001b[0;32m 739\u001b[0m \u001b[39m##############\u001b[39;00m\n", + "File \u001b[1;32md:\\ProgramFiles\\Anaconda\\envs\\cornac\\Lib\\site-packages\\cornac\\models\\vbpr\\recom_vbpr.py:165\u001b[0m, in \u001b[0;36mVBPR.fit\u001b[1;34m(self, train_set, val_set)\u001b[0m\n\u001b[0;32m 158\u001b[0m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39m_init(\n\u001b[0;32m 159\u001b[0m n_users\u001b[39m=\u001b[39m\u001b[39mself\u001b[39m\u001b[39m.\u001b[39mtotal_users,\n\u001b[0;32m 160\u001b[0m n_items\u001b[39m=\u001b[39m\u001b[39mself\u001b[39m\u001b[39m.\u001b[39mtotal_items,\n\u001b[0;32m 161\u001b[0m features\u001b[39m=\u001b[39mtrain_features,\n\u001b[0;32m 162\u001b[0m )\n\u001b[0;32m 164\u001b[0m \u001b[39mif\u001b[39;00m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39mtrainable:\n\u001b[1;32m--> 165\u001b[0m \u001b[39mself\u001b[39;49m\u001b[39m.\u001b[39;49m_fit_torch(train_set, train_features)\n\u001b[0;32m 167\u001b[0m \u001b[39mreturn\u001b[39;00m \u001b[39mself\u001b[39m\n", + "File \u001b[1;32md:\\ProgramFiles\\Anaconda\\envs\\cornac\\Lib\\site-packages\\cornac\\models\\vbpr\\recom_vbpr.py:170\u001b[0m, in \u001b[0;36mVBPR._fit_torch\u001b[1;34m(self, train_set, train_features)\u001b[0m\n\u001b[0;32m 169\u001b[0m \u001b[39mdef\u001b[39;00m \u001b[39m_fit_torch\u001b[39m(\u001b[39mself\u001b[39m, train_set, train_features):\n\u001b[1;32m--> 170\u001b[0m \u001b[39mimport\u001b[39;00m \u001b[39mtorch\u001b[39;00m\n\u001b[0;32m 172\u001b[0m \u001b[39mdef\u001b[39;00m \u001b[39m_l2_loss\u001b[39m(\u001b[39m*\u001b[39mtensors):\n\u001b[0;32m 173\u001b[0m l2_loss \u001b[39m=\u001b[39m \u001b[39m0\u001b[39m\n", + "\u001b[1;31mModuleNotFoundError\u001b[0m: No module named 'torch'" + ] + } + ], "source": [ "cornac.Experiment(eval_method=ratio_split,\n", " models=[bpr, vbpr],\n", " metrics=[auc, rec_50]).run()" - ], - "execution_count": 0, - "outputs": [] + ] }, { "cell_type": "markdown", "metadata": { - "id": "_Nbplrac5GQs", - "colab_type": "text" + "colab_type": "text", + "id": "_Nbplrac5GQs" }, "source": [ "Results after running the experiment:\n", @@ -298,5 +421,30 @@ "" ] } - ] -} \ No newline at end of file + ], + "metadata": { + "accelerator": "GPU", + "colab": { + "name": "vbpr_text.ipynb", + "provenance": [] + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.7" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +}