3 points par GN⁺ 2023-08-21 | 1 commentaires | Partager sur WhatsApp
  • Outil permettant d’exécuter l’inférence de modèles de diffusion en pur C/C++, y compris pour les familles SD, Flux et Wan, avec l’objectif d’une implémentation légère sans dépendances externes
  • L’implémentation repose sur ggml et adopte une structure Plain C/C++ fonctionnant de la même manière que llama.cpp
  • Les modèles pris en charge se répartissent entre modèles d’image, modèles d’édition d’image et modèles vidéo, avec notamment SD1.x, SD2.x, SDXL, SD3/SD3.5, FLUX, Qwen Image, Wan2.1/Wan2.2 et LTX-2.3
  • Les fonctionnalités couvrent PhotoMaker, Control Net pour SD 1.5, les LoRA au format stable-diffusion-webui, LCM/LCM-LoRA, le décodage latent basé sur TAESD, l’upscale ESRGAN, les negative prompts et la prise en charge d’un tokenizer avec pondération des tokens
  • Les backends d’exécution sont CPU, CUDA, Vulkan, Metal, OpenCL et SYCL, avec côté CPU la prise en charge d’AVX, AVX2 et AVX512 sur architecture x86
  • Les plateformes prises en charge sont Linux, Mac OS, Windows et Android, ce dernier via Termux et Local Diffusion
  • Les formats de poids pris en charge sont .ckpt, .pth, .pt, .safetensors et .gguf, et le mode de conversion permet de convertir les poids des modèles en .gguf ou .safetensors
  • Le flux d’utilisation de base consiste à récupérer un binaire précompilé depuis la page des releases ou à compiler depuis les sources, puis à télécharger les poids du modèle et lancer la génération d’image sous la forme ./bin/sd-cli -m ../models/v1-5-pruned-emaonly.safetensors -p "a lovely cat"
  • Des fonctions d’optimisation de l’usage mémoire sont proposées avec Flash Attention et le VAE tiling processing, tandis que l’optimisation du runtime, du placement des backends pour les paramètres et des performances fait l’objet de guides séparés
  • Les options de reproductibilité sont séparées entre --rng cuda et --rng cpu, avec pour objectif une cohérence respective avec le RNG GPU de stable-diffusion-webui et le RNG de ComfyUI
  • Dans les sorties PNG, les paramètres de génération sont insérés sous forme de chaîne de texte compatible webui
  • Il existe des projets wrapper pour Golang, C#, Python, Rust et Flutter/Dart, et Jellybox, Local Diffusion, LocalAI, KoboldCpp, entre autres, utilisent stable-diffusion.cpp comme backend de génération d’images
  • Le projet est en développement actif et l’API comme les options en ligne de commande peuvent changer fréquemment

1 commentaires

 
GN⁺ 2023-08-21
Avis sur Hacker News
  • Llama.cpp/ggml convient particulièrement bien aux LLM
    Les besoins mémoire sont importants, la quantification est efficace, et la génération de tokens est étonnamment séquentielle et limitée par la bande passante mémoire, ce qui convient bien au CPU, et encore mieux au pipeline d’inférence CPU/GPU particulier de ggml
    Mais Stable Diffusion, c’est différent. La quantification ne fonctionne pas aussi bien, UNet demande énormément de calcul, et la génération d’images par lots est efficace et utile même pour un seul utilisateur. C’est donc plus adapté aux GPU/GPU intégrés, et cela bénéficie beaucoup de la facilité de bidouille des implémentations Python
    Pour Stable Diffusion, je pense que la bonne voie consiste à produire des exécutables via de la compilation machine learning. AITemplate est déjà très rapide https://github.com/VoltaML/voltaML-fast-stable-diffusion, et TVM Vulkan est aussi très prometteur si quelqu’un termine correctement une implémentation de démo https://github.com/mlc-ai/web-stable-diffusion
    En plus, on conserve aussi l’essentiel de la bidouillabilité d’une implémentation PyTorch pure

    • Le projet ci-dessus prend aussi en charge le GPU dans une certaine mesure si l’on passe les bons flags de compilation GGML
      Par exemple, GGML_CUBLAS est pris en charge à la compilation, et on obtient une accélération plutôt correcte par rapport au C/C++ pur
    • À l’inverse, c’est bien pour les personnes qui n’ont pas de GPU NVIDIA avec au moins 6 Go de VRAM, mais veulent tout de même expérimenter localement avec ces réseaux neuronaux
      Même si cela prend un peu de temps, on peut l’exécuter sur un vieux portable
    • Si ma mémoire est bonne, j’avais aussi observé une assez bonne accélération avec torch.compile, et je me souviens avoir travaillé dessus moi-même
      Je vais voir si je peux retrouver les chiffres
  • C’est excellent d’avoir même implémenté CLIP
    Ce serait sympa de l’extraire séparément et de le compiler en implémentation WebAssembly
    Modification : il semble que quelqu’un ait déjà créé https://github.com/monatis/clip.cpp. Il ne reste plus qu’à en faire une version WebAssembly

    • Puisqu’on parle de CLIP, je m’inquiète toujours du fait qu’avec OpenAI et Google passés en mode concurrence, le prochain modèle de niveau CLIP ne soit pas rendu public
      C’est frustrant de se dire qu’il existe peut-être déjà quelque part, dans un coffre-fort secret, un modèle de niveau CLIP plus avancé
      Modification : je ne parle pas de CLIP-2, mais d’une avancée d’un niveau d’importance comparable à CLIP
  • La configuration est incroyablement simple, au point que je l’ai essayé immédiatement pour la première fois
    Je me demande quel niveau de performance est censé être normal
    Sous Linux, je l’ai lancé sur un AMD Ryzen 7 5700G avec cmake .. -DGGML_OPENBLAS=ON, sans GPU dédié, seulement le GPU intégré
    En exécutant ./bin/sd -m ../models/sd-v1-4-ggml-model-f32.bin -p "a lovely cat", chaque étape d’échantillonnage a pris environ 12 secondes, et l’échantillonnage complet 246,40 secondes
    Je me demande si c’est la performance attendue
    Modification : OpenBLAS n’était pas installé, donc ce flag n’a eu aucun effet

    • C’est bien. En gros, ça fait ce que je voulais il y a un an[0]
      À l’époque, presque toutes les solutions exigeaient un tas de dépendances Python, et l’installation prenait tellement longtemps qu’elle finissait par échouer faute d’espace disque
      Vraiment, littéralement, cela remplace plusieurs gigaoctets d’espace disque par un seul binaire de 799 Ko. En prime, si l’on utilise le format Q8_0, qui semble être le plus rapide, on économise aussi environ 2,3 Go de données
      Cela dit, il semble y avoir des bugs avec les tailles d’image autres que la taille par défaut 512x512. Certaines tailles comme 544x544 ont tendance à provoquer un échec d’assertion, les tailles inférieures à 512x512 produisent parfois des images incohérentes, et les tailles inférieures à 384x384 le font presque toujours
      [0] https://news.ycombinator.com/item?id=32555608
    • Il faut quantifier le modèle, mais environ 12 secondes par itération semble correct
    • En CPU uniquement, quantification 8 bits, Intel Core i7 4770S, 16 Go de RAM DDR3, sur un PC fanless vieux de 10 ans, j’obtiens 32 secondes par étape d’échantillonnage, et la sortie est correcte
  • Les implémentations C/C++ liées à l’IA ont quelque chose de particulièrement séduisant
    Le code paraît propre et intuitif, et cela donne l’impression que tout le domaine de l’IA devient tangible et apprenable
    Est-ce parce que l’écosystème Python est trop brouillon ?

    • Les réécritures améliorent généralement la qualité du code, et remplacer des dépendances par du code sur mesure qui ne fait que ce qui est nécessaire améliore aussi la qualité du code
      La version Python utilise aussi du code C et C++ pour la vitesse, mais ici tout est dans un seul langage
      Les trois facteurs qui permettent d’obtenir du code propre agissent donc ensemble
  • C’est agréable de voir des gens du machine learning sortir de Python et utiliser un langage qui exploite le matériel de façon optimale, sans devoir configurer un environnement spécial pour compiler et exécuter

    • La comparaison est assez étrange
      D’abord, le projet de l’article n’utilise pas le GPU, comme llama.cpp, alors que la plupart du code Python de machine learning utilise le GPU. Écrire du code Python qui exploite le GPU de façon optimale n’est pas difficile. On pourrait dire que le GPU est un environnement spécial pour compiler et exécuter, mais on peut considérer qu’il est bien mieux adapté à ce problème
      Ensuite, comme llama.cpp, le projet de l’article a produit du code efficace et très spécialisé après qu’il a été confirmé que des modèles précis comme Stable Diffusion/LLaMA fonctionnaient bien. En revanche, là où Python brille, c’est à l’étape de prototypage, quand on n’a pas encore trouvé le bon modèle. Je n’ai pas encore vu de prototypage aussi simple et confortable en C++
      Il ne s’agit pas de dénigrer l’excellent travail que font les gens de llama.cpp dans le domaine du machine learning sur CPU. Simplement, les problèmes résolus sont complètement différents
    • Ce serait bien mieux si tous les modèles de machine learning disposaient d’une API d’inférence en C simple, appelable directement depuis presque n’importe quel langage ou plateforme, sans bazar de dépendances ni de configuration d’environnement
    • Les composants critiques pour les performances dans la stack de machine learning ne sont d’ailleurs pas réellement implémentés en Python
      En interne, tout est depuis longtemps en CUDA, C et C++
      Python n’est qu’une colle très efficace qui assemble tout cela
    • Je suis vraiment reconnaissant envers les personnes qui font ce genre de travail
      C’est la seule façon dont j’ai pu exécuter ces modèles sans problèmes pénibles. La différence est énorme. Le duo CUDA et Linux n’est déjà pas idéal, et AMD avec Windows est catastrophique. Je ne pense probablement pas être le seul
    • Il est intéressant que mon CPU puisse faire tourner certains de ces modèles en forme quantifiée à une vitesse presque comparable à celle du GPU
      Au final, est-ce que tout n’était qu’une question de bande passante mémoire ?
      L’architecture GPU ne se résume pas à la puissance de calcul : elle place aussi la mémoire de travail près des unités de calcul. Chaque unité dispose d’une mémoire locale synchronisée avec la mémoire globale. Est-ce une grande partie de la raison pour laquelle les GPU sont forts sur ce type de tâche ?
  • Ça ressemble à du C++, alors pourquoi parler de C/C++ ?

    • D’après ce que je comprends, la dépendance de base, ggml, est écrite en C
  • J’ai vu ce dépôt aujourd’hui, je l’ai récupéré, puis j’ai compilé une .dylib sur Mac et généré des bindings depuis le fichier d’en-tête fourni avec l’outil ffi-gen de Dart
    Je fais des essais avec Flutter et j’utilise FFI pour éviter de lancer un sous-processus
    Résultat : un gros mal de tête et une app cassée. Je réessaierai demain avec les idées claires
    Cela dit, le dépôt lui-même est excellent, et sur M1 il s’exécute en f16 en moins de 10 minutes

  • Les exemples de différents niveaux de quantification sont assez impressionnants
    Le passage de f16 à q8_0 ressemble davantage à un changement de direction qu’à une perte de qualité. Le résultat en q5_1 semble difficile à distinguer de q8_0
    On perd le déterminisme des modèles haute précision, mais en pratique cela pourrait être tout à fait exploitable

  • Y a-t-il des benchmarks ?

    • Quelques personnes ont chronométré ici, et cela semble prendre environ 15 à 20 secondes par itération selon la quantification et le matériel
      https://github.com/leejet/stable-diffusion.cpp/issues/1
    • Je l’ai compilé avec la commande cmake .. -DGGML_CUBLAS=ON -DCMAKE_CUDA_COMPILER=/opt/cuda/bin/nvcc et utilisé une NVIDIA GeForce RTX 2060 SUPER
      Le modèle a été converti en FP16
      Avec cette option, le temps par itération est compris entre 8,5 et 9 secondes, et le temps total pour générer une image est d’environ 200 secondes