[Vidéo] Prototype : On a codé une IA pour recommander des jouets !
Quand on parle d'intelligence artificielle, on pense généralement tout de suite aux LLMs, les grands modèles de langage, composante phare des IA génératives qui sont à la mode en ce moment. Pourtant, on gagne souvent à utiliser des modèles plus petits et modestes, mais spécialisés.
Cette vidéo est l'une des premières à avoir été publiées sur notre chaîne Youtube, où on vous invite à nous suivre !
Expression du besoin
L'idée, c'est de monter un prototype qui permettra d'améliorer MesCadeaux.com en répondant à deux besoins distincts :
- en UX (eXpérience Utilisateur) : recherches classiques sur le site
- en SEO : optimiser le maillage interne automatiquement sur des pages produits sémantiquement proches
On ne peut pas se reposer sur l'historique de navigation de l'utilisateur, puisque ce n'est pas applicable quand Google crawle notre site, et sans avoir à tagger des dizaines de milliers de produits à la main : on veut tirer parti du "programmatic SEO", et ouvrir des pages de manière automatique à partir de catalogues d'affiliation notamment.
L'existant
Système de recommandations/maillage
Avant intervention, on a déjà un système basé sur des requêtes SQL classiques : sur chaque page produit, on a des informations comme sa marque, sa licence, le type de produit, etc. On va chercher en base les produits ayant des caractéristiques communes. On calcule ensuite un score pour chaque produit, directement tiré du nombre de correspondances. Ce score nous permet de garder en priorité les produits les plus proches de celui affiché.
Recherche
Le système actuel permet déjà des recherches "fuzzy" (correction automatique des fautes d'orthographe, etc) via MeiliSearch, qui est un outil open-source (licence MIT) hébergé sur un serveur dédié, et qui s'intègre très bien à Laravel (via Scout).
En switchant sur un système qui utilise de l'intelligence artificielle, on veut garder ce point : notre solution actuelle est totalement compatible RGPD, on n'a aucune sortie de données, qu'elles soient personnelles (navigation) ou pas (produits).
Les limites
Avec ce type de système, sans passer par une étape manuelle de classification, on ne peut ressortir que des produits qui contiennent le terme recherché. Par exemple, un utilisateur qui tape "Gotham" ne verra que des produits dont le nom ou la description contiennent ce terme.
Notre IA devra savoir que Gotham, c'est la ville où vit Batman, et renvoyer des produits associés à ce personnage et à son univers. Pour régler ce problème, il nous faudra des embeddings.
Embeddings
Les embeddings sont une des pierres angulaires du traitement automatisé du langage naturel (TALN) : chaque mot est remplacé par une représentation numérique, qui sera un vecteur de N dimensions (le modèle qu'on a choisi en utilise 700, mais on n'en gardera que 2 pour bien comprendre le principe).
Lors de son entraînement, le modèle va analyser des millions (souvent des milliards...) de contenus, et rapprocher spacialement, via leur vecteur, les mots qui sont liés. Les modèles les plus simples se limiteront aux co-occurrences (les mots qu'on aperçoit souvent ensemble - sur la même page, dans la même phrase). Les modèles plus évolués utiliseront des contextes plus longs, en pondérant les relations via des mécanismes d'attention.
Globalement, pour simplifier à l'extrême, le modèle sera capable de savoir que les mots apparaissent souvent ensemble, et sont donc liés par une relation logique.
Sur ce graph, on a placé :
- en abscisse, la tonalité : est-ce que c'est un sujet "dark" avec une ambiance pesante, ou "light" avec une ambiance légère ?
- en ordonnées, l'alignement : est-ce qu'on parle plutôt d'un gentil ou d'un méchant ?
On voit directement que Superman et Metropolis sont placés ensemble côté lumineux, et que Batman et Gotham sont côté sombre. Les méchants de Batman se retrouvent ensemble en bas à gauche. Si je cherche des produits autour du Pingouin, doit pouvoir proposer aussi des produits autour du Joker ou de Bane, d'autres ennemis emblématiques du Chevalier Noir (ne me cherchez pas sur ce sujet, je suis fan de longue date), plutôt qu'un Spider-man ou un Superman, qui seront moins proches, même si on les retrouvera forcément puisque ce sont aussi des super-héros.
Générer les embeddings
La première étape de création de notre prototype sera donc de générer ces embeddings sur nos produits. Pour cette tâche, on a utilisé MPNET (voir la model card sur HuggingFace), qui est développé par Microsoft, et basé sur Bert (de chez Google), amélioré pour mieux comprendre les phrases et les contextes longs. C'est un modèle gratuit, utilisable en local, qui propose un bon compromis entre précision, vitesse, et utilisation mémoire.
Motoriser les recherches
Une fois qu'on a réalisé les embeddings, il faut encore trouver le moteur qui répondra aux recherches. On a choisi d'utiliser FAISS (Facebook AI Similarity Search), qui est aussi un moteur OpenSource, développé par Facebook sous licence MIT.
Cette licence, qui est l'une des plus permissives, nous permet d'utiliser, de modifier, et elle nous permettrait même de distribuer commercialement la technologie : aucun souci juridique à l'horizon.
FAISS a été choisi pour sa licence et pour sa capacité à répondre, en quelques millisecondes, même avec des milliers de référence, sans faire appel à un GPU (une carte graphique) : en étant capable de tourner sur un CPU (processeur) uniquement, il est plus facile à héberger à moindre coût.
Intégrer au site
À partir de là, on a deux solutions :
- publier une API qui nous permettra de répondre en temps réel,
- et/ou faire tourner la demande de recommandations sur chaque produit, et stocker les relations en base, pour accélérer l'affichage ultérieur (et il faudra faire tourner ce script chaque fois qu'on ajoutera de nouveaux produits pour les prendre en compte)
API
L'API sera créée en Python via FastAPI, qui est une librairie/framework qui supporte les formats OpenAPI et Swagger, bien connus des développeurs. Avec cette solution, on pourra, en quelques lignes de code, définir nos endpoints et leurs paramètres, et partager une documentation technique complète et à jour.
Récapitulatif
- On utilise MySQL pour récupérer les produits en live (on aurait pu passer par un export en CSV pour décharger le serveur)
- On nettoie les données pour préparer et normaliser le dataframe
- On charge le modèle qui gère les embeddings
- On injecte ces embeddings dans FAISS
On a ensuite deux manières d'utiliser l'outil :
En ligne de commande (CLI)
Via un script Python, on charge le moteur FAISS. On demande à l'utilisateur d'entrer sa recherche, qu'on transforme en embedding, et on demande à FAISS de nous trouver les produits les plus proches.
Cette version CLI nous permet, pendant le dev, de vérifier si le système renvoie des réponses pertinentes.
Par API
Une fois qu'on est satisfait des réponses, on publie l'API, et on envoie le lien vers la doc au développeur qui sera chargé d'utiliser la solution.
FastAPI tournera comme un serveur, en attendant qu'on s'y connecte, et en gardant le modèle en mémoire, déjà chargé et prêt à répondre, pour qu'on n'aie pas plusieurs secondes de latence quand on reçoit une requête, ce qui ne serait pas acceptable pour nos visiteurs.
L'API renvoie une réponse en JSON, en environ 60 millisecondes pendant nos tests, ce qui sera à surveiller lors du passage en prod, notamment pour prendre en compte la future latence réseau (entre le serveur de production du site et celui de FAISS) ; on devrait se retrouver avec une latence totale de l'ordre de 300 ms, ce qui nous semble acceptable pour une page de recherche générée dynamiquement.
Aller plus loin ?
On a ajouté un système qui sur-pondère les licences : quelqu'un qui cherche "lego star wars" sera sûrement plus intéressé par d'autres produits Star Wars que par d'autres produits Lego ; c'est une décision empirique tirée de nos données métier (puisqu'on tracke les résultats sur le système existant).
On pourrait aussi embarquer les prix pour proposer des produits dans certaines gammes en fonction de la requête, ou importer le nombre de clics reçus par chaque produit pour affiner notre modèle.
Affiner le catalogue
En enregistrant les requêtes des utilisateurs et la distance médiane avec les réponses, on pourra afficher les requêtes pour lesquelles les résultats étaient les moins pertinents : ça nous aidera à affiner notre catalogue en ajoutant des produits qui répondent à des gaps dans la demande réelle.