def _get_negative_prediction(self, shape, user_representation): negative_items = sample_items( self._num_items, shape, random_state=self._random_state) negative_var = Variable( gpu(torch.from_numpy(negative_items), self._use_cuda) ) negative_prediction = self._net(user_representation, negative_var) return negative_prediction
def _get_negative_prediction(self, user_ids): negative_items = sample_items( self._num_items, len(user_ids), random_state=self._random_state) negative_var = Variable( gpu(torch.from_numpy(negative_items), self._use_cuda) ) negative_prediction = self._net(user_ids, negative_var) return negative_prediction
def predict(self, sequences, item_ids=None): """ Make predictions: given a sequence of interactions, predict the next item in the sequence. Parameters ---------- sequences: array, (1 x max_sequence_length) Array containing the indices of the items in the sequence. item_ids: array (num_items x 1), optional Array containing the item ids for which prediction scores are desired. If not supplied, predictions for all items will be computed. Returns ------- predictions: array Predicted scores for all items in item_ids. """ self._net.train(False) sequences = np.atleast_2d(sequences) if item_ids is None: item_ids = np.arange(self._num_items).reshape(-1, 1) sequences = torch.from_numpy(sequences.astype(np.int64).reshape(1, -1)) item_ids = torch.from_numpy(item_ids.astype(np.int64)) sequence_var = Variable(gpu(sequences, self._use_cuda)) item_var = Variable(gpu(item_ids, self._use_cuda)) _, sequence_representations = self._net.user_representation(sequence_var) out = self._net(sequence_representations.repeat(len(item_var), 1), item_var) return cpu(out.data).numpy().flatten()
def predict(self, user_ids, item_ids=None): """ Make predictions: given a user id, compute the recommendation scores for items. Parameters ---------- user_ids: int or array If int, will predict the recommendation scores for this user for all items in item_ids. If an array, will predict scores for all (user, item) pairs defined by user_ids and item_ids. item_ids: array, optional Array containing the item ids for which prediction scores are desired. If not supplied, predictions for all items will be computed. Returns ------- predictions: np.array Predicted scores for all items in item_ids. """ if item_ids is None: item_ids = np.arange(self._num_items) if isinstance(user_ids, int): user_ids = np.repeat(user_ids, len(item_ids)) user_ids = torch.from_numpy(user_ids.reshape(-1, 1).astype(np.int64)) item_ids = torch.from_numpy(item_ids.reshape(-1, 1).astype(np.int64)) user_var = Variable(gpu(user_ids, self._use_cuda)) item_var = Variable(gpu(item_ids, self._use_cuda)) out = self._net(user_var, item_var) return cpu(out.data).numpy().flatten()
def fit(self, interactions, verbose=False): """ Fit the model. When called repeatedly, model fitting will resume from the point at which training stopped in the previous fit call. Parameters ---------- interactions: :class:`spotlight.interactions.Interactions` The input dataset. Must have ratings. """ user_ids = interactions.user_ids.astype(np.int64) item_ids = interactions.item_ids.astype(np.int64) if not self._initialized: self._initialize(interactions) self._check_input(user_ids, item_ids) for epoch_num in range(self._n_iter): users, items, ratings = shuffle(user_ids, item_ids, interactions.ratings, random_state=self._random_state) user_ids_tensor = gpu(torch.from_numpy(users), self._use_cuda) item_ids_tensor = gpu(torch.from_numpy(items), self._use_cuda) ratings_tensor = gpu(torch.from_numpy(ratings), self._use_cuda) epoch_loss = 0.0 for (minibatch_num, (batch_user, batch_item, batch_ratings)) in enumerate(minibatch(user_ids_tensor, item_ids_tensor, ratings_tensor, batch_size=self._batch_size)): user_var = Variable(batch_user) item_var = Variable(batch_item) ratings_var = Variable(batch_ratings) predictions = self._net(user_var, item_var) if self._loss == 'poisson': predictions = torch.exp(predictions) self._optimizer.zero_grad() loss = self._loss_func(ratings_var, predictions) epoch_loss += loss.data[0] loss.backward() self._optimizer.step() epoch_loss /= minibatch_num + 1 if verbose: print('Epoch {}: loss {}'.format(epoch_num, epoch_loss))
def fit(self, interactions, verbose=False, return_loss=False): """ Fit the model. When called repeatedly, model fitting will resume from the point at which training stopped in the previous fit call. Parameters ---------- interactions: :class:`spotlight.interactions.Interactions` The input dataset. verbose: bool Output additional information about current epoch and loss. """ user_ids = interactions.user_ids.astype(np.int64) item_ids = interactions.item_ids.astype(np.int64) if not self._initialized: self._initialize(interactions) self._check_input(user_ids, item_ids) for epoch_num in range(self._n_iter): users, items = shuffle(user_ids, item_ids, random_state=self._random_state) user_ids_tensor = gpu(torch.from_numpy(users), self._use_cuda) item_ids_tensor = gpu(torch.from_numpy(items), self._use_cuda) epoch_loss = 0.0 epoch_cov_norm = 0.0 for (minibatch_num, (batch_user, batch_item)) in enumerate( minibatch(user_ids_tensor, item_ids_tensor, batch_size=self._batch_size)): positive_prediction = self._net(batch_user, batch_item) if self._loss in ('warp', 'adaptive_hinge'): negative_prediction = self._get_multiple_negative_predictions( batch_user, n=self._num_negative_samples) else: negative_prediction = self._get_negative_prediction( batch_user) self._optimizer.zero_grad() if self._margin is not None: loss = self._loss_func(positive_prediction, negative_prediction, m=self._margin) else: loss = self._loss_func(positive_prediction, negative_prediction) epoch_loss += loss.item() if self._cov_reg is not None: cov_loss = self._covariance_loss() loss += cov_loss * self._cov_reg epoch_cov_norm += cov_loss.item() loss.backward() self._optimizer.step() epoch_loss /= minibatch_num + 1 epoch_cov_norm /= minibatch_num + 1 if verbose: print('Epoch {}: loss {}, cov_norm {}'.format( epoch_num, epoch_loss, epoch_cov_norm)) if np.isnan(epoch_loss) or epoch_loss == 0.0: raise ValueError( 'Degenerate epoch loss: {}'.format(epoch_loss)) if return_loss: return epoch_loss, epoch_cov_norm
def fit(self, interactions, verbose=False): """ Fit the model. Parameters ---------- interactions: :class:`spotlight.interactions.Interactions` The input dataset. Must have ratings. """ user_ids = interactions.user_ids.astype(np.int64) item_ids = interactions.item_ids.astype(np.int64) (self._num_users, self._num_items) = (interactions.num_users, interactions.num_items) self._net = gpu( BilinearNet(self._num_users, self._num_items, self._embedding_dim, sparse=self._sparse), self._use_cuda ) if self._optimizer_func is None: self._optimizer = optim.Adam( self._net.parameters(), weight_decay=self._l2, lr=self._learning_rate ) else: self._optimizer = self._optimizer_func(self._net.parameters()) if self._loss == 'regression': loss_fnc = regression_loss elif self._loss == 'poisson': loss_fnc = poisson_loss else: raise ValueError('Unknown loss: {}'.format(self._loss)) for epoch_num in range(self._n_iter): users, items, ratings = shuffle(user_ids, item_ids, interactions.ratings, random_state=self._random_state) user_ids_tensor = gpu(torch.from_numpy(users), self._use_cuda) item_ids_tensor = gpu(torch.from_numpy(items), self._use_cuda) ratings_tensor = gpu(torch.from_numpy(ratings), self._use_cuda) epoch_loss = 0.0 for (minibatch_num, (batch_user, batch_item, batch_ratings)) in enumerate(minibatch(user_ids_tensor, item_ids_tensor, ratings_tensor, batch_size=self._batch_size)): user_var = Variable(batch_user) item_var = Variable(batch_item) ratings_var = Variable(batch_ratings) predictions = self._net(user_var, item_var) if self._loss == 'poisson': predictions = torch.exp(predictions) self._optimizer.zero_grad() loss = loss_fnc(ratings_var, predictions) epoch_loss += loss.data[0] loss.backward() self._optimizer.step() epoch_loss /= minibatch_num + 1 if verbose: print('Epoch {}: loss {}'.format(epoch_num, epoch_loss))
def fit(self, interactions, validation_interactions=None, verbose=False): """ Fit the model. When called repeatedly, model fitting will resume from the point at which training stopped in the previous fit call. Parameters ---------- interactions: :class:`spotlight.interactions.SequenceInteractions` The input sequence dataset. """ train_loss = np.zeros(self._n_iter) val_loss = np.zeros(self._n_iter) sequences = interactions.sequences.astype(np.int64) weights = interactions.weights.astype(np.float32) if not self._initialized: self._initialize(interactions) self._check_input(sequences) if validation_interactions is not None: validation_sequences = validation_interactions.sequences.astype( np.int64) validation_weights = validation_interactions.weights.astype( np.float32) self._check_input(validation_sequences) for epoch_num in range(self._n_iter): sequences, weights = shuffle(sequences, weights, random_state=self._random_state) sequences_tensor = gpu(torch.from_numpy(sequences), self._use_cuda) weights_tensor = gpu(torch.from_numpy(weights), self._use_cuda) epoch_loss = 0.0 for minibatch_num, (batch_sequence, batch_weights) in enumerate( minibatch(sequences_tensor, weights_tensor, batch_size=self._batch_size)): sequence_var = batch_sequence weights_var = batch_weights user_representation, _ = self._net.user_representation( sequence_var) positive_prediction = self._net(user_representation, sequence_var) if self._loss == 'adaptive_hinge': negative_prediction = self._get_multiple_negative_predictions( sequence_var.size(), user_representation, n=self._num_negative_samples) else: negative_prediction = self._get_negative_prediction( sequence_var.size(), user_representation) self._optimizer.zero_grad() loss = self._loss_func(positive_prediction, negative_prediction, mask=(sequence_var != PADDING_IDX), weights=weights_var) epoch_loss += loss.item() loss.backward() self._optimizer.step() train_loss[epoch_num] = epoch_loss / (minibatch_num + 1) if validation_interactions is not None: val_loss[epoch_num] = self._get_validation_loss( validation_sequences, validation_weights) if verbose and validation_interactions is not None: print('Epoch {}: loss {} \t val-loss {}'.format( epoch_num, train_loss[epoch_num], val_loss[epoch_num])) else: print('Epoch {}: loss {}'.format(epoch_num, train_loss[epoch_num])) if np.isnan(epoch_loss) or epoch_loss == 0.0: raise ValueError( 'Degenerate epoch loss: {}'.format(epoch_loss)) return train_loss, val_loss
def fit(self, interactions, verbose=False): """ Fit the model. Parameters ---------- interactions: :class:`spotlight.interactions.Interactions` The input dataset. Must have ratings. """ user_ids = interactions.user_ids.astype(np.int64) item_ids = interactions.item_ids.astype(np.int64) (self._num_users, self._num_items) = (interactions.num_users, interactions.num_items) self._net = gpu( BilinearNet(self._num_users, self._num_items, self._embedding_dim, sparse=self._sparse), self._use_cuda ) if self._optimizer is None: self._optimizer = optim.Adam( self._net.parameters(), weight_decay=self._l2, lr=self._learning_rate ) if self._loss == 'regression': loss_fnc = regression_loss elif self._loss == 'poisson': loss_fnc = poisson_loss else: raise ValueError('Unknown loss: {}'.format(self._loss)) for epoch_num in range(self._n_iter): users, items, ratings = shuffle(user_ids, item_ids, interactions.ratings, random_state=self._random_state) user_ids_tensor = gpu(torch.from_numpy(users), self._use_cuda) item_ids_tensor = gpu(torch.from_numpy(items), self._use_cuda) ratings_tensor = gpu(torch.from_numpy(ratings), self._use_cuda) epoch_loss = 0.0 for (minibatch_num, (batch_user, batch_item, batch_ratings)) in enumerate(minibatch(user_ids_tensor, item_ids_tensor, ratings_tensor, batch_size=self._batch_size)): user_var = Variable(batch_user) item_var = Variable(batch_item) ratings_var = Variable(batch_ratings) predictions = self._net(user_var, item_var) if self._loss == 'poisson': predictions = torch.exp(predictions) self._optimizer.zero_grad() loss = loss_fnc(ratings_var, predictions) epoch_loss += loss.data[0] loss.backward() self._optimizer.step() epoch_loss /= minibatch_num + 1 if verbose: print('Epoch {}: loss {}'.format(epoch_num, epoch_loss))
def fit(self, interactions, verbose=False): """ Fit the model. Parameters ---------- interactions: :class:`spotlight.interactions.SequenceInteractions` The input sequence dataset. """ sequences = interactions.sequences.astype(np.int64) self._num_items = interactions.num_items if self._representation == 'pooling': self._net = PoolNet(self._num_items, self._embedding_dim, sparse=self._sparse) elif self._representation == 'cnn': self._net = CNNNet(self._num_items, self._embedding_dim, sparse=self._sparse) elif self._representation == 'lstm': self._net = LSTMNet(self._num_items, self._embedding_dim, sparse=self._sparse) else: self._net = self._representation self._net = gpu(self._net, self._use_cuda) if self._optimizer is None: self._optimizer = optim.Adam(self._net.parameters(), weight_decay=self._l2, lr=self._learning_rate) if self._loss == 'pointwise': loss_fnc = pointwise_loss elif self._loss == 'bpr': loss_fnc = bpr_loss elif self._loss == 'hinge': loss_fnc = hinge_loss else: loss_fnc = adaptive_hinge_loss for epoch_num in range(self._n_iter): sequences = shuffle(sequences, random_state=self._random_state) sequences_tensor = gpu(torch.from_numpy(sequences), self._use_cuda) epoch_loss = 0.0 for minibatch_num, batch_sequence in enumerate( minibatch(sequences_tensor, batch_size=self._batch_size)): sequence_var = Variable(batch_sequence) user_representation, _ = self._net.user_representation( sequence_var) positive_prediction = self._net(user_representation, sequence_var) if self._loss == 'adaptive_hinge': negative_prediction = [ self._get_negative_prediction(sequence_var.size(), user_representation) for __ in range(5) ] else: negative_prediction = self._get_negative_prediction( sequence_var.size(), user_representation) self._optimizer.zero_grad() loss = loss_fnc(positive_prediction, negative_prediction, mask=(sequence_var != PADDING_IDX)) epoch_loss += loss.data[0] loss.backward() self._optimizer.step() epoch_loss /= minibatch_num + 1 if verbose: print('Epoch {}: loss {}'.format(epoch_num, epoch_loss))
def fit(self, interactions, verbose=False, calc_map=False, neg_mode="original"): """ Fit the model. When called repeatedly, model fitting will resume from the point at which training stopped in the previous fit call. Parameters ---------- interactions: :class:`spotlight.interactions.SequenceInteractions` The input sequence dataset. """ sequences = interactions.sequences.astype(np.int64) user_ids = interactions.user_ids if not self._initialized: self._initialize(interactions) self._check_input(sequences) best_map = 0 np.random.seed(0) random_state = np.random.RandomState() for epoch_num in range(self._n_iter): start_time = time.time() shuffle_indices = np.arange(sequences.shape[0]) random_state.shuffle(shuffle_indices) sequences = sequences[shuffle_indices] user_ids = user_ids[shuffle_indices] #sequences = shuffle(sequences, # random_state=self._random_state) sequences_tensor = gpu(torch.from_numpy(sequences), self._use_cuda) epoch_loss = 0.0 #for minibatch_num, batch_sequence in enumerate(minibatch(sequences_tensor, # batch_size=self._batch_size)): for minibatch_num in range(sequences.shape[0] // self._batch_size): sequence_var = sequences_tensor[minibatch_num * self._batch_size: (minibatch_num + 1) * self._batch_size] mini_user_ids = user_ids[minibatch_num * self._batch_size:(minibatch_num + 1) * self._batch_size] #sequence_var = batch_sequence user_representation, _ = self._net.user_representation( sequence_var) positive_prediction = self._net(user_representation, sequence_var) if neg_mode == "original": if self._loss == 'adaptive_hinge': negative_prediction = self._get_multiple_negative_predictions( sequence_var.size(), user_representation, n=self._num_negative_samples) else: negative_prediction = self._get_negative_prediction( sequence_var.size(), user_representation) elif neg_mode == "mine": ''' example: sequence = [1,4,2,48,999,8], userid = 0 use neg_prob of user 0 to pick a food 'x' replace the 8 with 'x' negative sequence = [1,4,2,48,999,x] ''' if self._loss == 'adaptive_hinge' or self._loss == 'mine': negative_prediction = self.my_get_multiple_negative_predictions2( sequence_var.cpu().data, mini_user_ids, user_representation, n=self._num_negative_samples) #tmp = np.zeros((self._batch_size * self._num_negative_samples, negative_prediction.size(1))) #tmp[:, -1] = 1 #tmp = gpu(torch.from_numpy(tmp).float(), self._use_cuda) #negative_prediction *= tmp else: negative_prediction = self.my_get_negative_prediction2( sequence_var.cpu().data, mini_user_ids, user_representation) #tmp = np.zeros((self._batch_size, negative_prediction.size(1))) #tmp[:, -1] = 1 #tmp = gpu(torch.from_numpy(tmp).float(), self._use_cuda) #negative_prediction *= tmp self._optimizer.zero_grad() #loss = self._loss_func(positive_prediction, # negative_prediction, # mask=(sequence_var != PADDING_IDX)) loss = torch.nn.CrossEntropyLoss()(positive_prediction, sequence_var[:, -1]) epoch_loss += loss.item() loss.backward() self._optimizer.step() epoch_loss /= minibatch_num + 1 if calc_map: # takes too much time, about 20 mins, so set as an option print("calculating map") self._net.train(False) map_start = time.time() ap = NewAveragePrecision(self, self.test_data, 20) print("calculate map time", time.time() - map_start) self._net.train(True) else: ap = np.zeros(10) if verbose: print('Epoch {}: loss {} map: {}; time: {}'.format( epoch_num, epoch_loss, ap.mean(), time.time() - start_time)) if np.isnan(epoch_loss) or epoch_loss == 0.0: raise ValueError( 'Degenerate epoch loss: {}'.format(epoch_loss))
def fit(self, interactions, verbose=False): """ Fit the model. When called repeatedly, model fitting will resume from the point at which training stopped in the previous fit call. Parameters ---------- interactions: :class:`spotlight.interactions.Interactions` The input dataset. verbose: bool Output additional information about current epoch and loss. """ user_ids = interactions.user_ids.astype(np.int64) item_ids = interactions.item_ids.astype(np.int64) ratings = interactions.ratings.astype(np.int64) user_ids = user_ids[0:self.inputSample] item_ids = item_ids[0:self.inputSample] ratings = ratings[0:self.inputSample] # pdb.Pdb.complete = rlcompleter.Completer(locals()).complete # pdb.set_trace() if not self._initialized: self._initialize(interactions) self._check_input(user_ids, item_ids) for epoch_num in range(self._n_iter): users, items = shuffle(user_ids, item_ids, random_state=self._random_state) user_ids_tensor = gpu(torch.from_numpy(users), self._use_cuda) item_ids_tensor = gpu(torch.from_numpy(items), self._use_cuda) rating_ids_tensor = gpu(torch.from_numpy(ratings), self._use_cuda) epoch_loss = 0.0 for (minibatch_num, (batch_user, batch_item, batch_rating)) in enumerate(minibatch(user_ids_tensor, item_ids_tensor, rating_ids_tensor, batch_size=self._batch_size)): user_var = Variable(batch_user) pos_item = Variable(batch_item) rating = Variable(batch_rating) neg_item = self._get_negative_items(user_var) x, target = self._rankDataPrepSwapping(user_var, pos_item, rating, neg_item) pred_prob = self._net(x) self._optimizer.zero_grad() loss = self.bceLoss(pred_prob, target) # loss = self._loss_func(pred_prob, target) ''' if random > 0.5: pred = self._net(user_var, pos_item, neg_item) else: pred = self._net(user_var, neg_item, pos_item) pred = self._net(user_var, item_var, neg_item) self._optimizer.zero_grad() loss = self._loss_func(positive_prediction, negative_prediction) ''' epoch_loss += loss.data[0] loss.backward() self._optimizer.step() epoch_loss /= minibatch_num + 1 if verbose: print('Epoch {}: loss {}'.format(epoch_num, epoch_loss)) if np.isnan(epoch_loss) or epoch_loss == 0.0: raise ValueError('Degenerate epoch loss: {}' .format(epoch_loss))
def fit(self, interactions, verbose=False): """ Fit the model. Parameters ---------- interactions: :class:`spotlight.interactions.SequenceInteractions` The input sequence dataset. """ sequences = interactions.sequences.astype(np.int64) self._num_items = interactions.num_items if self._representation == 'pooling': self._net = PoolNet(self._num_items, self._embedding_dim, sparse=self._sparse) elif self._representation == 'cnn': self._net = CNNNet(self._num_items, self._embedding_dim, sparse=self._sparse) elif self._representation == 'lstm': self._net = LSTMNet(self._num_items, self._embedding_dim, sparse=self._sparse) else: self._net = self._representation self._net = gpu(self._net, self._use_cuda) if self._optimizer is None: self._optimizer = optim.Adam( self._net.parameters(), weight_decay=self._l2, lr=self._learning_rate ) else: self._optimizer = self._optimizer_func(self._net.parameters()) if self._loss == 'pointwise': loss_fnc = pointwise_loss elif self._loss == 'bpr': loss_fnc = bpr_loss elif self._loss == 'hinge': loss_fnc = hinge_loss else: loss_fnc = adaptive_hinge_loss for epoch_num in range(self._n_iter): sequences = shuffle(sequences, random_state=self._random_state) sequences_tensor = gpu(torch.from_numpy(sequences), self._use_cuda) epoch_loss = 0.0 for minibatch_num, batch_sequence in enumerate(minibatch(sequences_tensor, batch_size=self._batch_size)): sequence_var = Variable(batch_sequence) user_representation, _ = self._net.user_representation( sequence_var ) positive_prediction = self._net(user_representation, sequence_var) if self._loss == 'adaptive_hinge': negative_prediction = [self._get_negative_prediction(sequence_var.size(), user_representation) for __ in range(5)] else: negative_prediction = self._get_negative_prediction(sequence_var.size(), user_representation) self._optimizer.zero_grad() loss = loss_fnc(positive_prediction, negative_prediction, mask=(sequence_var != PADDING_IDX)) epoch_loss += loss.data[0] loss.backward() self._optimizer.step() epoch_loss /= minibatch_num + 1 if verbose: print('Epoch {}: loss {}'.format(epoch_num, epoch_loss))
def fit(self, interactions, verbose=False): """ Fit the model. When called repeatedly, model fitting will resume from the point at which training stopped in the previous fit call. Parameters ---------- interactions: :class:`spotlight.interactions.SequenceInteractions` The input sequence dataset. """ # genereate list of short sequences from a long sequence # + window_size is max_sequence_length: length of short sequences # + step_size for number of short sequences. # + For example: long sequence with length 10 with step_size = 3 # will generate 3 short sequences. sequences = interactions.sequences.astype(np.int64) if not self._initialized: self._initialize(interactions) self._check_input(sequences) for epoch_num in range(self._n_iter): sequences = shuffle(sequences, random_state=self._random_state) sequences_tensor = gpu(torch.from_numpy(sequences), self._use_cuda) epoch_loss = 0.0 for minibatch_num, batch_sequence in enumerate( minibatch(sequences_tensor, batch_size=self._batch_size)): # sequence_var is list of short sequences, size of this list equal batch_size # sequence_var shape: torch.Size([batch_size, window_size]) sequence_var = batch_sequence # user_presentation shape: torch.Size([batch_size, embedding_size, window_size]) user_representation, _ = self._net.user_representation( sequence_var) positive_prediction = self._net(user_representation, sequence_var) if self._loss == 'adaptive_hinge': negative_prediction = self._get_multiple_negative_predictions( sequence_var.size(), user_representation, n=self._num_negative_samples) else: negative_prediction = self._get_negative_prediction( sequence_var.size(), user_representation) self._optimizer.zero_grad() loss = self._loss_func(positive_prediction, negative_prediction, mask=(sequence_var != PADDING_IDX)) epoch_loss += loss.item() loss.backward() self._optimizer.step() epoch_loss /= minibatch_num + 1 if verbose: print('Epoch {}: loss {}'.format(epoch_num, epoch_loss)) if np.isnan(epoch_loss) or epoch_loss == 0.0: raise ValueError( 'Degenerate epoch loss: {}'.format(epoch_loss))
def fit(self, interactions, verbose=False): """ Fit the model. When called repeatedly, model fitting will resume from the point at which training stopped in the previous fit call. Parameters ---------- interactions: :class:`spotlight.interactions.Interactions` The input dataset. """ user_ids = interactions.user_ids.astype(np.int64) item_ids = interactions.item_ids.astype(np.int64) if not self._initialized: self._initialize(interactions) self._check_input(user_ids, item_ids) for epoch_num in range(self._n_iter): users, items = shuffle(user_ids, item_ids, random_state=self._random_state) user_ids_tensor = gpu(torch.from_numpy(users), self._use_cuda) item_ids_tensor = gpu(torch.from_numpy(items), self._use_cuda) epoch_loss = 0.0 for (minibatch_num, (batch_user, batch_item)) in enumerate( minibatch(user_ids_tensor, item_ids_tensor, batch_size=self._batch_size)): user_var = Variable(batch_user) item_var = Variable(batch_item) positive_prediction = self._net(user_var, item_var) if self._loss == 'adaptive_hinge': negative_prediction = self._get_multiple_negative_predictions( user_var, n=self._num_negative_samples) else: negative_prediction = self._get_negative_prediction( user_var) self._optimizer.zero_grad() loss = self._loss_func(positive_prediction, negative_prediction) epoch_loss += loss.data[0] loss.backward() self._optimizer.step() epoch_loss /= minibatch_num + 1 if verbose: print('Epoch {}: loss {}'.format(epoch_num, epoch_loss)) if np.isnan(epoch_loss) or epoch_loss == 0.0: raise ValueError( 'Degenerate epoch loss: {}'.format(epoch_loss))
def fit(self, interactions, verbose=False): """ Fit the model. When called repeatedly, model fitting will resume from the point at which training stopped in the previous fit call. Parameters ---------- interactions: :class:`spotlight.interactions.Interactions` The input dataset. verbose: bool Output additional information about current epoch and loss. """ user_ids = interactions.user_ids.astype(np.int64) item_ids = interactions.item_ids.astype(np.int64) if not self._initialized: self._initialize(interactions) self._check_input(user_ids, item_ids) time_step = 0 for epoch_num in range(self._n_iter): users, items = shuffle(user_ids, item_ids, random_state=self._random_state) user_ids_tensor = gpu(torch.from_numpy(users), self._use_cuda) item_ids_tensor = gpu(torch.from_numpy(items), self._use_cuda) epoch_loss = 0.0 interval_loss = 0.0 interval_batches = 0.0 epoch_batches = 0.0 for (minibatch_num, (batch_user, batch_item)) in enumerate( minibatch(user_ids_tensor, item_ids_tensor, batch_size=self._batch_size)): self._net.train(True) positive_prediction = self._net(batch_user, batch_item) if self._loss == 'adaptive_hinge': negative_prediction = self._get_multiple_negative_predictions( batch_user, n=self._num_negative_samples) else: negative_prediction = self._get_negative_prediction( batch_user) self._optimizer.zero_grad() loss = self._loss_func(positive_prediction, negative_prediction) loss_val = loss.item() epoch_loss += loss_val interval_loss += loss_val loss.backward() self._optimizer.step() interval_batches += 1 if time_step % self._log_loss_interval == 0: if self._notify_loss_completion: self._notify_loss_completion( epoch_num, time_step, interval_loss / interval_batches, self._net, self) if time_step % self._log_eval_interval == 0: if self._notify_batch_eval_completion: self._notify_batch_eval_completion( epoch_num, time_step, interval_loss / interval_batches, self._net, self) if time_step % self._log_loss_interval == 0: interval_loss = 0.0 interval_batches = 0.0 time_step += 1 epoch_batches += 1 epoch_loss /= epoch_batches if verbose: print('Epoch {}: loss {}'.format(epoch_num, epoch_loss)) if self._notify_epoch_completion: self._notify_epoch_completion(epoch_num, epoch_loss, self._net, self) if np.isnan(epoch_loss) or epoch_loss == 0.0: raise ValueError( 'Degenerate epoch loss: {}'.format(epoch_loss))
def fit(self, interactions, verbose=False): """ Fit the model. When called repeatedly, model fitting will resume from the point at which training stopped in the previous fit call. Parameters ---------- interactions: :class:`spotlight.interactions.SequenceInteractions` The input sequence dataset. """ sequences = interactions.sequences.astype(np.int64) if not self._initialized: self._initialize(interactions) self._check_input(sequences) for epoch_num in range(self._n_iter): sequences = shuffle(sequences, random_state=self._random_state) sequences_tensor = gpu(torch.from_numpy(sequences), self._use_cuda) epoch_loss = 0.0 for minibatch_num, batch_sequence in enumerate(minibatch(sequences_tensor, batch_size=self._batch_size)): sequence_var = Variable(batch_sequence) user_representation, _ = self._net.user_representation( sequence_var ) positive_prediction = self._net(user_representation, sequence_var) if self._loss == 'adaptive_hinge': negative_prediction = [self._get_negative_prediction(sequence_var.size(), user_representation) for __ in range(5)] else: negative_prediction = self._get_negative_prediction(sequence_var.size(), user_representation) self._optimizer.zero_grad() loss = self._loss_func(positive_prediction, negative_prediction, mask=(sequence_var != PADDING_IDX)) epoch_loss += loss.data[0] loss.backward() self._optimizer.step() epoch_loss /= minibatch_num + 1 if verbose: print('Epoch {}: loss {}'.format(epoch_num, epoch_loss))
def fit(self, interactions, validation_interactions=None, verbose=False): """ Fit the model. When called repeatedly, model fitting will resume from the point at which training stopped in the previous fit call. Parameters ---------- interactions: :class:`spotlight.interactions.Interactions` The input dataset. verbose: bool Output additional information about current epoch and loss. """ train_loss = np.zeros(self._n_iter) val_loss = np.zeros(self._n_iter) user_ids = interactions.user_ids.astype(np.int64) item_ids = interactions.item_ids.astype(np.int64) weights = interactions.weights.astype(np.float32) if not self._initialized: self._initialize(interactions) self._check_input(user_ids, item_ids) for epoch_num in range(self._n_iter): users, items, weights = shuffle(user_ids, item_ids, weights, random_state=self._random_state) user_ids_tensor = gpu(torch.from_numpy(users), self._use_cuda) item_ids_tensor = gpu(torch.from_numpy(items), self._use_cuda) weights_tensor = gpu(torch.from_numpy(weights), self._use_cuda) epoch_loss = 0.0 for (minibatch_num, (batch_user, batch_item, batch_weight)) in enumerate( minibatch(user_ids_tensor, item_ids_tensor, weights_tensor, batch_size=self._batch_size)): positive_prediction = self._net(batch_user, batch_item) if self._loss == 'adaptive_hinge': negative_prediction = self._get_multiple_negative_predictions( batch_user, n=self._num_negative_samples) else: negative_prediction = self._get_negative_prediction( batch_user) self._optimizer.zero_grad() loss = self._loss_func(positive_prediction, negative_prediction, weights=batch_weight) epoch_loss += loss.item() loss.backward() self._optimizer.step() train_loss[epoch_num] = epoch_loss / (minibatch_num + 1) if validation_interactions is not None: val_loss[epoch_num] = self._get_validation_loss( validation_interactions) if verbose and validation_interactions is not None: print('Epoch {}: loss {} \t val-loss {}'.format( epoch_num, train_loss[epoch_num], val_loss[epoch_num])) else: print('Epoch {}: loss {}'.format(epoch_num, train_loss[epoch_num])) if np.isnan(epoch_loss) or epoch_loss == 0.0: raise ValueError( 'Degenerate epoch loss: {}'.format(epoch_loss)) return train_loss, val_loss
def fit(self, interactions, verbose=False): """ Fit the model. Parameters ---------- interactions: :class:`spotlight.interactions.Interactions` The input dataset. """ user_ids = interactions.user_ids.astype(np.int64) item_ids = interactions.item_ids.astype(np.int64) (self._num_users, self._num_items) = (interactions.num_users, interactions.num_items) self._net = gpu( BilinearNet(self._num_users, self._num_items, self._embedding_dim, sparse=self._sparse), self._use_cuda) if self._optimizer is None: self._optimizer = optim.Adam(self._net.parameters(), weight_decay=self._l2, lr=self._learning_rate) else: self._optimizer = self._optimizer_func(self._net.parameters()) if self._loss == 'pointwise': loss_fnc = pointwise_loss elif self._loss == 'bpr': loss_fnc = bpr_loss elif self._loss == 'hinge': loss_fnc = hinge_loss else: loss_fnc = adaptive_hinge_loss for epoch_num in range(self._n_iter): users, items = shuffle(user_ids, item_ids, random_state=self._random_state) user_ids_tensor = gpu(torch.from_numpy(users), self._use_cuda) item_ids_tensor = gpu(torch.from_numpy(items), self._use_cuda) epoch_loss = 0.0 for (minibatch_num, (batch_user, batch_item)) in enumerate( minibatch(user_ids_tensor, item_ids_tensor, batch_size=self._batch_size)): user_var = Variable(batch_user) item_var = Variable(batch_item) positive_prediction = self._net(user_var, item_var) if self._loss == 'adaptive_hinge': negative_prediction = [ self._get_negative_prediction(user_var) for _ in range(5) ] else: negative_prediction = self._get_negative_prediction( user_var) self._optimizer.zero_grad() loss = loss_fnc(positive_prediction, negative_prediction) epoch_loss += loss.data[0] loss.backward() self._optimizer.step() epoch_loss /= minibatch_num + 1 if verbose: print('Epoch {}: loss {}'.format(epoch_num, epoch_loss))
def fit(self, interactions, verbose=False): """ Fit the model. Parameters ---------- interactions: :class:`spotlight.interactions.Interactions` The input dataset. """ user_ids = interactions.user_ids.astype(np.int64) item_ids = interactions.item_ids.astype(np.int64) (self._num_users, self._num_items) = (interactions.num_users, interactions.num_items) self._net = gpu( BilinearNet(self._num_users, self._num_items, self._embedding_dim, sparse=self._sparse), self._use_cuda ) if self._optimizer is None: self._optimizer = optim.Adam( self._net.parameters(), weight_decay=self._l2, lr=self._learning_rate ) else: self._optimizer = self._optimizer_func(self._net.parameters()) if self._loss == 'pointwise': loss_fnc = pointwise_loss elif self._loss == 'bpr': loss_fnc = bpr_loss elif self._loss == 'hinge': loss_fnc = hinge_loss else: loss_fnc = adaptive_hinge_loss for epoch_num in range(self._n_iter): users, items = shuffle(user_ids, item_ids, random_state=self._random_state) user_ids_tensor = gpu(torch.from_numpy(users), self._use_cuda) item_ids_tensor = gpu(torch.from_numpy(items), self._use_cuda) epoch_loss = 0.0 for (minibatch_num, (batch_user, batch_item)) in enumerate(minibatch(user_ids_tensor, item_ids_tensor, batch_size=self._batch_size)): user_var = Variable(batch_user) item_var = Variable(batch_item) positive_prediction = self._net(user_var, item_var) if self._loss == 'adaptive_hinge': negative_prediction = [self._get_negative_prediction(user_var) for _ in range(5)] else: negative_prediction = self._get_negative_prediction(user_var) self._optimizer.zero_grad() loss = loss_fnc(positive_prediction, negative_prediction) epoch_loss += loss.data[0] loss.backward() self._optimizer.step() epoch_loss /= minibatch_num + 1 if verbose: print('Epoch {}: loss {}'.format(epoch_num, epoch_loss))