3 Punkte von GN⁺ 2023-08-21 | 1 Kommentare | Auf WhatsApp teilen
  • Ein Tool zur Ausführung von Diffusion-Modell-Inferenz in reinem C/C++, einschließlich der SD-, Flux- und Wan-Familien, mit dem Ziel einer schlanken Implementierung ohne externe Abhängigkeiten
  • Die Implementierung basiert auf ggml und ist als Plain C/C++-Struktur aufgebaut, die ähnlich wie llama.cpp funktioniert
  • Der unterstützte Modellumfang ist in Bildmodelle, Bildbearbeitungsmodelle und Videomodelle unterteilt und umfasst SD1.x, SD2.x, SDXL, SD3/SD3.5, FLUX, Qwen Image, Wan2.1/Wan2.2, LTX-2.3 usw.
  • Zum Funktionsumfang gehören PhotoMaker, Control Net für SD 1.5, LoRA im Stil von stable-diffusion-webui, LCM/LCM-LoRA, latent decoding auf Basis von TAESD, ESRGAN-Upscaling, negative prompts sowie Unterstützung für Token-Gewichtung im Tokenizer
  • Als Laufzeit-Backends werden CPU, CUDA, Vulkan, Metal, OpenCL und SYCL unterstützt; bei der CPU einschließlich Unterstützung für AVX, AVX2 und AVX512 auf x86-Architekturen
  • Unterstützte Plattformen sind Linux, Mac OS, Windows und Android; unter Android erfolgt die Ausführung über Termux und Local Diffusion
  • Unterstützte Gewichtsformate sind .ckpt, .pth, .pt, .safetensors und .gguf; der Konvertierungsmodus wandelt Modellgewichte in .gguf oder .safetensors um
  • Der grundlegende Nutzungsablauf besteht darin, vorkompilierte Binärdateien von der releases page herunterzuladen oder aus dem Quellcode zu bauen, anschließend die Modellgewichte zu laden und die Bildgenerierung in der Form ./bin/sd-cli -m ../models/v1-5-pruned-emaonly.safetensors -p "a lovely cat" auszuführen
  • Zur Optimierung des Speicherverbrauchs werden Flash Attention und VAE tiling processing bereitgestellt; Backend-Zuweisung zur Laufzeit, Parameterabstimmung und Performance-Verbesserungen sind Gegenstand separater Anleitungen
  • Die Reproduzierbarkeitsoptionen sind in --rng cuda und --rng cpu unterteilt und zielen jeweils auf Konsistenz mit dem GPU-RNG von stable-diffusion-webui bzw. dem RNG von ComfyUI ab
  • In die PNG-Ausgabe werden Generierungsparameter als mit webui kompatible Textzeichenfolge eingebettet
  • Es gibt Wrapper-Projekte für Golang, C#, Python, Rust und Flutter/Dart; Jellybox, Local Diffusion, LocalAI, KoboldCpp und weitere verwenden stable-diffusion.cpp als Backend für die Bildgenerierung
  • Das Projekt wird aktiv weiterentwickelt, und API sowie Kommandozeilenoptionen können sich häufig ändern

1 Kommentare

 
GN⁺ 2023-08-21
Meinungen auf Hacker News
  • Llama.cpp/ggml passt ungewöhnlich gut zu LLMs.
    Der Speicherbedarf ist groß, Quantisierung ist effektiv, die Token-Erzeugung ist überraschend seriell und an die Speicherbandbreite gebunden, was gut zu CPUs passt – und noch besser zu ggmls ungewöhnlicher CPU/GPU-Pipeline für Inferenz.
    Stable Diffusion ist dagegen anders. Quantisierung funktioniert dort nicht ganz so gut, UNet ist extrem rechenintensiv, und Batch-Bilderzeugung ist selbst für einzelne Nutzer effektiv und nützlich. Daher passt es besser zu GPUs/integrierten GPUs und profitiert stark von der Hackbarkeit der Python-Implementierung.
    Für Stable Diffusion halte ich den Ansatz, per Machine-Learning-Compiler ausführbare Dateien zu erzeugen, für richtig. AITemplate ist bereits sehr schnell https://github.com/VoltaML/voltaML-fast-stable-diffusion, und TVM Vulkan ist ebenfalls sehr vielversprechend, falls jemand eine Demo-Implementierung sauber fertigstellt https://github.com/mlc-ai/web-stable-diffusion.
    Außerdem bleibt der Großteil der Hackbarkeit einer reinen PyTorch-Implementierung erhalten.

    • Auch das obige Projekt unterstützt GPUs in gewissem Umfang, wenn man die richtigen GGML-Compiler-Flags übergibt.
      Zum Beispiel wird beim Kompilieren GGML_CUBLAS unterstützt, und gegenüber reinem C/C++ erzielt man eine ziemlich ordentliche Beschleunigung.
    • Umgekehrt ist es gut für Leute, die keine NVIDIA-GPU mit 6 GB oder mehr VRAM haben, aber lokal mit diesen neuronalen Netzen herumspielen möchten.
      Selbst wenn es etwas dauert, kann man es auf einem alten Laptop ausführen.
    • Wenn ich mich richtig erinnere, gab es auch mit torch.compile eine recht gute Beschleunigung, und ich meine, selbst daran gearbeitet zu haben.
      Ich werde prüfen, ob ich die Zahlen finden kann.
  • Dass sogar CLIP implementiert wurde, ist großartig.
    Es wäre schon cool, allein das herauszulösen und als WebAssembly-Implementierung zu kompilieren.
    Edit: Offenbar hat bereits jemand https://github.com/monatis/clip.cpp gebaut. Jetzt muss es nur noch zu WebAssembly gemacht werden.

    • Apropos CLIP: Seit OpenAI und Google in den Wettbewerbsmodus gewechselt sind, mache ich mir immer Sorgen, dass das nächste Modell auf CLIP-Niveau nicht veröffentlicht wird.
      Es ist schade, wenn man daran denkt, dass irgendwo in einem geheimen Tresor vielleicht bereits ein weiterentwickeltes Modell auf CLIP-Niveau liegt.
      Edit: Ich meine nicht CLIP-2, sondern einen Fortschritt von ähnlich großer Bedeutung wie CLIP.
  • Die Einrichtung war unglaublich einfach, daher habe ich es zum ersten Mal direkt ausprobiert.
    Ich frage mich, welche Geschwindigkeit ungefähr normal sein sollte.
    Unter Linux mit cmake .. -DGGML_OPENBLAS=ON auf einem AMD Ryzen 7 5700G ausgeführt, ohne dedizierte GPU, nur mit integrierter Grafik.
    Bei ./bin/sd -m ../models/sd-v1-4-ggml-model-f32.bin -p "a lovely cat" dauerte jeder Sampling-Schritt etwa 12 Sekunden, das gesamte Sampling dauerte 246,40 Sekunden.
    Ich frage mich, ob das die erwartbare Leistung ist.
    Edit: OpenBLAS war nicht installiert, daher hatte das Flag keine Wirkung.

    • Das ist gut. Im Grunde macht es das, was ich mir vor einem Jahr gewünscht hatte[0].
      Damals erforderten fast alle Lösungen einen Haufen Python-Abhängigkeiten, die Installation dauerte viel zu lange und scheiterte am Ende, weil der Speicherplatz ausging.
      Im Ernst: Es ersetzt buchstäblich mehrere Gigabyte Speicherplatz durch eine einzige 799-KB-Binärdatei. Als Bonus spart man mit dem offenbar schnellsten Q8_0-Format auch noch etwa 2,3 GB Daten.
      Allerdings scheint es außerhalb der Standardbildgröße 512x512 Bugs zu geben. Einige Größen wie 544x544 neigen zu Assert-Fehlern, Größen unter 512x512 erzeugen manchmal Schrottbilder, und unter 384x384 passiert das fast immer.
      [0] https://news.ycombinator.com/item?id=32555608
    • Man muss das Modell quantisieren, aber etwa 12 Sekunden pro Iteration klingen plausibel.
    • Nur CPU, 8-Bit-Quantisierung, Intel Core i7 4770S, 16 GB DDR3-RAM, 10 Jahre alter lüfterloser PC: 32 Sekunden pro Sampling-Schritt, und die Ausgabe ist in Ordnung.
  • C/C++-Implementierungen im KI-Bereich haben einen besonderen Reiz.
    Der Code wirkt sauber und intuitiv, und er lässt das ganze KI-Feld greifbar und erlernbar erscheinen.
    Liegt das daran, dass das Python-Ökosystem so chaotisch ist?

    • Neuschreiben erhöht im Allgemeinen die Codequalität, und Abhängigkeiten durch maßgeschneiderten Code zu ersetzen, der nur das Nötige tut, erhöht die Codequalität ebenfalls.
      Auch die Python-Version verwendet aus Geschwindigkeitsgründen C- und C++-Code, aber hier ist alles in einer einzigen Sprache geschrieben.
      Damit wirken drei Faktoren zusammen, die sauberen Code ermöglichen.
  • Es ist schön zu sehen, dass Leute aus dem Machine-Learning-Bereich sich von Python lösen und Sprachen verwenden, die Hardware optimal ausnutzen und keine spezielle Umgebung fürs Bauen und Ausführen erfordern

    • Das ist ein ziemlich seltsamer Vergleich.
      Zunächst einmal nutzt das Projekt im Originalbeitrag, ähnlich wie llama.cpp, keine GPU, während der Großteil des Python-Codes für Machine Learning GPUs nutzt. Es ist nicht schwer, Python-Code zu schreiben, der GPUs optimal ausnutzt. Man könnte eine GPU zwar als spezielle Umgebung fürs Bauen und Ausführen bezeichnen, aber für dieses Problem passt eine GPU deutlich besser.
      Zweitens wurde auch das Projekt im Originalbeitrag, ähnlich wie llama.cpp, erst dann mit effizientem und hochspezialisiertem Code umgesetzt, nachdem bestätigt war, dass bestimmte Modelle wie Stable Diffusion/LLaMA gut funktionieren. Python glänzt dagegen in der Prototyping-Phase, in der man das passende Modell noch nicht gefunden hat. Ein so einfaches und bequemes Prototyping habe ich in C++ bisher nicht gesehen.
      Ich will damit nicht die hervorragende Arbeit schmälern, die die Leute rund um llama.cpp im Bereich Machine Learning auf der CPU leisten. Die gelösten Probleme sind aber völlig unterschiedlich.
    • Es wäre viel besser, wenn es für jedes Machine-Learning-Modell eine einfache C-Inferenz-API gäbe, die man ohne Abhängigkeits- und Konfigurationschaos praktisch aus jeder Sprache und auf jeder Plattform direkt aufrufen könnte.
    • Die performancekritischen Komponenten im Machine-Learning-Stack sind tatsächlich auch nicht in Python implementiert.
      Intern war das schon lange alles CUDA, C und C++.
      Python ist nur ein sehr effektiver Klebstoff, der all das zusammenhält.
    • Ich bin den Leuten, die solche Arbeit machen, wirklich dankbar.
      Das ist die einzige Methode, mit der ich diese Modelle ohne Kopfschmerzen zum Laufen gebracht habe. Der Unterschied ist riesig. CUDA und Linux sind schon keine tolle Kombination, und AMD mit Windows ist erbärmlich. Vermutlich bin ich damit nicht allein.
    • Interessant ist, dass meine CPU einige davon in quantisierter Form fast so schnell wie eine GPU ausführen kann.
      War es am Ende alles nur ein Problem der Speicherbandbreite?
      Die GPU-Architektur besteht nicht nur aus Rechenleistung, sondern auch daraus, Arbeitsspeicher nahe an den Recheneinheiten zu platzieren. Jede Einheit hat lokalen Speicher, der mit dem globalen Speicher synchronisiert wird. Ist das ein wichtiger Grund, warum GPUs bei solchen Aufgaben so stark sind?
  • Es sieht nach C++ aus, warum wird es also als C/C++ bezeichnet?

    • Soweit ich es verstehe, ist die zugrunde liegende Abhängigkeit ggml in C geschrieben.
  • Ich habe mir dieses Repository heute angesehen, es heruntergezogen und auf dem Mac eine .dylib gebaut, dann mit Darts ffi-gen-Tool Bindings aus der bereitgestellten Header-Datei erzeugt.
    Ich experimentiere damit zusammen mit Flutter und nutze FFI, um keinen Subprozess starten zu müssen.
    Am Ende blieben starke Kopfschmerzen und eine kaputte App übrig. Morgen versuche ich es mit klarem Kopf noch einmal.
    Trotzdem ist das Repository selbst großartig, und auf einem M1 läuft es mit f16 in unter 10 Minuten.

  • Die Beispiele für verschiedene Quantisierungsstufen sind ziemlich beeindruckend.
    Der Wechsel von f16 zu q8_0 wirkt eher wie eine Änderung der Richtung als wie ein Qualitätsverlust. Das Ergebnis von q5_1 scheint kaum von q8_0 zu unterscheiden zu sein.
    Bei hochpräzisen Modellen verliert man zwar Determinismus, aber praktisch könnte es durchaus brauchbar sein.

  • Gibt es Benchmarks?

    • Einige Leute haben hier Zeiten gemessen, und je nach Quantisierung und Hardware scheint es etwa 15–20 Sekunden pro Iteration zu dauern.
      https://github.com/leejet/stable-diffusion.cpp/issues/1
    • Ich habe mit dem Befehl cmake .. -DGGML_CUBLAS=ON -DCMAKE_CUDA_COMPILER=/opt/cuda/bin/nvcc kompiliert und eine NVIDIA GeForce RTX 2060 SUPER verwendet.
      Das Modell habe ich nach FP16 konvertiert.
      Mit dieser Option liegt die Zeit pro Iteration zwischen 8,5 und 9 Sekunden, und die Gesamtzeit zum Erzeugen eines Bildes beträgt etwa 200 Sekunden.