In this example, we'll build a quick explicit feedback recommender system: that is, a model that takes into account explicit feedback signals (like ratings) to recommend new content.
We'll use an approach first made popular by the Netflix prize contest: matrix factorization.
The basic idea is very simple:
[0.3, -1.2, 0.5] and an item by [1.0, -0.3, -0.6].Spotlight fits models such as these using stochastic gradient descent. The procedure goes roughly as follows:
We start with importing a famous dataset, the Movielens 100k dataset. It contains 100,000 ratings (between 1 and 5) given to 1683 movies by 944 users:
In [9]:
import numpy as np
from spotlight.datasets.movielens import get_movielens_dataset
dataset = get_movielens_dataset(variant='100K')
print(dataset)
The dataset object is an instance of an Interactions class, a fairly light-weight wrapper that Spotlight users to hold the arrays that contain information about an interactions dataset (such as user and item ids, ratings, and timestamps).
We can feed our dataset to the ExplicitFactorizationModel class - and sklearn-like object that allows us to train and evaluate the explicit factorization models.
Internally, the model uses the BilinearNet class to represents users and items. It's composed of a 4 embedding layers:
(num_users x latent_dim) embedding layer to represent users,(num_items x latent_dim) embedding layer to represent items,(num_users x 1) embedding layer to represent user biases, and(num_items x 1) embedding layer to represent item biases.Together, these give us the predictions. Their accuracy is evaluated using one of the Spotlight losses. In this case, we'll use the regression loss, which is simply the squared difference between the true and the predicted rating.
In [13]:
import torch
from spotlight.factorization.explicit import ExplicitFactorizationModel
model = ExplicitFactorizationModel(loss='regression',
embedding_dim=128, # latent dimensionality
n_iter=10, # number of epochs of training
batch_size=1024, # minibatch size
l2=1e-9, # strength of L2 regularization
learning_rate=1e-3,
use_cuda=torch.cuda.is_available())
In order to fit and evaluate the model, we need to split it into a train and a test set:
In [14]:
from spotlight.cross_validation import random_train_test_split
train, test = random_train_test_split(dataset, random_state=np.random.RandomState(42))
print('Split into \n {} and \n {}.'.format(train, test))
With the data ready, we can go ahead and fit the model. This should take less than a minute on the CPU, and we should see the loss decreasing as the model is learning better and better representations for the user and items in our dataset.
In [15]:
model.fit(train, verbose=True)
Now that the model is estimated, how good are its predictions?
In [16]:
from spotlight.evaluation import rmse_score
train_rmse = rmse_score(model, train)
test_rmse = rmse_score(model, test)
print('Train RMSE {:.3f}, test RMSE {:.3f}'.format(train_rmse, test_rmse))
This is a fairly simple model, and can be extended by adding side-information, adding more non-linear layers, and so on.
However, before plunging into such extensions, it is worth knowing that models using explicit ratings have fallen out of favour both in academia and in industry. It is now widely accepted that what people choose to interact with is more meaningful than how they rate the interactions they have.
These scenarios are called implicit feedback settings. If you're interested in building these models, have a look at Spotlight's implicit factorization models, as well as the implicit sequence models which aim to explicitly model the sequential nature of interaction data.