Cet article présentera la formation au système de recommandation à grande échelle de WeChat basée sur PyTorch. Contrairement à certains autres domaines du deep learning, le système de recommandation utilise toujours Tensorflow comme cadre de formation, ce qui est critiqué par la majorité des développeurs. Bien que certaines pratiques utilisent PyTorch pour la formation aux recommandations, l'échelle est petite et il n'y a pas de véritable vérification commerciale, ce qui rend difficile la promotion des premiers utilisateurs d'entreprises.
En février 2022, l'équipe PyTorch a lancé la bibliothèque officielle recommandée TorchRec. Notre équipe a commencé à essayer TorchRec en interne en mai et a lancé une série de coopération avec l'équipe TorchRec. Au cours de plusieurs mois d'essai d'utilisation, nous avons constaté de nombreux avantages de TorchRec, et nous pensons également que TorchRec présente encore quelques lacunes dans les modèles à très grande échelle. En réponse à ces lacunes, nous avons conçu des fonctions étendues pour combler ses problèmes. En septembre 2022, l'intégration dynamique des fonctions étendues que nous avons conçue a été officiellement intégrée dans la branche principale de TorchRec et est toujours optimisée en permanence avec l'équipe officielle.
Pour différents membres de l'équipe, TorchRec présente différents avantages. Tout d'abord, pour la grande majorité des ingénieurs en algorithmes de l'équipe, le framework recommandé par PyTorch permet à chacun de profiter enfin des graphiques dynamiques plus conviviaux et de l'expérience de débogage expérimentée par les ingénieurs CV et NLP.
De plus, l'excellente compatibilité de PyTorch (un modèle basé sur PyTorch1.8 peut fonctionner sur la dernière version 1.13 sans modifier une seule ligne de code) permet aux ingénieurs en algorithmes de mettre enfin à niveau le framework en toute confiance. Vous pouvez ainsi en profiter. les dernières fonctionnalités du framework et de meilleures performances. D'un autre côté, certains frameworks recommandés basés sur TensorFlow sont souvent bloqués sur une certaine version de TensorFlow. Par exemple, de nombreuses équipes peuvent encore utiliser des frameworks internes basés sur TensorFlow 1.x. TensorFlow 1.x a arrêté la maintenance en janvier 2021, ce qui signifie qu'au cours des deux dernières années, tous les nouveaux bugs et nouvelles fonctionnalités ne seront pas bien pris en charge. Les problèmes rencontrés lors de l'utilisation ne peuvent être réparés que par l'équipe de maintenance interne, ce qui ajoute des coûts supplémentaires. Les mises à niveau opportunes du framework peuvent également apporter des améliorations gratuites de la vitesse. Les versions supérieures de PyTorch correspondent souvent aux versions supérieures de CUDA, ainsi que certaines nouvelles fonctionnalités telles que le graphique CUDA, qui peuvent encore augmenter la vitesse d'entraînement et améliorer l'efficacité de l'entraînement.
En plus des ingénieurs en algorithmes, l'équipe framework est également une partie importante de l'équipe de recommandation. L'équipe framework de l'entreprise effectuera un développement secondaire en fonction des besoins internes après avoir sélectionné le framework open source. Pour eux, un framework recommandé pour PyTorch conduira à une expérience de développement plus rationalisée. De nombreux frameworks traditionnels recommandés par TensorFlow imiteront TF pour créer une extension basée sur une session C++ - cette solution de conception était considérée comme une solution très avancée à l'époque - mais cela nécessite une compilation complète de l'intégralité de TensorFlow juste pour modifier une ligne de code, ce qui prend du temps C'est très long, et il faut même résoudre des problèmes triviaux comme le téléchargement de dépendances externes sur l'intranet, et l'expérience de développement n'est pas très bonne.
Vous ne rencontrerez pas de tels problèmes lors de l'utilisation de PyTorch, car PyTorch prend la philosophie Python comme noyau et espère que tout le monde pourra l'étendre librement. Lorsque nous effectuons un développement secondaire, il nous suffit de l'encapsuler avec une bibliothèque Python relativement mature comme pybind11, de conditionner notre bibliothèque dans une bibliothèque de liens dynamiques, puis de la charger. De cette façon, la vitesse globale de compilation sera naturellement beaucoup plus rapide et le coût d'apprentissage sera bien inférieur.
Comme mentionné précédemment, PyTorch est un framework avec une très bonne compatibilité descendante, ce qui élimine le besoin pour l'équipe de maintenance de maintenir plusieurs versions. De nombreux problèmes courants peuvent être officiellement résolus et chacun peut se concentrer sur des problèmes spécifiques. aux besoins, l’efficacité des membres de l’équipe sera considérablement améliorée.
Ce qui précède présente tous les avantages de TorchRec en tant que cadre de recommandation PyTorch. Ce qui nous rend très heureux, c'est que l'équipe TorchRec ne s'arrête pas à créer un cadre de recommandation PyTorch. Ils ont observé les caractéristiques des modèles et du matériel de recommandation existants et ont ajouté de nombreuses nouvelles fonctionnalités au cadre, donnant à TorchRec un net avantage en termes de performances par rapport aux cadres de recommandation traditionnels. Ensuite, j'en choisirai quelques-uns à présenter, à savoir l'intégration GPU, l'excellent noyau GPU de TorchRec et la division d'intégration que TorchRec peut effectuer sur la base de la communication réseau.
Le premier est l'intégration de GPU. Examinons d'abord le processus de formation GPU du système de recommandation traditionnel. Nous placerons le modèle spécifique sur le travailleur GPU et l'intégration sera stockée sur le PS distant. Chaque étape d'itération extraira d'abord les paramètres du PS distant, puis effectuera des calculs avant et arrière du modèle sur le GPU, transférera le dégradé vers le PS et mettra à jour les paramètres sur le PS.
La partie verte sur l'image est l'opération effectuée sur le GPU, et la partie rouge est effectuée sur le réseau ou le CPU. On peut voir que bien que le GPU soit la partie la plus coûteuse du système, de nombreuses opérations ne sont pas effectuées sur le GPU.
Le processus traditionnel n'utilise pas pleinement le GPU. Dans le même temps, d'un point de vue matériel, la mémoire d'une seule carte GPU devient de plus en plus grande, et certains modèles denses sont loin d'utiliser pleinement le GPU ; avec l'optimisation continue de NVIDIA, la liaison NV et le RDMA direct GPU ont également fait l'inter -Communication par carte de plus en plus rapide.
L'intégration de GPU est une solution très simple. Il divise directement l'intégration et la place sur le GPU - par exemple, s'il y a 8 cartes sur une seule machine, nous divisons directement l'intégration en 8 parties, et plaçons chaque partie sur une carte - garantissant ainsi que toutes les opérations restent sur le carte. . L'efficacité d'utilisation du GPU sera considérablement améliorée et la vitesse d'entraînement sera également améliorée qualitativement. Si vous craignez un espace mémoire vidéo insuffisant sur le GPU, TorchRec prend également en charge UVM, qui peut diviser à l'avance une partie de la mémoire sur l'hôte en complément de la mémoire vidéo, augmentant ainsi la taille d'intégration pouvant être placée à l'intérieur d'un GPU. une seule machine.
En plus de l'intégration GPU, TorchRec implémente également un très excellent noyau GPU. Ces noyaux profitent pleinement des dernières fonctionnalités matérielles et des fonctionnalités CUDA.
Par exemple, si vous souhaitez implémenter un noyau de recherche d'intégration, c'est-à-dire que vous devez trouver un tas de vecteurs d'intégration correspondant aux ID d'une grande intégration, alors dans une implémentation normale, chaque thread GPU sera Attribuez un identifiant et laissez-les trouver respectivement l'intégration correspondante. À ce stade, nous devons considérer que la couche inférieure du GPU est planifiée en fonction du warp, et que les 32 threads d'un warp liront et écriront ensemble la mémoire vidéo. Cela signifie que dans le processus ci-dessus, bien que la mémoire vidéo soit continuellement consultée lors de la lecture de l'ID, les copies suivantes deviennent un état de lecture et d'écriture aléatoire. Pour le matériel, la lecture et l'écriture aléatoires ne peuvent pas utiliser pleinement la bande passante mémoire et l'efficacité de fonctionnement n'est pas suffisamment élevée.
TorchRec utilise des primitives de chaîne comme shuffle_sync pour diffuser l'ID à tous les threads de la chaîne après avoir lu l'ID dans chaque thread, permettant à 32 threads dans un wrap de le traiter en même temps Avec la même intégration , une lecture et une écriture continue de la mémoire peuvent être effectuées, ce qui améliore considérablement l'efficacité d'utilisation de la bande passante de la mémoire vidéo et augmente plusieurs fois la vitesse du noyau.
Ce tableau est un test officiel d'amélioration des performances de recherche d'intégration. Ici, Fused EBC est le noyau optimisé. On peut voir que sous différents paramètres, TorchRec a des performances améliorées des dizaines de fois par rapport au PyTorch natif. Sur la base de TorchRec, nous avons constaté que lorsque l'intégration est relativement petite (moins de 128), la moitié ou plus des threads peuvent être inactifs, nous regroupons donc davantage les threads dans la chaîne et les laissons traiter plusieurs intégrations en même temps.
Grâce à nos améliorations, le noyau sur les petites dimensions d'intégration a été amélioré de 10% à 30%. Cette optimisation a également été intégrée au repo officiel. Il est à noter que le noyau de TorchRec est placé dans la bibliothèque FBGEMM. Les amis intéressés peuvent y jeter un œil.
Enfin, je voudrais présenter le mécanisme de division d'intégration de TorchRec. Comme mentionné précédemment, l'intégration GPU consiste à diviser l'intégration et à la placer sur la carte, donc comment la diviser devient une question à considérer. Traditionnellement, il existe deux idées de division, par ligne et par colonne. Par rangée signifie que s'il y a 20 000 fonctionnalités, les nombres 0 à 10 000 sont placés sur la carte 1, et les nombres 10 000 à 20 000 sont placés sur la carte 2. De cette façon, lorsque nous nous entraînerons, si l'ID correspond à la carte 1, nous commencerons de Prenez-le de la carte 1, qui correspond à la carte 2, prenez-le donc de la carte 2. Le problème avec Row Wise est que, comme nous ne savons pas s'il existe un grand écart entre le volume de trafic des 10 000 premiers numéros et des 10 000 derniers numéros, la communication est déséquilibrée et le matériel réseau ne peut pas être pleinement utilisé.
La colonne est divisée du point de vue de la longueur d'intégration. Par exemple, la longueur totale de l'intégration est de 128. Les 64 premières dimensions et les 64 dernières dimensions peuvent être placées dans des positions différentes. De cette façon, la communication sera plus équilibrée, mais lors de la lecture, elle devra communiquer avec toutes les cartes ou PS. .
La différence de mode de division entraîne un compromis dans la sélection. Les cadres de recommandation traditionnels corrigeront la méthode de division d'intégration dans la conception, tandis que TorchRec prend en charge plusieurs méthodes de division - telles que par ligne, par colonne, voire par table, parallèlement aux données - et fournit en interne des modules tels que Planner, Estimator, PerfModel et d'autres modules peuvent automatiquement calculez la méthode de division en fonction de la bande passante, de la mémoire vidéo, de la mémoire, de la taille du modèle et d'autres paramètres du scénario d'utilisation. De cette façon, l'intégration peut être divisée plus efficacement en fonction de nos conditions matérielles réelles et le matériel peut être utilisé le plus efficacement possible. La plupart de ces fonctions sont implémentées en Python. Cela nous permet de le personnaliser pour notre environnement interne et de créer facilement un système de recommandation le plus adapté à notre environnement interne.
Dans nos expériences, pour des modèles standards tels que DeepFM et DCN, le cadre recommandé par TorchRec par rapport aux benchmarks précédents aura un étonnant 10 à 15x amélioration des performances. L’obtention de tels gains de performances nous donne la confiance nécessaire pour lancer TorchRec en affaires.
Pour le modèle d'impression fine de lecture WeChat, basé sur la précision de l'alignement, nous avons constaté que les performances sont améliorées d'environ 3 fois sur des données réelles, et même d'environ 10 fois sur de fausses données. La différence ici est que la lecture des données pour l’entraînement est devenue un goulot d’étranglement et nous continuons à optimiser davantage à cet égard.
03
La solution originale présente des lacunes dans les modèles de 100 milliards et plus
Les modèles présentés précédemment sont essentiellement des modèles de 10 milliards ou moins. C’est-à-dire un modèle pouvant tenir dans une seule machine. En poussant TorchRec vers des modèles plus grands, nous avons observé quelques problèmes avec la conception native de TorchRec. Pour les grands modèles, la solution d'intégration GPU pure de TorchRec nécessite plus de cartes - peut-être que la vitesse d'entraînement originale de 8 cartes peut absorber toutes les données, mais nous devons utiliser 16 cartes pour définir l'intégration, ce qui rend difficile l'amélioration du matériel GPU. l’efficacité d’utilisation a encore été réduite.
Et pour les scénarios de grands modèles, l'équipe d'algorithmes propose souvent des exigences d'ajout et de suppression dynamiques pour l'intégration, telles que la suppression des identifiants qui n'ont pas été consultés depuis une semaine. La solution de TorchRec ne prend pas en charge une telle fonctionnalité. De plus, les très grandes entreprises modèles impliquent généralement de nombreuses équipes, et la migration des frameworks de base se heurtera à de grandes résistances. Ce dont nous avons besoin, c’est d’un soutien à une migration progressive et progressive, et nous ne pouvons pas laisser tout le monde abandonner le travail en cours. Cela serait trop coûteux et risqué.
Sur la base des exigences ci-dessus, nous avons réfléchi à la manière de modifier TorchRec afin qu'il puisse s'adapter au scénario de modèles à très grande échelle. Nous pensons que dans la formation à très grande échelle, il est toujours nécessaire de prendre en charge les connexions PS distantes, car le CPU PS distant est très mature et peut facilement prendre en charge l'ajout dynamique de l'intégration. Dans le même temps, pour la coopération entre équipes, PS peut être utilisé pour isoler la formation et l'inférence afin de réaliser une migration progressive.
Ensuite, la question suivante est de savoir comment introduire le PS. Si le PS est directement connecté à l'intégration du GPU, chaque étape d'itération doit toujours accéder au PS distant, ce qui augmentera la proportion des opérations globales du réseau et du CPU, et l'utilisation du GPU sera réduite.
04
Comment résoudre le problème de l'intégration dynamique de l'équipe WeChat
À ce stade, nous avons constaté que les nouveaux identifiants dans les données par unité de temps ne sont en réalité que représentaient une très petite partie du total des données Pour une petite partie, HugeCTR a également mentionné des conclusions similaires dans le document : seule une petite partie des identifiants sera fréquemment consultée. À partir de là, nous avons pensé à utiliser l'intégration de GPU pour l'entraînement normalement, puis à expulser les identifiants vers PS par lots lorsque la mémoire d'affichage est pleine.
Selon cette idée, si seulement n identifiants peuvent être stockés dans l'intégration GPU, et que le nombre total d'identifiants est N, voire infini. L'ID global peut être mappé sur 0, 1, 2, 3... dans l'ordre, et la relation de mappage est stockée dans une structure appelée ID transform, permettant à l'intégration GPU d'utiliser les résultats du mappage pour un entraînement normal. Lorsque l'intégration GPU est pleine, c'est-à-dire lorsqu'il y a n paires de mappages dans le transformateur d'ID, les ID sont expulsés vers PS par lots.
Dans cette conception, le PS peut rarement intervenir, et la communication entre le travailleur GPU et le PS n'est requise que lors de l'expulsion.
De plus, dans cette conception, PS doit uniquement être utilisé comme KV et n'a pas besoin de prendre en charge les mises à jour des paramètres. Il n'est pas nécessaire de mettre en œuvre des opérations liées à l'optimiseur, ce qui permet à l'équipe PS. se concentrer sur les opérations liées au stockage. Nous prenons également en charge les plug-ins qui implémentent n'importe quel stockage KV, et la version open source dispose d'un plug-in Redis intégré, de sorte que Redis peut également être utilisé comme PS.
Voici quelques détails de conception en intégration dynamique. Le transformateur d'identification le plus simple et le plus basique que nous avons implémenté utilise en fait une table de hachage, en utilisant le ska::flat_hash_map hautes performances de PyTorch.
En tant que seule opération CPU dans le processus, ID Transformer peut avoir des exigences de performances relativement élevées, nous avons donc également implémenté une version hautes performances qui est stockée dans les unités de cacheline L1 pour améliorer encore l'efficacité de l'accès à la mémoire. .
De plus, pour le schéma d'expulsion, nous espérons fusionner efficacement LRU et LFU sans augmenter la pression du cache mémoire. Inspirés de la solution LFU de Redis, nous avons conçu un algorithme probabiliste : seul l'index de la fréquence d'accès à l'ID est stocké. Par exemple, si vous y accédez 32 fois, 5 sont stockées. Lors de la mise à jour de la fréquence, si cet identifiant est à nouveau accédé, un nombre aléatoire à 5 chiffres sera généré. Si les 5 chiffres sont 0, c'est-à-dire qu'un événement avec une probabilité de 1/32 s'est produit, nous augmenterons l'indice de fréquence. à 6. Grâce à un tel algorithme de probabilité, les fréquences de LRU et LFU peuvent être mises dans uint32, intégrant LRU et LFU sans augmenter la pression d'accès à la mémoire.
Enfin, présentons brièvement notre solution multi-cartes. Nous rassemblons actuellement toutes les données de la carte vers le transformateur d'identification de la première carte, puis les diffusons. Étant donné que l'ID Transformer que nous avons implémenté a de très hautes performances et peut être combiné avec le pipeline de calcul GPU, il ne deviendra pas un goulot d'étranglement spécifique en termes de performances.
Voici quelques idées de conception pour une intégration dynamique. Dans l'une de nos activités internes de niveau billion, dans le cas de la précision de l'alignement, la solution d'intégration dynamique présente une amélioration des performances d'environ 3 fois par rapport à notre framework GPU Tensorflow d'origine interne. Par rapport à la version optimisée TF, elle présente toujours un avantage de performances de plus de 50 %.
Enfin, je recommande à tout le monde d'essayer Torchrec. Pour les entreprises relativement petites, comme des dizaines de milliards d'entreprises, il est recommandé d'utiliser directement le TorchRec natif : plug and play, aucun développement secondaire n'est requis et les performances peuvent être doublées. Pour les très grandes entreprises, nous vous recommandons d'essayer d'intégrer chez nous l'intégration dynamique de TorchRec. D'une part, il facilite la connexion au PS interne, d'autre part, il prend également en charge l'expansion et la migration progressive de l'intégration, et d'autre part. en même temps, vous pouvez toujours obtenir une certaine amélioration des performances.
Voici quelques modèles de précision et scénarios d'application existants que nous avons alignés. Les amis intéressés peuvent l'essayer.
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!