Building an Incremental Recommender System | by Dimitris Poulopoulos | Towards Data Science

Learning Rate is a newsletter for those who are curious about the world of AI and MLOps. You’ll hear from me every Friday with updates and thoughts on the latest AI news and articles. Subscribe here!
Part II of this story is now published via Towards Data Science below.
towardsdatascience.com
Although I will try to keep the math jargon to a minimum, this story expects that the reader is familiar with concepts like user-item interaction matrix, matrix factorization, embedding spaces, as well as basic machine learning terminology. This story is not an introduction to recommender systems. It is an introduction to their incremental variant. In any case, the story is addressed to what you might call a beginner in the world of machine learning and recommender systems.
Starting a machine learning project, data scientists gather data, process them, train a model and deploy it to production. When the model’s performance starts to deteriorate, data scientists usually repeat the cycle from scratch. Only this time, they have new data examples to bring the model up to date and boost its performance. However, this is usually a counter-productive practice as well as an inefficient one, especially for business domains where making decisions on current data is crucial.
Now, enter the world of recommender systems, where user preferences frequently change according to seasons, budgets, fashion trends and more. Moreover, arriving customers and new inventory create the so-called cold-start problem, where the system does not have enough information to match consumers to products or services. A recommender system should ideally adapt to these changes as they happen, modifying its model to always speak for the current status, while requiring a single pass through the data. This is the idea of incremental learning.
This story is the first part of a series where we explore how to adapt the idea of incremental learning to recommender systems, with a hands-on approach. In the first part, we reproduce the results presented in the “Fast incremental matrix factorization for recommendation with positive-only feedback” publication by João Vinagre et al., using a python library built on Pytorch, called CF Step. Next, we will aim higher by applying several tricks.
The algorithm we are going to implement works with implicit, positive-only feedback. Let us try to demystify these two keywords. Implicit feedback means that users have never expressed a direct opinion on the items they have interacted with, such as a rating. An example of implicit feedback would be something like how many times a customer has bought a product, or how many minutes they have spent watching a movie. The more a customer buys a product or uses a service, the more confident we can be that this is a sign of preference. Positive-only feedback is a term that usually goes together with implicit feedback. This is because, in the case of implicit feedback, we rarely have a clue about what constitutes a negative interaction. A user not interacting with an item does not mean much. Image the case of a consumer in a supermarket. If a customer has not bought a specific product yet, we cannot be sure why.
Returning to our implementation, positive-only means that the user-item interaction matrix R contains only Boolean values, where true values indicate preference and false values are treated as missing. This assumption has two main implications; the sparsity of R is maintained, as only positive feedback is used during training, and false values, for any user-item interaction, are perfectly valid recommendation candidates.
Let us now take a closer look at the Incremental Stochastic Gradient Descent (ISGD) algorithm presented in the paper.
The data we have are tuples or user-item interactions. Remember that these are all positive interactions. The input to the algorithm are three numbers; feat, which is the dimensions of the user or item embedding space, λ, which is the regularization coefficient, and η, the learning rate. The output of the algorithm is the two embedding matrices: A for users and B for items. The dimensions of these matrices are number_of_users x feat for A and number_of_items x feat for B. Then we have several distinct steps:
Check if the active user is known. If not, create a new user with random latent features, drawn from a normal distribution with mean 0 and standard deviation of 1. Do the same for the active item.
Calculate the loss. Because we only have to deal with positive feedback, the target is always 1. Thus, we only have to subtract our prediction from 1.
Update the active user’s latent features (parameters in the user embedding matrix) using a common update rule. Do the same for the active item.
Go to the next data point. This way, we can deal with an arbitrary length of streaming data.
For this implementation, we will use the CF Step Python library and the well-know Movielens dataset. CF Step is an open-source library, written in python and build on Pytorch, that enables fast implementation of incremental learning recommender systems. The library is a by-product of the European research project CloudDBAppliance. You can easily install the library by running the command below:
pip install cf-step
Next, download the movielens 1m dataset and extract the ratings.dat file in a convenient location, for example, the tmp folder in Linux. For this implementation, we only need this file. The rest of the files (users.dat and movies.dat) contain metadata on users and movies. We will use pandas to load the files in memory:
As you can see, we transform the user and movie IDs to categories so we can extract the category code. Now, we do not have to create separate vocabularies for the embedding matrix generation. We only have to work with the user and movie codes instead of IDs. Finally, we always have a connection between codes and IDs through this dataframe to find the original user and movie. Now, let us see the number of unique users and movies we are working with.
If we print those numbers, we will see that we are working with 6040 unique users and 3706 unique movies. Next, we will sort the data by timestamp to simulate streaming events.
As we have discussed, the algorithm supports only positive feedback. Thus, we will consider a rating of 5 as positive feedback and discard any other. We want to identify likes with 1 and dislikes with 0 and create a new column called preference to keep them. Then, we can filter only for preference == 1.
Following, let us initialize our model. For this, we need a model architecture, an objective function (i.e. loss function) and an optimizer. We will use the SimpleCF network for the model architecture, a built-in neural network architecture offered by CF Step. For the objective function, we will use a simple lambda function that takes a prediction and a target and subtracts the prediction from the target. In our case, the target is always 1. For the optimizer, we will use Pytorch’s SGD implementation. The number of factors we choose is 128 and the learning rate is 0.06. Now we are ready to initialize the Step model.
The evaluation methodology is as follows:
Bootstrap the model by training it on the first 20% of the data.
Simulate a data stream and evaluate the model’s performance using recall@k as a metric.
If the user is known, make a prediction and calculate recall@kfor this prediction. Then, incrementally train the algorithm incrementally, using this user-item interaction.
If the user is not known, just train the algorithm incrementally, using this user-item interaction.
To this end, let us take the first 20% of the data, create the data loaders and batch fit the model.
Then, we get the remaining data and create a different data set.
Finally, simulate the stream and evaluate the model using recall@10. This step takes five to six minutes on a GPU.
We can visualize the results of our training using the code below. For this, we will use a moving average of a 5k sliding window, like they do in the publication. We can see that the plot follows the results presented in the paper for the movielens dataset. To save the model, use the model.save() built-in method and pass a valid path.
In this story, we presented the importance of incremental learning in recommender systems and reproduced the results of the “Fast incremental matrix factorization for recommendation with positive-only feedback” publication by João Vinagre et al. We introduced the CF Step python library, an open-source library, that enables fast implementation of incremental learning recommender systems. On the next chapter, we will take this a bit further and try to increase the accuracy of the algorithm.
My name is Dimitris Poulopoulos and I’m a machine learning researcher at BigDataStack and PhD(c) at the University of Piraeus, Greece. I have worked on designing and implementing AI and software solutions for major clients such as the European Commission, Eurostat, IMF, the European Central Bank, OECD, and IKEA. If you are interested in reading more posts about Machine Learning, Deep Learning and Data Science, follow me on Medium, LinkedIn or @james2pl on twitter.