REngine : un moteur de recommandation sur graphe
Moteur de recommandation qui passe les données d'une boutique Sylius / MySQL dans un graphe Neo4j, calcule affinités et similarités entre clients, et sert les suggestions via FastAPI.
- Contexte
- Projet perso, toujours en cours : recommander des produits à partir de l'historique d'achat d'une boutique Sylius / MySQL.
- Mon rôle
- Tout le projet : ETL MySQL vers Neo4j, calcul des affinités et des similarités, API de recommandation.
- Résultat
- Un prototype qui répond en 19 ms en moyenne en local, grâce aux liens d'affinité pré-calculés dans le graphe.
L’idée
Les données d’une boutique e-commerce sont relationnelles : des clients, des produits, des commandes. Pour recommander, la question intéressante est justement la relation : « les clients qui ressemblent à celui-ci, qu’ont-ils acheté d’autre ? ». En SQL, ça devient vite une suite de jointures. Dans un graphe, c’est un parcours.
REngine passe donc les données d’une boutique Sylius (MySQL) dans Neo4j, et calcule les recommandations dans le graphe.
Le pipeline
Quatre étapes, chacune dans son service :
- Synchronisation : migration des clients, produits et commandes de MySQL vers Neo4j, en filtrant le bruit (voir plus bas).
- Affinités : chaque client est relié aux produits qui l’intéressent (
INTERESTED_IN). - Similarités : les clients aux profils proches sont reliés entre eux (
SIMILAR_TO). - API : les recommandations sont servies en REST par FastAPI.
Le code est découpé en migrators, services et repositories, ce qui permet de faire évoluer une étape sans toucher aux autres.
Le problème : les produits que tout le monde achète
Aux premiers tests, des articles de base (sacs réutilisables, produits du quotidien) reliaient presque tous les clients entre eux. Résultat : des similarités qui ne voulaient rien dire.
La solution : un service d’analyse repère le top 1 % des ventes, leur pose un label CommonProduct et les exclut du calcul des similarités. Les produits offerts ou promotionnels sont aussi écartés, pour ne garder que de vrais choix d’achat.
Calculer la proximité entre clients
Deux clients sont candidats s’ils partagent au moins trois produits. Un premier calcul de similarité se fait en mémoire avec Pandas ; pour aller plus loin, j’utilise l’extension Neo4j Graph Data Science (GDS), qui écrit des relations SIMILAR_TO pondérées directement dans le graphe.
Les suggestions s’appuient ensuite sur le framework GraphAware Recommendation Engine, et sur APOC pour l’analyse de panier.
Le principe : on regarde ce que les clients les plus proches ont acheté, et on le propose.
Éviter les doublons
Une recommandation utile n’est pas une variante du produit déjà vu. Si un client consulte un produit en boîte de 30 gélules, lui proposer la boîte de 60 n’apporte rien : le moteur écarte ces variantes pour proposer autre chose.
Performance
Les liens d’affinité et de similarité sont pré-calculés. Une requête de recommandation n’a donc qu’à parcourir le graphe : 19 ms en moyenne, en local.
Ce que j’en retiens
- La forme des données compte autant que l’algorithme : passer des tables aux relations a rendu le problème beaucoup plus simple.
- Cypher remplace beaucoup de code procédural par une seule requête de graphe.
- Des outils spécialisés (GDS, GraphAware) évitent de réimplémenter des algorithmes de similarité à la main.
La suite
C’est un prototype que je continue de mon côté. La prochaine étape est de recalculer affinités et similarités périodiquement, pour intégrer les nouveaux comportements d’achat au fil de l’eau.