Building real-time recommendations for e-commerce giant
Sephora China is one of the Asian giants in the cosmetics e-commerce industry. Together, we have built an online recommendation engine to boost an already existing offline system by exploiting the latest customer's behavior in the mobile application.
Need for real-time recommendations
Just like a real salesperson would talk to the customers to understand their needs, the recommendation system listens to them by looking at the actions users make. All those actions are then transformed into data that is understandable by the machine learning model which chooses the most appropriate products for each customer. The comparison to shopping in physical stores with the help of a merchant becomes even more relevant when it comes to real-time recommender systems as they are capable of answering to customer’s behavior within a fraction of second, just like the salesperson is.
The solution we developed consists of a couple of interacting parts, each of which is wrapped in a docker container for trouble-free deployment. The first part is the web application that awaits incoming requests (e.g. information about a new event or inserting item attributes to the database) and triggers Python code to handle that request. The second part is the Redis database that we use to store and quickly retrieve data. We will discuss those components in more detail later in this article.
Machine learning behind the scenes
Let's now dive into the details of the recommendation process. When preparing the recommendation for a given user we begin by gathering candidate products from different sources - e.g. trending items and offline recommendations. The idea behind using offline recommendations is that they can be computed with any sophisticated, and thus time-consuming, algorithm and reevaluated e.g. once a day. Even though they do not access the latest user’s behavior, they still can yield relevant product suggestions.
After offline recommendations have been prepared we generate online ones with an algorithm that takes into account the latest user’s actions. This step greatly improves the former recommendations as it adds products that the user is possibly searching right now.
Having gathered candidate products we come across the following problem. We cannot recommend everything, because there are simply too many choices. We could try to pick one of the products from each recommendation source, but this does not seem to be flexible enough - if a user keeps searching for some niche product, there is probably no point in presenting them the trending ones. Fortunately, there is a better way. We apply a machine learning algorithm to rank all of those products from the most to the least relevant. The algorithm incorporates all information that we gather using the accumulators. In particular, it can use information about the user (including demographics, and historical and recent behavior) and about the products and thus is able to decide whether the particular product will possibly match the user’s expectations or not. Then, having all items ranked, we can just select e.g. top 3 most relevant and present them to the user. This technique is very powerful as not only does it solve the problem of selecting the best ones, but also allows for exploiting almost any data that can be useful for item ranking.
Events as quanta of data
The whole system is focused around events - transforming events into meaningful data and also responding to events with the best possible recommendations. To implement the simple real-life nature of the event (as something that happens at some time), we introduced a special data structure that allows us to encode all necessary information about what happened:
@dataclass class Event: user_id: str item_id: str event_type: str event_attr: Dict timestamp: int session_id: str
Here, we utilized Python 3.7’s data classes, which greatly simplify the definition of data structures like that. As the code shows, there is some information associated with that event, in particular the ids of the user and item that were involved in that event - this is critical from the standpoint of personalized recommendations. Also, event type (e.g. adding product to cart) and other event attributes (e.g. the information if the user is on their home page or maybe on the cart page) should be supplied. The exact time of the event can be extracted from the timestamp field, which keeps the number of milliseconds from 1.01.1970. Finally, an id of the current session is kept. Such a simple structure is capable of carrying all event data that is needed for machine learning.
Built with the client in mind
While working on this project we've built the framework which:
- is easy to work with
- is fast
- captures real-time behavior
One of the features that can state the easiness of use, is that we don't need to call different endpoints for different event types. Our API can take any event from the client and stream it into the pipeline of accumulators that are updated if the specific criteria are met. This is achieved with /new_event post method that accepts event data in json format. We have also implemented a couple of useful endpoints, which make it easy to integrate the solution with client's application.
Redis as our go-to database
To make sure our framework is fast, we selected the in-memory key-value database Redis, which can support a lot of types of data structures such as strings, hashes, lists, sets, etc.. The fact that it uses RAM allowed us to write and retrieve the information much faster than in case of traditional hard disk based stores. The key-value nature of Redis also saves a lot of time, as the necessary values can be accessed much faster than in relational databases, in which they should be searched in potentially large tables. The search is done in constant time as opposed to linear or logarithmic depending on the indices' existence.
Interestingly, the Redis feature that proved to be extremely versatile was a pipeline. The pipeline is a command that sends many Redis requests at once. For example, imagine you want to update ten independent accumulators. With the standard workflow, you would need to call Redis ten times, but with the pipeline, you call it only once. This greatly increases the performance of the application.
Additionally, it is supported in Python and requires no SQL knowledge to approach it. It's highly intuitive even for the novices. Last but not least, it has the capability to set an expiration time for any key. For online behavior with dozens of millions of users per day, it is a huge benefit, as it allows to manage existing data with ease.
Accumulators as building blocks
So the question is what is an accumulator? It's an object that takes the events and processes them into meaningful features for the ranking model. The framework we developed has two types of accumulators: general-purpose and meta accumulators.
General-purpose accumulators can gather item information (e.g. CTR or popularity), user information (e.g. device type) or user-item interaction (e.g. a number of times a user clicked on the particular item or user’s favorite product types). Those accumulators come in all shapes and sizes to capture the specific aspects of online behavior.
Meta accumulators act like a model, which in some way resembles the ensemble learning algorithm. Online item-based collaborative filtering is one of those meta accumulators. It gathers the information of user-item interaction and generates recommendations with the scores.
In our meta accumulator stack, we also have contextual multi-armed bandit, which increases the exploration of the proposed items to increase the diversity and reduce the risk of proposing only the most popular items.
Above and Beyond
Our API was built with broad use cases in mind. We all know that recommendations are an essential part of any online business. It can be easily converted to show appropriate job offers for the applicants in the HR industry, to recommend interesting courses in online education, to propose games players might enjoy. Also, it can be used in video suggestions, the online publishing industry, and, of course, any e-commerce that is looking for real-time recommendations.
If you are interested to take your recommendations into another level, just drop us a message at firstname.lastname@example.org