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": [ "