- Um die großen Abhängigkeiten und den Bereitstellungsaufwand Python-zentrierter Inferenz-Stacks zu verringern, wird eine Rust+Wasm-Llama2-App als 2-MB-Binärdatei paketiert und auf verschiedenen Geräten ausgeführt.
- Die Implementierung ist eine an Wasm angepasste Form von llama.cpp und nutzt GGUF-Modelldateien, das GGML-Plugin von WasmEdge sowie die WASI-NN-API, um lokale Hardwarebeschleunigung zu verwenden.
- Das Beispiel verwendet das 5-Bit-quantisierte Modell Llama-2-7B-Chat GGUF und erreicht etwa 25 Token/s auf einem günstigen M2 MacBook sowie etwa 50 Token/s auf einer Nvidia A10G.
- Die WasmEdge-Laufzeit kann zusammen mit Container-Tools verwendet werden, sodass sich dieselbe Wasm-Binärdatei einfach auf Edge-Geräten, On-Premises und in der Cloud bereitstellen lässt.
- Die GGML-Tools sind bereits nutzbar, befinden sich aber noch in einer frühen Phase; es fehlen noch mehr Hardware- und OS-Plugins, Unterstützung für llama.cpp-Konfigurationen sowie Unterstützung der WASI-NN-API in mehreren Wasm-kompatiblen Sprachen.
Llama2-Inferenz mit Rust+Wasm ausführen
- Der Rust+Wasm-Stack kann bei AI-Inferenz als Alternative zu Python dienen.
- Rust+Wasm-Apps können im Vergleich zu Python 1/100 der Größe haben, 100-mal schneller sein und dieselbe Binärdatei ohne Codeänderungen in verschiedenen Umgebungen mit Hardwarebeschleunigung nutzen.
- Second State hat ein einfaches Rust-Programm erstellt, das Llama2-Modellinferenz mit nativer Geschwindigkeit ausführt.
- Die zu Wasm kompilierte Binär-App ist 2 MB groß.
- Diese Wasm-App ist auf Geräte mit heterogenen Hardwarebeschleunigern portierbar.
- Die WasmEdge-Laufzeit bietet eine sichere Ausführungsumgebung in Cloud-Umgebungen.
- WasmEdge kann zusammen mit Container-Tools verwendet werden, um portable Apps auf verschiedenen Geräten zu orchestrieren und auszuführen.
Basierend auf llama.cpp und GGUF
- Diese Arbeit basiert auf llama.cpp, das von Georgi Gerganov entwickelt wurde.
- Das ursprüngliche C++-Programm wurde so angepasst, dass es in Wasm läuft.
- Die Modelldateien verwenden das GGUF-Format.
Ausführungsschritte
- Auf einem Linux- oder Mac-Gerät WasmEdge und das GGML-Plugin installieren.
curl -sSf https://raw.githubusercontent.com/WasmEdge/WasmEdge/… | bash -s -- --plugins wasmedge_rustls wasi_nn-ggml
- Die vorab gebaute Wasm-App herunterladen.
curl -LO https://github.com/LlamaEdge/LlamaEdge/…
- Das Beispiel verwendet eine GGUF-Datei des auf 5-Bit-Gewichte quantisierten Llama2-7B-Chat-Modells.
curl -LO https://huggingface.co/second-state/Llama-2-7B-Chat-GGUF/…
- Wenn die Wasm-Inferenz-App mit WasmEdge ausgeführt und das GGUF-Modell übergeben wird, können Fragen interaktiv eingegeben werden.
wasmedge --dir .:. --nn-preload default:GGML:AUTO:Llama-2-7b-chat-hf-Q5_K_M.gguf llama-chat.wasm
Modellkonfiguration und Leistungsbeispiele
llama-chat.wasm konfiguriert per Kommandozeilenoptionen, wie mit dem Modell interagiert wird.
--ctx-size: Größe des Prompt-Kontexts, Standardwert 512
--n-predict: Anzahl der vorherzusagenden Tokens, Standardwert 1024
--n-gpu-layers: Anzahl der auf der GPU auszuführenden Layer, Standardwert 100
--batch-size: Batch-Größe für die Prompt-Verarbeitung, Standardwert 512
--temp: Sampling-Temperatur, Standardwert 0.8
--repeat-penalty: Strafwert für wiederholte Tokens, Standardwert 1.1
--prompt-template: unterstützt llama-2-chat, codellama-instruct, mistral-instruct-v0.1, chatml, deepseek-chat, deepseek-coder usw.
--log-stat: Ausgabe von Statistiken
- Ein Ausführungsbeispiel mit Kontextlänge 2048, maximal 512 Antwort-Tokens und aktivierter Statistik-Ausgabe sieht so aus:
wasmedge --dir .:. --nn-preload default:GGML:AUTO:Llama-2-7b-chat-hf-Q5_K_M.gguf llama-chat.wasm -c 2048 -n 512 --log-stat
- Auf einem günstigen M2 MacBook wird die LLM-Antwort standardmäßig als Stream ausgegeben und mit etwa 25 Token/s erzeugt.
- Ein Log-Beispiel zeigt auf Basis von
eval time bei 82 Ausführungen 25.64 Token/s.
kv self size wird als 1024.00MB und compute buffer total size als 630.14MB angezeigt.
- Auf einer Nvidia-A10G-Hardware läuft es mit etwa 50 Token/s.
- Dabei wird CUDA-GPU-Beschleunigung verwendet und 35/35 Layer werden auf die GPU ausgelagert.
- Die VRAM-Nutzung wird als Modell 4474.93MB, Kontext 1648.02MB, insgesamt 6122.95MB angezeigt.
- Auf Basis von
eval time werden bei 83 Ausführungen 50.55 Token/s erreicht.
OpenAI-kompatibler API-Server und LLM-Apps
- Es wird auch ein mit Rust und WasmEdge erstellter OpenAI-kompatibler API-Server bereitgestellt.
- Mit diesem Server lassen sich mit OpenAI-kompatiblen Entwickler-Tools wie flows.network LLM-Agenten und Apps erstellen.
- Eine zugehörige Erklärung findet sich in Build a super lightweight AI agent.
Grenzen des Python-Inferenz-Stacks
- LLMs wie Llama2 werden meist mit Python-basierten Frameworks wie PyTorch, Tensorflow und JAX trainiert.
- Für Inferenzanwendungen, die rund 95 % des AI-Computings ausmachen, wird Python jedoch als nachteilig angesehen.
- Python-Pakete können wegen komplexer Abhängigkeiten schwierig einzurichten und zu verwenden sein.
- Python- oder PyTorch-Docker-Images sind oft mehrere GB oder sogar Dutzende GB groß, was bei AI-Inferenz auf Edge-Servern oder Geräten eine große Belastung sein kann.
- Python ist im Vergleich zu kompilierten Sprachen wie C, C++ und Rust sehr langsam und kann bis zu 35.000-mal langsamer sein.
- Der Großteil realer Workloads wird an native Shared Libraries unterhalb der Python-Wrapper delegiert.
- Diese Struktur eignet sich gut für Demos, gilt aber als schwer intern an geschäftsspezifische Anforderungen anpassbar.
- Die starke Abhängigkeit von nativen Bibliotheken und das komplexe Abhängigkeitsmanagement verringern die Portabilität von Python-AI-Programmen, die gerätespezifische Hardwarefunktionen nutzen.
- Es gibt auch Fälle, in denen häufig im LLM-Toolchain verwendete Python-Pakete wegen Anforderungen an die
pydantic-Version miteinander kollidieren.
llama-cpp-python verlangt pydantic 2.0.1 und funktioniert nicht mit <=2.0.
fastapi und chromadb verlangen pydantic 1.9.2 und funktionieren nicht mit >=2.0.
Vorteile von Rust+Wasm
- Der Rust+Wasm-Stack kann als einheitliche Cloud-Computing-Infrastruktur für Geräte, Edge-Cloud, On-Premises-Server und Public Cloud dienen.
- Für AI-Inferenzanwendungen kann er eine starke Alternative zum Python-Stack sein.
-
Ultraleicht
- Die Inferenzanwendung ist inklusive aller Abhängigkeiten 2 MB groß.
- Das ist weniger als 1 % der Größe eines typischen PyTorch-Containers.
-
Schnelle Ausführung
- Über Vorverarbeitung, Tensor-Berechnung und Nachverarbeitung hinweg sind native C/Rust-Geschwindigkeiten möglich.
-
Portabilität
- Dieselbe Wasm-Bytecode-Anwendung läuft auf den wichtigsten Computing-Plattformen.
- Auch heterogene Hardwarebeschleunigung wird unterstützt.
-
Einfachere Einrichtung, Entwicklung und Bereitstellung
- Komplexe Abhängigkeiten werden reduziert.
- Eine einzelne Wasm-Datei kann mit Standard-Tools auf einem Notebook gebaut und in mehrere Umgebungen bereitgestellt werden.
-
Sicherheit und Cloud-Tauglichkeit
- Die Wasm-Laufzeit ist darauf ausgelegt, nicht vertrauenswürdigen Benutzercode zu isolieren.
- Sie kann mit Container-Tools verwaltet und auf Cloud-nativen Plattformen bereitgestellt werden.
Struktur des Rust-Inferenzprogramms
- Das Demo-Inferenzprogramm ist in Rust geschrieben und zu Wasm kompiliert.
- Der zentrale Rust-Quellcode umfasst etwa 40 Zeilen.
- Das Rust-Programm übernimmt die folgenden Aufgaben:
- Verwaltung der Benutzereingaben
- Nachverfolgung des Gesprächsverlaufs
- Umwandlung von Text in das Llama2-Chat-Template
- Ausführung der Inferenz über die WASI-NN-API
- Für einen direkten Build müssen der Rust-Compiler und das Kompilierungsziel
wasm32-wasi installiert werden.
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
rustup target add wasm32-wasi
- Danach das Quellprojekt herunterladen und die Wasm-Datei mit
cargo bauen.
git clone https://github.com/second-state/llama-utils
cd llama-utils/chat/
cargo build --target wasm32-wasi --release
cp target/wasm32-wasi/release/llama-chat.wasm .
Deployment in Cloud und am Edge
- Sobald die Wasm-Bytecode-Datei vorliegt, kann sie auf jedem Gerät bereitgestellt werden, das die WasmEdge-Laufzeit unterstützt.
- Aktuell unterstützt das GGML-Plugin generisches Linux und Ubuntu Linux.
- x86- und ARM-CPU
- Nvidia-GPU
- Apple M1/M2/M3
- Das WasmEdge-GGML-Plugin nutzt auf Basis von llama.cpp automatisch die Hardwarebeschleunigung des Geräts.
- Wenn eine Nvidia-GPU vorhanden ist, installiert das Installationsprogramm automatisch das CUDA-optimierte GGML-Plugin.
- Das GGML-Plugin für Mac OS verwendet die Metal-API, um Inferenz-Workloads auf der integrierten neural processing engine von M1/M2/M3 auszuführen.
- Der Linux-CPU-Build verwendet OpenBLAS, um moderne CPU-Rechenfunktionen wie AVX und SIMD automatisch zu erkennen und zu nutzen.
- Dieser Ansatz zielt darauf ab, Portabilität zwischen heterogener AI-Hardware und Plattformen zu erreichen, ohne Leistung aufzugeben.
Weitere Arbeiten
- Die WasmEdge-GGML-Tools sind derzeit nutzbar und werden von Cloud-Native-Kunden verwendet, befinden sich aber noch in einer frühen Phase.
- Mögliche Beiträge gibt es in folgenden Bereichen:
- Hinzufügen von GGML-Plugins für mehr Hardware- und OS-Plattformen
- Interesse besteht an TPU, ARM-NPU sowie spezialisierten AI-Chips unter Linux und Windows.
- Unterstützung für mehr llama.cpp-Konfigurationen
- Derzeit kann nur ein Teil der Konfigurationsoptionen aus Wasm an das GGML-Plugin übergeben werden.
- Ziel ist die Unterstützung aller von GGML angebotenen Optionen.
- Unterstützung der WASI-NN-API in anderen Wasm-kompatiblen Sprachen
- Interesse besteht an Go, Zig, Kotlin, JavaScript, C und C++.
Unterstützung für andere Modelle als LLMs
- Mit WasmEdge und WASI-NN lassen sich als leichtgewichtige, schnelle, portable und sichere Python-Alternative auch Inferenz-Apps auf Basis populärer AI-Modelle außerhalb von LLMs erstellen.
- mediapipe-rs bietet eine Rust+Wasm-API für Googles mediapipe-Familie von Tensorflow-Modellen.
- WasmEdge YOLO ist ein Rust+Wasm-API-Projekt für PyTorch-Modelle von YOLOv8.
- WasmEdge ADAS demo ist ein Beispiel, das mit Intel-OpenVINO-Modellen Straßensegmentierung für autonome Fahrzeuge durchführt.
- WasmEdge Document AI soll eine Rust+Wasm-API für OCR und eine Familie von Dokumentverarbeitungsmodellen bereitstellen.
- Diskussionen und Beiträge zu WasmEdge sind im WasmEdge Discord möglich.
1 Kommentare
Hacker-News-Kommentare
Rust und WASM sind gut, aber wenn man sich den Code ansieht, sind es nur 150 Zeilen eines einfachen Rust-Kommandozeilenskripts.
Die schwere Arbeit erledigt eine Zeile, die das Modell an das WASI-NN-Backend übergibt; hier wird es von der WasmEdge-Runtime bereitgestellt.
Allerdings ist WasmEdge nicht Rust, sondern C++, und in diesem Fall bringt Rust kaum Vorteile; das Backend ließe sich auch aus anderen Sprachen wie Python aufrufen.
Python und PyTorch so zu bündeln, dass Endnutzer es per Doppelklick ausführen können, ist immer noch beinahe ein Chaos. Der eigentliche High-Performance-Code ist auf beiden Seiten C++, aber wenn man statt mehr als 2 GB und Zehntausenden Dateien nur dafür ausliefern zu müssen, ein paar Befehle an dieses C++ zu schicken, mit einer 2-MB-Executable auskommt, ist das sinnvoll.
Großartige Arbeit. Wer es ausprobieren will, muss allerdings weiterhin die großen Gewichtsdateien herunterladen.
Im Grunde ist das ein vollständig portables, abhängigkeitsfreies llama.cpp in 2 MB.
Für App-Entwickler könnte das die einfachste Methode sein, die Inferenz-Engine in eine auslieferbare Datei zu packen. Die Gewichte sind bereits portabel und können bei Bedarf heruntergeladen werden; der Teil, den man wirklich festnageln möchte, ist die Inferenz-Engine.
Wie gesagt: Die Gewichte überragen diese Größe bei Weitem.
main-Datei, die llama.cpp auf meiner Maschine baut, ist 1,2 MB groß.Die Größe von 2 MB ist an sich nicht besonders beeindruckend; der Kernpunkt ist, dass es durch das Targeting von wasm portabler wird, nicht dass es besonders stärker komprimiert wäre.
Ist das am Ende nicht einfach ein Wrapper um llama.cpp? Ehrlich gesagt bin ich ziemlich müde von Projekten, die
x.cppeinwickeln.Ich habe in den letzten 6 Monaten an einem Rust- + WebGPU-Machine-Learning-Framework gearbeitet und sehr schnell erkannt, wie beeindruckend GGs Arbeit ist.
Es ist noch in einem frühen Stadium, aber man kann es hier sehen:
https://www.ratchet.sh/
https://github.com/FL33TW00D/whisper-turbo
Das wasm-nn, von dem das hier abhängt, also https://github.com/WebAssembly/wasi-nn, ist ein Vorschlag, bei dem beliebige Chunks an eine Vendor-Implementierung geschickt werden. Auch die API ist im Grunde nur Eingabe setzen, Berechnung, Ausgabe setzen.
Deshalb ist das überhaupt nicht portabel.
Dass es funktioniert, liegt daran, dass es sich auf die Abstraktionen stützt, die bereits in llama.cpp implementiert sind. Hier sieht man, dass WasmEdge genau den Code übernommen hat, der gguf-Modelle auf verschiedene Hardware-Targets abbildet: https://github.com/WasmEdge/WasmEdge/tree/master/plugins/was...
Daher ist die Beschreibung nach dem Motto „Entwickler können Bindings nutzen, um ML-Apps in High-Level-Sprachen zu schreiben, nach WebAssembly zu kompilieren und sie dann in einer wasi-nn-fähigen Runtime wie WasmEdge auszuführen“ völlig falsch. In der Praxis geht das so nicht.
Das ist weder portabel, noch eine Sandbox, noch eine Hardware-Abstraktionsschicht.
Selbst wenn man ein wasm-Binary hat, kann es nur laufen, wenn die verwendete Runtime-Version zufällig genau das benötigte ggml-Backend implementiert; eine solche Anforderung gibt es überhaupt nicht, also wird das meistens vermutlich nicht der Fall sein.
Und selbst wenn es geht, ruft es letztlich den ggml-Code von llama.cpp auf, sodass die Sicherheit nur so gut ist wie diese Bibliothek.
Der Artikel betont „Portabilität“ und „Rust“ viel zu stark, aber tatsächlich kommt der Vorteil von keinem von beiden wirklich zur Geltung.
Angenommen, es gibt auf neuer Hardware eine WASI-Runtime: Kann man das Modell ausführen? Gibt es GPU-Support? Die Antwort lautet dann: „Prüfe, ob llama.cpp auf dieser Plattform mit GPU-Support kompiliert wird, ob die verwendete Runtime ein ggml-Plugin hat und ob darin die passende Version von ggml vendored ist. Sonst nicht.“
Wozu dann überhaupt WASI verwenden?
Cross-Platform-GPU-Support ist sicher schwierig, aber das hier wirkt ziemlich absurd.
Stell dir vor, WebGPU würde so funktionieren: „Wirf einen Binary-Chunk auf die GPU, und wenn es zufällig ein Chunk ist, der zur aktuellen Hardware passt, zeichnet sie vielleicht etwas.“ Genau diese Struktur ist das hier.
Kann man das offline auf einem iPhone laufen lassen? Dann könnte man es beim Camping unabhängig vom Empfang ungefähr wie eine einfache Internetsuche nutzen.
Allerdings braucht man weiterhin mehrere GB Arbeitsspeicher, um das Modell selbst zu laden.
[0] https://github.com/mlc-ai/mlc-llm
Anfangs habe ich sie mit ggml gebaut, später mlc-llm entdeckt und sie darauf portiert.
[0]: https://apps.apple.com/us/app/private-llm/id6448106860
Im Lauf der Zeit werden sicher weitere Methoden entstehen, um Transformer-Architekturen am Edge effizienter und schneller auszuführen, aber die VRAM-Anforderungen lassen sich nicht mit Rust lösen, daher scheint man hier nahe an eine Grenze zu kommen.
Genau das ist der wichtigste Flaschenhals, wenn man ausreichend große Modelle lädt.
Man kann zwar sagen: „Kleine Modelle werden besser, schaut euch Mistral und Llama 2 an“, aber auch kleine Modelle nähern sich Kapazitätsgrenzen. Es gibt Grenzen dafür, wie viel Information man in 7 Milliarden Parameter packen kann.
Ich glaube nicht, dass dieser KI-Ansatz zu AGI führen wird. Er fühlt sich viel zu ineffizient an.
Die Aussage „Das GGML-Plugin für Mac OS verwendet die Metal API, um Inferenzaufgaben auf der integrierten Neural Processing Engine von M1/M2/M3 auszuführen“ scheint nicht korrekt zu sein.
Dass GGML die Metal API verwendet, bedeutet, dass es auf der GPU des M1/2/3 läuft, nicht auf der Neural Engine.
Das ist natürlich auch für sich genommen gut, aber genau genommen ist es so.
Ich mag dieses Clickbait-Marketing nicht, bei dem so getan wird, als würde ein Projekt die Größe gegenüber anderen Lösungen auf 1/100 reduzieren oder die Geschwindigkeit um das 100- bis 35.000-Fache steigern, nur weil ein Wrapper um die Kernbibliothek in einer anderen Sprache geschrieben ist.
Dabei werden auch die Tooling-Ökosysteme und die Community-Expertise rund um andere Lösungen völlig ignoriert.
Zunächst einmal basiert dieses Projekt auf llama.cpp[1], und die schwere Arbeit, mehrere GB große Modelldateien in GPU/CPU zu laden und auszuführen, übernimmt llama.cpp.
Die Inferenzgeschwindigkeit wird nicht durch die Wahl des Wrappers begrenzt; es gibt auch Wrapper für Go, Python, Node und Rust, und man kann llama.cpp auch direkt verwenden.
Auch die Binärgröße ist nicht besonders wichtig. Übliche quantisierte Modelldateien liegen im Bereich von 5 GB bis 40 GB, und man braucht eine starke GPU oder eine Maschine mit 16 bis 64 GB RAM.
[1] https://github.com/ggerganov/llama.cpp
Wenn ein großer Teil der Größe letztlich aus den trainierten Modellgewichten besteht, wie kann man die Größe dann ohne Genauigkeitsverlust um mehrere Größenordnungen reduzieren?
Die hier genannten 2 MB scheinen sich auf die Programmgröße ohne Modell zu beziehen. Es sieht nach einem Rust-Server aus, der wasm und llama.cpp ausführt und darüber llama.cpp laufen lässt.
Ich mag das kleine
llama.cpp/examples/serverund bette es in FreeChat ein, aber mehr Tool-Optionen sind immer gut.Ich habe nachgesehen: Die arm64/x86-Executables, die ich einbette, sind derzeit 4,2 MB groß. FreeChat ist 12,1 MB groß, aber das Standardmodell hat etwa 3 GB, daher mache ich mir wegen 2 MB Unterschied nicht viele Gedanken.
[0]: https://github.com/ggerganov/llama.cpp/tree/master/examples/...
In diesem Artikel geht es darum, die Größe der Inferenz-App um den Faktor 100 zu reduzieren.
Ich sehe nicht, warum man das statt direkter Nutzung von llama.cpp verwenden sollte.
Im Artikel wird außerdem erklärt, dass auf Geräten mit heterogenen Hardware-Beschleunigern Hardware-Beschleunigung genutzt wird.
Das bedeutet, dass ein zu wasm kompiliertes Programm auf verschiedenen Geräten unterschiedliche Hardwareressourcen wie GPUs oder spezialisierte KI-Chips effizient nutzen kann.
Bei einer direkten Implementierung in C++ könnten je nach Hardwaretyp separate Optimierungen oder Versionen nötig sein, um ähnliche Performance zu erreichen.