Building and Testing Recommender Systems With Surprise, Step-By-Step | by Susan Li | Towards Data Science

Recommender systems are one of the most common used and easily understandable applications of data science. Lots of work has been done on this topic, the interest and demand in this area remains very high because of the rapid growth of the internet and the information overload problem. It has become necessary for online businesses to help users to deal with information overload and provide personalized recommendations, content and services to them.
Two of the most popular ways to approach recommender systems are collaborative filtering and content-based recommendations. In this post, we will focus on the collaborative filtering approach, that is: the user is recommended items that people with similar tastes and preferences liked in the past. In another word, this method predicts unknown ratings by using the similarities between users.
We’ll be working with the Book-Crossing, a book ratings data set to develop recommendation system algorithms, with the Surprise library, which was built by Nicolas Hug. Let’s get started!
The Book-Crossing data comprises three tables, we will use two of them: The users table and the book ratings table.
user = pd.read_csv('BX-Users.csv', sep=';', error_bad_lines=False, encoding="latin-1")user.columns = ['userID', 'Location', 'Age']rating = pd.read_csv('BX-Book-Ratings.csv', sep=';', error_bad_lines=False, encoding="latin-1")rating.columns = ['userID', 'ISBN', 'bookRating']df = pd.merge(user, rating, on='userID', how='inner')df.drop(['Location', 'Age'], axis=1, inplace=True)df.head()
We can see that over 62% of all ratings in the data are 0, and very few ratings are 1 or 2, or 3, low rating books mean they are generally really bad.
df.groupby('ISBN')['bookRating'].count().reset_index().sort_values('bookRating', ascending=False)[:10]
Most of the books in the data received less than 5 ratings, and very few books have many ratings, although the most rated book has received 2,502 ratings.
df.groupby('userID')['bookRating'].count().reset_index().sort_values('bookRating', ascending=False)[:10]
Most of the users in the data gave less than 5 ratings, and not many users gave many ratings, although the most productive user have given 13,602 ratings.
I’m sure you have noticed that the above two plots share the same distribution. The number of ratings per book and the number of ratings per user decay exponentially.
To reduce the dimensionality of the data set, and avoid running into “memory error”, we will filter out rarely rated books and rarely rating users.
To load a data set from the above pandas data frame, we will use the load_from_df() method, we will also need a Reader object, and the rating_scale parameter must be specified. The data frame must have three columns, corresponding to the user ids, the item ids, and the ratings in this order. Each row thus corresponds to a given rating.
reader = Reader(rating_scale=(0, 9))data = Dataset.load_from_df(df_new[['userID', 'ISBN', 'bookRating']], reader)
With the Surprise library, we will benchmark the following algorithms:
NormalPredictor
NormalPredictor algorithm predicts a random rating based on the distribution of the training set, which is assumed to be normal. This is one of the most basic algorithms that do not do much work.
BaselineOnly
BaselineOnly algorithm predicts the baseline estimate for given user and item.
KNNBasic
KNNBasic is a basic collaborative filtering algorithm.
KNNWithMeans
KNNWithMeans is basic collaborative filtering algorithm, taking into account the mean ratings of each user.
KNNWithZScore
KNNWithZScore is a basic collaborative filtering algorithm, taking into account the z-score normalization of each user.
KNNBaseline
KNNBaseline is a basic collaborative filtering algorithm taking into account a baseline rating.
SVD
SVD algorithm is equivalent to Probabilistic Matrix Factorization
SVDpp
The SVDpp algorithm is an extension of SVD that takes into account implicit ratings.
NMF
NMF is a collaborative filtering algorithm based on Non-negative Matrix Factorization. It is very similar with SVD.
SlopeOne is a straightforward implementation of the SlopeOne algorithm.
Coclustering is a collaborative filtering algorithm based on co-clustering.
We use “rmse” as our accuracy metric for the predictions.
BaselineOnly algorithm gave us the best rmse, therefore, we will train and predict with BaselineOnly and use Alternating Least Squares (ALS).
print('Using ALS')bsl_options = {'method': 'als',               'n_epochs': 5,               'reg_u': 12,               'reg_i': 5               }algo = BaselineOnly(bsl_options=bsl_options)cross_validate(algo, data, measures=['RMSE'], cv=3, verbose=False)
We use the train_test_split() to sample a trainset and a testset with given sizes, and use the accuracy metric of rmse. We’ll then use the fit() method which will train the algorithm on the trainset, and the test() method which will return the predictions made from the testset.
trainset, testset = train_test_split(data, test_size=0.25)algo = BaselineOnly(bsl_options=bsl_options)predictions = algo.fit(trainset).test(testset)accuracy.rmse(predictions)
To inspect our predictions in details, we are going to build a pandas data frame with all the predictions. The following code were largely taken from this notebook.
Best Predictions:
The above are the best predictions, and they are not lucky guesses. Because Ui is anywhere between 25 to 146, they are not really small, meaning that significant number of users have rated the target book.
Worst predictions:
The worst predictions look pretty surprise. Let’s look in more details of the last one ISBN “055358264X”. The book was rated by 47 users, user “26544” rated 10, our BaselineOnly algorithm predicts this user would rate 0.
import matplotlib.pyplot as plt%matplotlib notebookdf_new.loc[df_new['ISBN'] == '055358264X']['bookRating'].hist()plt.xlabel('rating')plt.ylabel('Number of ratings')plt.title('Number of ratings book ISBN 055358264X has received')plt.show();
It turns out, most of the ratings this book received was 0, in another word, most of the users in the data rated this book 0, only very few users rated 10. Same with the other predictions in “worst predictions” list. It seems that for each prediction, the users are some kind of outsiders.
That was it! I hope you enjoyed the recommendation (or rather, a rating prediction) journey with Surprise. Jupyter notebook can be found on Github. Happy Holidays!
Reference: Surprise’ documentation