Le Blog Amazon Web Services

Foxintelligence crée un moteur d’inférences Machine Learning avec AWS Lambda et Amazon SQS

Cet article a été rédigé par Lucas Fauchille, Lead DevOps Engineer chez Foxintelligence, Barthélémy Pavy, Senior Machine Learning Engineer chez Foxintelligence, et Ihsen Ouerghi, Solutions Architect chez AWS France.

Foxintelligence est une société de Consumer Intelligence dédiée à l’e-commerce. Elle se base sur un panel de consommateurs e-commerce en Europe, panel qu’elle a créé en développant des applications mobiles destinées au grand public. La plus connue, Cleanfox, compte 4 millions d’utilisateurs à travers l’Europe et offre la possibilité de se libérer des newsletters superflues. Grâce à cette application, Foxintelligence récupère les données transactionnelles, les anonymise et les agrège, s’abstenant rigoureusement de vendre ou transférer des données à des fins publicitaires, de profilage ou de réidentification.

Cette démarche permet aux différents leaders de l’e-commerce de pouvoir prendre des décisions basées sur de la donnée concrète, et ainsi repérer des opportunités de croissance, faire des benchmarks de performance, réaliser une stratégie marketing de pricing, etc. Aujourd’hui, les clients de Foxintelligence sont des retailers, des Marketplace, des marques, des fonds d’investissement et des sociétés de conseil.

Foxintelligence a été acheté fin 2021 par NielsenIQ et s’est ouverte à différents pays. Cela a donc ajouté beaucoup de volume à traiter dans tout le pipeline, notamment pour la catégorisation des données de transaction.

Challenge

Le grand nombre d’e-mails de transaction offre à Foxintelligence des opportunités d’analyse inédites dans le secteur du commerce en ligne. Ces e-mails contiennent des détails sur les produits tels que leur nom, leur description et leur prix, mais ils ne précisent pas leur catégorie. Cependant, la catégorisation est essentielle pour regrouper des produits similaires et fournir des analyses détaillées aux clients. Parmi les défis liés à l’enrichissement de la donnée, la catégorisation de produits est l’un des plus importants. La classification suit une taxonomie hiérarchique créée par Foxintelligence afin de qualifier les produits avec plus ou moins de granularité. Voici un extrait des catégories présentes dans la partie High-Tech :

Pour accompagner la croissance de Foxintelligence (qui a doublé le nombre de transactions quotidiennement traitées entre 2022 et 2023), une solution de classification basée sur le Machine Learning a été développée et améliorée depuis 2018.
Initialement, la catégorisation était réalisée avec un seul modèle prenant en entrée les informations de transaction (notamment nom, marchand et marque) et retournant en sortie les catégories.
Ensuite, pour une meilleure précision, le processus a évolué avec l’utilisation de plusieurs modèles, c’est semblable à un arbre de décision où chaque nœud de l’arbre est un modèle de classification retournant en sortie une catégorie. Cela permet d’apporter des améliorations ciblées sur des catégories spécifiques sans affecter les autres, tout en facilitant le suivi des évolutions de la taxonomie, notamment l’ajout, la suppression ou le déplacement de catégories.
La dernière évolution de cette solution a consisté à utiliser des modèles XLM-Roberta pour chaque nœud de l’arbre de catégorisation (en remplacement d’un modèle de type LSTM).

Ci-dessous un exemple visuel de l’arbre représentant la taxonomie :

Bien qu’optimale en termes de précision, cette approche induit beaucoup de complexité pour le déploiement de l’arbre de catégorisation : pour s’aligner à la taxonomie, l’arbre a besoin de 360 modèles, étalés sur 4 couches à ce jour. De plus, chaque modèle pèse plusieurs GB (~2GB) et le nombre de transactions est très variable, donc l’approche précédente consistant à exposer les modèles sur des instances EC2 provisionné en avance n’était plus viable, notamment en termes de couts et performances, et une nouvelle architecture a dû être pensée pour répondre à ces enjeux.

Précisément, l’architecture d’inférence doit répondre aux exigences suivantes :

  • Scalabilité : le nombre de transactions à traiter est fortement variable à la fois en distribution et en volume (passages fréquents de 0 à plusieurs milliers de transaction par secondes en l’espace de quelques minutes). Il faut pouvoir s’adapter à ces variations,
  • Coûts : pour rendre la solution viable, les couts par transactions doivent êtres minimisés et proportionnels aux volumes de transactions traités,
  • Performance : bien qu’il n’y ait pas de contrainte de temps réel, il faut être capable de traiter rapidement les nouvelles transactions pour éviter de créer un goulot d’étranglement,
  • Simplicité opérationnelle : Foxintelligence veut rester focalisée sur son métier et limiter au maximum les tâches opérationnelles pour déployer et maintenir la solution,
  • Agilité : les modèles et la taxonomie évoluent en continu, il est nécessaire que les changements puissent être introduits rapidement et en limitant les impacts.

Solution

Le moteur de calcul choisi pour déployer chaque modèle est AWS Lambda, c’est ainsi plus de 360 fonctions Lambda différentes qui sont déployées afin de traiter les transactions. En plus du moteur de calcul, il faut router (orchestrer) les messages à travers les différents modèles, une fonction AWS Lambda est également utilisée pour cela. Enfin, chacune de ces fonctions sont précédées d’une queue Amazon SQS afin de découpler chaque calcul et contrôler le flux d’inférence sur chaque modèle. La combinaison de ces services permet de mettre en place une architecture d’inférence sans serveur, répondant aux exigences énoncées précédemment.

Avant d’expliquer chacun de ces choix, observons le schéma général de la solution :

Les étapes de traitement d’une transaction sont les suivantes :

  1. Après un pré-traitement, le message arrive vers le couple queue SQS/fonction Lambda du nœud (modèle) de la couche 0 de l’arbre pour inférence,
  2. En fonction de la sortie et de la taxonomie, le routeur envoie le message vers le nœud suivant,
  3. Le nœud suivant, en couche 1, effectue une nouvelle inférence, puis le message est dirigé vers le routeur,
  4. Le routeur traite de nouveau la sortie et l’envoie vers le nœud suivant… Ce processus continue jusqu’à ce qu’un nœud de sortie soit atteint,
  5. Si un nœud de sortie est atteint, le routeur envoie le message vers un topic Amazon SNS, auquel d’autres services vont souscrire, pour notamment écrire le résultat dans une base de données.

Choix du moteur de calcul

Pour un cas d’usage de type Machine Learning, AWS Lambda ne semble pas être le choix le plus intuitif, car en déployant un modèle sur une fonction Lambda, le rapport coût / inférence est plus élevé que sur une instance EC2 par exemple. Cependant plusieurs facteurs ont rendu Lambda bien plus intéressant pour Foxintelligence :

  • La forte variabilité du volume de transactions à traiter : pendant la journée ce volume passe souvent de 0 inférence à la minute à plusieurs centaines de milliers dans certains cas. Ces variations ne peuvent pas être anticipées, ni en fréquence, ni en pic. Avec Lambda, le nombre d’environnements d’exécution peut automatiquement augmenter en fonction de la demande et on ne paie que lorsque qu’il y a des transactions à traiter, rendant les coûts proportionnels à l’usage,
  • Le besoin de Foxintelligence en agilité : les modèles sont itérativement améliorés et la taxonomie change, il faut donc pouvoir rapidement introduire ces changements à l’arbre de catégorisation sans impacter l’existant. Lambda est la seule solution viable qui permet efficacement de déployer un seul modèle sur sa propre infrastructure et donc limiter l’impact global de chaque déploiement,
  • Lambda étant un service sans serveur (serverless), les efforts opérationnels sont limités.

Orchestration des inférences

La méthode d’orchestration des inférences est structurante pour utiliser AWS Lambda de façon optimale et tirer parti de tous les avantages.
Contrairement à une approche intuitive qui consisterait à utiliser un orchestrateur central pour lancer les inférences de façon synchrone et router les messages vers les bons modèles, Foxintelligence a choisi une autre solution.
C’est la fonction Lambda qui est présente entre chaque couche de l’arbre qui route les messages vers le prochain couple fonction Lambda / queue SQS (ou sortie) en fonction de la taxonomie et du résultat précédent. Chaque inférence est asynchrone et indépendante des autres.
La distribution des inférences nécessaires à chaque transaction est donc plus optimisée pour les raisons suivantes :

  • La queue SQS permet d’accumuler les messages destinés à un modèle et les traiter au rythme désiré en fonction de la demande appliquée à ce modèle,
  • Lorsque les messages sont dépliés de la queue il est possible de les traiter par lot (batch) et améliorer la performance des inférences,
  • Chaque modèle peut scaler indépendamment des autres en fonction de la demande, ce qui est crucial : par exemple, le nœud de la couche 0 sera statistiquement beaucoup plus sollicité que les nœuds de la couche 4.

En résumé, cette méthode d’orchestration va permettre d’accumuler efficacement les inférences au niveau de chaque modèle, qui va les traiter à un rythme optimal en fonction de la demande appliquée, permettant ainsi de réduire le cout par inférence, de scaler efficacement et d’améliorer les performances.

Fonctionnement d’un nœud

Les fonctions Lambda ont un code strictement identique, mais il est nécessaire d’en déployer une distincte par nœud car les modèles à charger sont différents et le cycle de vie de la fonction Lambda doit être propre au nœud.
C’est ainsi à ce jour un total de 360 couples SQS/Lambda déployés, ayant une structure semblable au schéma ci-dessus.

Lors du provisionnement d’un environnement d’exécution Lambda, le modèle est téléchargé depuis S3 et stocké en mémoire, la localisation du modèle est retrouvée grâce à une variable d’environnement propre à chaque fonction. Cette étape est réalisée en dehors du gestionnaire (handler) Lambda afin de ne télécharger qu’une seule fois le modèle lors du cycle de vie de l’environnement.

Le lien entre la queue SQS et la fonction Lambda est réalisé grâce à un mapping de source d’évènement (event source mapping), une ressource Lambda qui va lire la queue et appeler la fonction, permettant d’abstraire le processus de dépilage de la queue. Le rythme de lecture des messages est défini en jouant sur le comportement de traitement par lot (batching behaviour) : la taille du lot (BatchingSize) définit le nombre maximal de messages que la fonction récupère, et la fenêtre de traitement indique la durée maximale durant laquelle on attend des messages. Foxintelligence a choisi des lots de 100 messages.

Avec ces paramètres, le nombre d’environnements d’exécution provisionnés sera proportionnel à la taille de la queue SQS, permettant ainsi d’absorber les forts pics de charge. Néanmoins, le nombre de transaction pouvant être très élevé à un instant donné, il faut limiter la simultanéité (concurrency) afin de protéger les autres fonctions Lambda du compte AWS. Pour cela, Foxintelligence utilise la simultanéité maximum (maximum concurrency), un paramètre qui permet de limiter le nombre d’environnements d’exécution qu’un mapping de source d’évènement (event source mapping) peut provisionner, protégeant ainsi la simultanéité sans générer d’erreurs lors de l’atteinte de la limite d’exécution d’une fonction.
Une valeur différente est appliquée en fonction de la couche dans laquelle le nœud est situé.

Le déploiement de modèles de Machine Leaning peut néanmoins être soumis à quelques limitations : les librairies (PyTorch et HuggingFace) nécessaires au fonctionnement des modèles sont plus volumineuses que la taille maximale de package d’une fonction Lambda (250 MB décompréssés). Dans ce cas, depuis 2020, les fonctions peuvent être déployées avec des images de conteneurs Lambda dont la taille maximale du package décompressé est de 10 GB. Aussi, la taille maximale du modèle qu’il est possible de déployer est de 10 GB à ce jour, car la quantité maximale de RAM qu’on peut allouer à une fonction Lambda est 10 GB (idem pour le stockage éphémère /tmp). Aucune de ces limitations n’était bloquante pour Foxintelligence, permettant l’utilisation d’AWS Lambda sans problème.

Conclusion

Cette architecture mise en place par Foxintelligence a permis de répondre à toutes les exigences initiales. En comparaison à la précédente solution où les modèles étaient déployés sur des instances EC2 :

  • Les couts par inférence sont 94% moins élevés,
  • La performance (nombre de transactions par secondes) est multipliée par 3.5 en pic,
  • Le système est capable de scaler rapidement, passage de 0 à plusieurs centaines de milliers de requêtes par minutes sans problèmes,
  • De par sa nature sans serveur, Lambda réduit fortement la charge opérationnelle, permettant de se focaliser sur l’entraînement et le déploiement de modèles toujours plus performants,
  • Il est possible de déployer les modèles fréquemment et avec un impact limité. En moyenne, des dizaines de déploiements sont réalisés chaque semaine.

Ainsi, Foxintelligence peut se permettre de traiter en moyenne 20 millions de transactions par jour (contre 800 000 auparavant), avec des pics à plusieurs centaines de millions certains jours.

Le travail continue néanmoins : il est maintenant question d’optimiser encore plus les couts / inférences, d’augmenter la précision des modèles et d’améliorer les pipelines de déploiements.