Est-il difficile d'exécuter Stable Diffusion sur iPhone ? Dans l'article que nous allons présenter aujourd'hui, l'auteur donne la réponse : ce n'est pas difficile, et l'iPhone a encore 50 % de performances.
Comme nous le savons tous, Apple lance chaque année un nouvel iPhone qui prétend être plus rapide et meilleur dans tous les aspects, principalement grâce au développement rapide de nouveaux modèles visuels et capteurs d'image. Prenons l'exemple de la photographie. Si vous revenez à il y a 10 ans, pouvez-vous prendre des photos de haute qualité avec un iPhone ? La réponse est non, car le développement de la technologie est progressif. En 10 ans, il suffit d'améliorer la technologie. de la photographie sur téléphone portable.
En raison de ce modèle de développement (progressif) de la technologie, il viendra un moment où certains programmes deviendront presque inutilisables, même lorsqu'ils fonctionnent sur le meilleur équipement informatique. Mais ces nouveaux programmes avec de nouveaux scénarios activés ont attiré l'attention de certains utilisateurs et les gens étaient prêts à les étudier.
L'auteur de cet article est l'un d'entre eux. Au cours des 3 dernières semaines, l'auteur a développé une application qui peut générer (invoquer) des images via Stable Diffusion, puis les éditer comme vous le souhaitez. L'application ne prend qu'une minute pour générer des images sur le dernier iPhone 14 Pro, en utilisant environ 2 Go de mémoire d'application, et environ 2 Go de données initiales doivent être téléchargées pour commencer.
Lien de l'App Store : https://apps.apple.com/us/app/draw-things-ai-generation/id6444050820
Ce résultat a attiré de nombreux internautes à discuter, et certaines personnes ont commencé s'inquiéter pour leur téléphone portable Problème de consommation de batterie, et a déclaré en plaisantant : C'est cool, mais cela semble être un bon moyen de vider la batterie du téléphone.
"Je n'ai jamais été aussi heureux de sentir la chaleur de mon iPhone que maintenant."
"En cet hiver froid, vous pouvez utiliser votre téléphone comme chauffe-mains."
Mais tout le monde se moque de la chaleur du téléphone. En même temps, ils ont également donné une très haute évaluation à ce travail."C'est incroyable. Il faut environ 45 secondes pour générer une image complète sur mon iPhone SE3 - c'est presque aussi rapide que la version originale sur mon macbook M1 Pro!"
Optimisation de la mémoire et du matériel sur en même temps
Comment ça se fait ? Jetons ensuite un coup d'œil au processus d'implémentation de l'auteur :Si vous souhaitez terminer l'exécution de Stable Diffusion sur l'iPhone tout en économisant 50 % des performances, un défi majeur consiste à exécuter le programme sur un appareil iPhone avec 6 Go. RAM. 6 Gio semble beaucoup, mais si vous utilisez plus de 2,8 Gio sur un appareil de 6 Gio ou 2 Gio sur un appareil de 4 Gio, iOS tuera votre application.
Alors, combien de mémoire le modèle de diffusion stable nécessite-t-il pour l'inférence ?
Cela commence également par la structure du modèle. Habituellement, le modèle de diffusion stable contient 4 parties : 1. Encodeur de texte, qui génère des vecteurs de caractéristiques de texte pour guider la génération d'images ; 2. Encodeur d'image en option, qui encode les images dans l'espace latent (pour la génération d'image à image) ; , qui débruite lentement la représentation latente de l'image du bruit ; 4. Décodeur d'image, qui décode l'image à partir de la représentation latente.
Les 1er, 2ème et 4ème modules sont exécutés une fois pendant l'inférence et nécessitent un maximum d'environ 1 Go. Le modèle de débruiteur prend environ 3,2 Go (virgule flottante complète) et doit être exécuté plusieurs fois, l'auteur souhaite donc conserver le module dans la RAM plus longtemps.
Le modèle original de diffusion stable nécessitait près de 10 Gio pour effectuer une inférence d'image unique. Entre une seule entrée (2x4x64x64) et une sortie (2x4x64x64), de nombreuses couches de sortie sont intercalées. Toutes les sorties des couches ne peuvent pas être réutilisées immédiatement, certaines d'entre elles doivent conserver certains paramètres pour une utilisation ultérieure (réseaux résiduels).
Depuis un certain temps, les chercheurs optimisent PyTorch Stable Diffusion. Ils ont réservé un espace de stockage temporaire pour les bibliothèques NVIDIA CUDNN et CUBLAS utilisées par PyTorch. Ces optimisations visent toutes à réduire l'utilisation de la mémoire, de sorte que le modèle de diffusion stable peut fonctionner avec des cartes. aussi bas que 4 Go. Mais cela a quand même dépassé les attentes de l’auteur. Par conséquent, l'auteur a commencé à se concentrer sur le matériel Apple et son optimisation. Dans un premier temps, l'auteur a envisagé un nombre à virgule flottante de 3,2 Go ou 1,6 Go s'il ne voulait pas déclencher le OOM (Out of Memory) d'Apple, qui fait référence à la mémoire occupée par l'application atteignant la limite supérieure du système iOS. mémoire occupée par une seule application, ce serait System force kill), l'auteur dispose d'environ 500 Mo d'espace à utiliser. La première question, quelle est la taille de chaque sortie intermédiaire ? Il s'avère que la plupart d'entre eux sont relativement petits, moins de 6 Mo chacun (2x320x64x64). Le framework utilisé par l'auteur (s4nnc) peut raisonnablement les regrouper dans moins de 50 Mo pour les réutiliser. Il convient de mentionner que le débruiteur dispose d'un mécanisme d'auto-attention qui prend sa propre représentation latente de l'image en entrée. Pendant le calcul d'auto-attention, il existe une matrice de lots de taille 16x4096x4096, qui après application de softmax est d'environ 500 Mo dans FP16, et peut être effectuée "sur place", ce qui signifie qu'elle peut réécrire son entrée en toute sécurité sans être endommagée. Heureusement, les bibliothèques de bas niveau Apple et NVIDIA fournissent des implémentations softmax sur place, contrairement aux bibliothèques de niveau supérieur telles que PyTorch. Alors, cela peut-il vraiment être fait en utilisant environ 550 Mo + 1,6 Go de mémoire ? Sur le matériel Apple, une option courante pour implémenter des backends de réseaux neuronaux consiste à utiliser le framework MPSGraph. L'auteur a donc d'abord essayé d'utiliser MPSGraph pour implémenter toutes les opérations du réseau neuronal. L'utilisation maximale de la mémoire avec la précision FP16 est d'environ 6 Gio, ce qui est évidemment bien plus que l'utilisation de la mémoire attendue. Que se passe-t-il ? L'auteur a analysé les raisons en détail. Tout d'abord, il n'a pas utilisé MPSGraph à la manière courante de TensorFlow. MPSGraph nécessite d'encoder l'intégralité du graphe informatique, puis de consommer des tenseurs d'entrée/sortie, de gérer les allocations internes et de permettre à l'utilisateur de soumettre l'intégralité du graphe pour exécution. La façon dont l'auteur utilise MPSGraph est très similaire à PyTorch - en tant que moteur d'exécution d'opérations. Pour effectuer des tâches d'inférence, de nombreux MPSGraphExecutables compilés sont exécutés sur la file d'attente de commandes Metal, chacun d'entre eux pouvant contenir une mémoire allouée intermédiaire. Si elles sont soumises en une seule fois, toutes ces commandes conservent la mémoire allouée jusqu'à ce qu'elles terminent leur exécution. Un moyen simple de résoudre ce problème est d'ajuster la vitesse de soumission, il n'est pas nécessaire de soumettre toutes les commandes en même temps. En fait, Metal a une limite de 64 soumissions simultanées par file d'attente. L'auteur a essayé de soumettre 8 opérations à la fois, et la mémoire maximale a été réduite à 4 Go. Cependant, cela représente toujours 2 Gio de plus que ce que l'iPhone peut gérer. Pour calculer l'auto-attention à l'aide de CUDA, il existe une astuce courante dans l'implémentation originale du code Stable Diffusion : utilisez le déplacement au lieu de la transposition. Cette astuce fonctionne car CUBLAS peut gérer directement les tenseurs striés permutés, évitant ainsi d'avoir à utiliser une mémoire dédiée pour transposer le tenseur. Mais MPSGraph n'a pas de support de tenseur strié, un tenseur permuté sera de toute façon transposé en interne, ce qui nécessite une allocation de mémoire intermédiaire. En transposant explicitement, les allocations seront gérées par des couches de niveau supérieur, évitant ainsi les inefficacités internes de MPSGraph. En utilisant cette astuce, l'utilisation de la mémoire sera proche de 3 Go. Il s'avère qu'à partir d'iOS 16.0, MPSGraph ne peut plus prendre de décisions d'allocation optimales pour softmax. Même si les tenseurs d'entrée et de sortie pointent vers les mêmes données, MPSGraph alloue un tenseur de sortie supplémentaire, puis copie le résultat à l'emplacement indiqué. L'auteur a constaté que l'utilisation de l'alternative Metal Performance Shaders faisait parfaitement l'affaire et réduisait l'utilisation de la mémoire à 2,5 Go sans aucune incidence sur les performances. D'autre part, le noyau GEMM de MPSGraph nécessite une transposition interne. Les transpositions explicites n'aideront pas non plus ici, car ces transpositions ne sont pas des opérations « sur place » de couches de niveau supérieur, et pour un tenseur de taille spécifique de 500 Mo, cette allocation supplémentaire est inévitable. En passant aux Metal Performance Shaders, les auteurs du projet ont récupéré 500 Mo supplémentaires avec une pénalité de performances d'environ 1 %, réduisant finalement l'utilisation de la mémoire aux 2 Go idéaux.
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!