KI-Modelle und Plattformen

SGLang: Effiziente Ausführung von strukturierten Sprachmodell-Programmen

mm
Unite.AI zu deinen bevorzugten Quellen auf Google hinzufügen

Große Sprachmodelle (LLMs) werden immer häufiger für komplexe Aufgaben eingesetzt, die mehrere Generationsschritte, erweiterte Prompting-Techniken, Kontrollfluss und strukturierte Eingaben/Ausgaben erfordern. Allerdings fehlen effiziente Systeme für die Programmierung und Ausführung dieser Anwendungen. SGLang, ein neu eingeführtes System, zielt darauf ab, dies zu lösen, indem es eine effiziente Ausführung von komplexen Sprachmodell-Programmen bietet. SGLang besteht aus einer Frontend-Sprache und einer Laufzeit. Die Frontend-Sprache vereinfacht die Programmierung mit Primitiven für Generation und Parallelismus-Kontrolle, während die Laufzeit die Ausführung durch neuartige Optimierungen wie RadixAttention für KV-Cache-Wiederverwendung und komprimierte endliche Automaten für schnelleres strukturiertes Ausgabe-Decoding beschleunigt. Experimente zeigen, dass SGLang bis zu 6,4-mal höhere Durchsatzraten im Vergleich zu State-of-the-Art-Inferenzsystemen auf verschiedenen großen Sprach- und Multimodal-Modellen erreicht, die Aufgaben wie Agenten-Kontrolle, logisches Denken, Few-Shot-Learning-Benchmarks, JSON-Decoding, Retrieval-augmented Generation-Pipelines und Multi-Turn-Chat bewältigen.

Die jüngsten Fortschritte in den Fähigkeiten von LLMs haben ihre Nützlichkeit erweitert, sodass sie eine breitere Palette von allgemeinen Aufgaben bewältigen und als autonome Agenten fungieren können. In diesen Anwendungen interagieren LLMs mit externen Umgebungen, nutzen Werkzeuge, mehrere Eingabemodi und verschiedene Prompting-Techniken wie Few-Shot-Learning, Selbstkonsistenz, Skelett-des-Gedankens und Baum-des-Gedankens. Diese neuen Anwendungsfälle erfordern häufig mehrere, oft abhängige, LLM-Generationsschritte, was auf eine Tendenz hinweist, Multi-Call-Strukturen zu verwenden, um komplexe Aufgaben zu bewältigen.

Dieser Wandel markiert einen Übergang von einfachen Chats zu einer komplexeren programmatischen Nutzung von LLMs, bei der Programme die Generierungsprozesse von LLMs planen und kontrollieren. Diese Programme werden als “Sprachmodell-Programme” (LM-Programme) bezeichnet. Erweiterte Prompting-Techniken und Agenten-Workflows fallen in den Bereich von LM-Programmen. Es gibt zwei gemeinsame Eigenschaften von LM-Programmen: (1) LM-Programme umfassen typischerweise mehrere LLM-Aufrufe, die mit Kontrollfluss kombiniert werden, um komplexe Aufgaben zu bewältigen und die Gesamtleistung zu verbessern. (2) LM-Programme erhalten strukturierte Eingaben und produzieren strukturierte Ausgaben, was die Zusammensetzung von LM-Programmen und die Integration in bestehende Softwaresysteme ermöglicht.

In diesem Artikel werden wir uns mit dem SGLang-Framework auseinandersetzen, seine Architektur erkunden, seine Leistung analysieren und es mit State-of-the-Art-Frameworks vergleichen. Also los geht’s.

Eine Einführung in SGLang

Trotz der weit verbreiteten Nutzung von LM-Programmen bleiben die aktuellen Systeme für ihre Ausdrucksweise und Ausführung ineffizient. SGLang identifiziert zwei primäre Herausforderungen im Zusammenhang mit der effizienten Nutzung von LM-Programmen:

  • Programmier-Komplexität: Die Entwicklung von LM-Programmen ist mühsam und schwierig aufgrund der nicht-deterministischen Natur von LLMs. Dies beinhaltet umfangreiche String-Manipulation, experimentelle Anpassung von Prompts, brüchige Ausgabe-Parsing, Umgang mit mehreren Eingabemodi und Implementierung von Parallelismus-Mechanismen. Diese Komplexität reduziert die Lesbarkeit von sogar einfachen Programmen erheblich.
  • Ausführungs-Ineffizienz: Die Ausführung von LM-Programmen ist ineffizient aufgrund redundanter Berechnungen und Speicherbedarfs. State-of-the-Art-Inferenz-Engines, die optimiert sind, um Latenz zu reduzieren und Durchsatz zu verbessern, haben keine direkte Kenntnis der Arbeitslast, was zu erheblichen Ineffizienzen führt. Ein bemerkenswertes Beispiel ist die Wiederverwendung des Key-Value-Caches, der aus wiederverwendbaren Zwischenergebnis-Tensoren für generative Inferenz besteht. Aktuelle Systeme haben keine effektiven Mechanismen, um die Wiederverwendung des KV-Caches über mehrere LLM-Aufrufe hinweg zu ermöglichen, die einen gemeinsamen Präfix teilen, was zu unnötigen Berechnungen und verschwendetem Speicher führt. Darüber hinaus ist die eingeschränkte Decodierung für strukturierte Ausgaben wie JSON-Modus suboptimal, da bestehende Systeme nur ein Token auf einmal decodieren.

Um diese Herausforderungen zu meistern, führt SGLang eine strukturierte Generierungs-Sprache für LLMs ein. Die Kernidee besteht darin, die Multi-Call-Struktur in LM-Programmen systematisch auszunutzen, um eine effiziente Ausführung zu ermöglichen. Wie in der folgenden Abbildung gezeigt, besteht SGLang aus zwei Teilen: einer Frontend-Sprache und einer Laufzeit.

Die Frontend-Sprache vereinfacht die Programmierung von LM-Programmen, und die Laufzeit beschleunigt ihre Ausführung. Diese Teile können zusammenarbeiten, um eine bessere Leistung zu erzielen, oder unabhängig voneinander funktionieren.

SGLang ist eine domänenspezifische Sprache, die in Python eingebettet ist, und bietet Primitiven für Generation (z.B. extend, gen, select) und Parallelismus-Kontrolle (z.B. fork, join). Es ist kompatibel mit Pythons Kontrollfluss und Bibliotheken, was es Benutzern ermöglicht, erweiterte Prompting-Workflows mit nativer Python-Syntax einfach zu entwickeln. SGLang umfasst einen Interpreter und einen Compiler. Der Interpreter verwaltet den Prompt-Zustand als Stream und übermittelt primitive Operationen an den Stream für asynchrone Ausführung, um eine ordnungsgemäße Kontrolle über Synchronisation und intra-Programm-Parallelismus zu gewährleisten. Darüber hinaus können SGLang-Programme nachverfolgt und für weitere Optimierungen kompiliert werden. Die Laufzeit von SGLang schlägt mehrere neuartige Optimierungen vor, um die Ausführung von LM-Programmen zu beschleunigen:

  • RadixAttention: Diese Technik ermöglicht die automatische Wiederverwendung des KV-Caches über mehrere Generationsschritte hinweg. In bestehenden Inferenz-Engines wird der KV-Cache eines Antrags nach der Verarbeitung verworfen, was die Wiederverwendung über mehrere Anträge hinweg verhindert und die Ausführung verlangsamt. SGLang verwaltet einen LRU-Cache des KV-Caches in einem Radix-Baum und verwaltet den KV-Cache wie einen herkömmlichen Cache, wobei der Radix-Baum für effizientes Matching, Einfügen und Löschen verwendet wird. Dies ermöglicht es der Laufzeit, verschiedene Wiederverwendungs-Muster effizient zu handhaben.
  • Komprimierter endlicher Automat: Diese Technik ermöglicht ein schnelleres eingeschränktes Decoding für strukturierte Ausgaben. Bestehende Systeme folgen den Einschränkungen nur für das nächste Token, was sie nur ein Token auf einmal decodieren lässt. Stattdessen analysiert SGLang die Einschränkungen und baut einen komprimierten endlichen Automaten auf, um sie darzustellen, wodurch ein Multi-Token-Pfad in einen einzelnen Schritt umgewandelt wird, wenn möglich, was die Decodierung mehrerer Token auf einmal für eine schnellere Geschwindigkeit ermöglicht.
  • API-Spekulative Ausführung: Für API-Modelle wie OpenAIs GPT-4 führt SGLang die API-spezifische Ausführung ein, um Multi-Call-Programme zu optimieren.

Mit SGLang wurden verschiedene LLM-Anwendungen implementiert, darunter Agenten-Kontrolle, logisches Denken, Few-Shot-Learning-Benchmarks, JSON-Decoding, Retrieval-augmented Generation-Pipelines, Multi-Turn-Chat und Multi-Modal-Verarbeitung. Die Leistung wurde auf Modellen wie Llama-7B/70B, Mistral-8x7B, LLaVA-v1.5-7B (Bild) und LLaVA-NeXT-34B (Video) auf NVIDIA (NVDA ) A10G- und A100-GPUs getestet. Experimentelle Ergebnisse zeigen, dass SGLang bis zu 6,4-mal höhere Durchsatzraten über eine breite Palette von Workloads, Modellen und Hardware-Konfigurationen hinweg erreicht, im Vergleich zu bestehenden Programmier- und Inferenz-Systemen, einschließlich Guidance, vLLM und LMQL.

SGLang: Programmier-Modell und Methodik

Das SGLang-Programmier-Modell wird durch ein laufendes Beispiel eingeführt, das seine Sprach-Primitiven und Ausführungs-Modi beschreibt und die Laufzeit-Optimierungs-Möglichkeiten skizziert. Dieses Modell vereinfacht mühsame Operationen in Multi-Call-Workflows (z.B. String-Manipulation, API-Aufrufe, Einschränkungs-Spezifikation, Parallelismus) durch flexible und komponierbare Primitiven. SGLang ist eine domänenspezifische Sprache, die in Python eingebettet ist. Die folgende Abbildung zeigt ein Programm, das einen Essay über ein Bild mit der branch-solve-merge-Prompting-Methode auswertet.

Die Funktion multi_dimensional_judge nimmt drei Argumente: `s`, `path` und `essay`. s verwaltet den Prompt-Zustand, path ist der Pfad des Bildes, und essay ist der Text des Essays. Neue Strings und SGLang-Primitiven können dem Zustand s für die Ausführung mithilfe des +=-Operators hinzugefügt werden. Zunächst fügt die Funktion das Bild und den Essay zum Prompt hinzu. Dann prüft es, ob der Essay mit dem Bild verknüpft ist, indem es select verwendet und das Ergebnis in s[“related”] speichert. Wenn verknüpft, wird der Prompt in drei Kopien für parallele Auswertung aus verschiedenen Dimensionen aufgeteilt, indem gen verwendet wird, um die Ergebnisse in f[“judgment”] zu speichern. Als Nächstes werden die Urteile zusammengeführt, eine Zusammenfassung generiert und ein Buchstaben-Grad zugewiesen. Schließlich gibt es die Ergebnisse im JSON-Format aus, das einem Schema entspricht, das durch einen regulären Ausdruck definiert ist regex. SGLang vereinfacht dieses Programm erheblich, da ein äquivalentes Programm mit einer OpenAI-ähnlichen Schnittstelle 2,1-mal so viele Codezeilen benötigen würde, aufgrund manueller String-Manipulation und Parallelismus-Kontrolle.

SGLang bietet Primitiven für die Kontrolle des Prompt-Zustands, der Generation und des Parallelismus, die mit Python-Syntax und -Bibliotheken verwendet werden können. Hier sind die Primitiven:

gen: ruft ein Modell auf, um zu generieren und speichert die Ergebnisse in einer Variable mit dem Namen, der in seinem ersten Argument angegeben ist. Es unterstützt ein regex-Argument, um die Ausgabe auf ein Grammatik-Format einzuschränken, das durch einen regulären Ausdruck definiert ist (z.B. ein JSON-Schema).

  • select: ruft ein Modell auf, um die höchste Wahrscheinlichkeits-Option aus einer Liste auszuwählen.
  • += oder extend: fügt einen String zum Prompt hinzu.
  • [Variable-Name]: ruft die Ergebnisse einer Generation ab.
  • fork: erstellt parallele Forks des Prompt-Zustands.
  • join: vereint den Prompt-Zustand.
  • image und video: nehmen Bild- und Video-Eingaben entgegen.

Die einfachste Möglichkeit, ein SGLang-Programm auszuführen, besteht darin, es durch einen Interpreter auszuführen, bei dem ein Prompt als asynchroner Stream behandelt wird. Primitiven wie extend, gen und select werden dem Stream für asynchrone Ausführung übermittelt. Diese nicht-blockierenden Aufrufe ermöglichen es dem Python-Code, ohne auf die Fertigstellung der Generation zu warten, weiterzulaufen, ähnlich wie bei der asynchronen Ausführung von CUDA-Kernels. Jeder Prompt wird von einem Stream-Executor in einem Hintergrund-Thread verwaltet, was intra-Programm-Parallelismus ermöglicht. Das Abrufen von Generationsergebnissen blockiert, bis sie bereit sind, was eine ordnungsgemäße Synchronisation gewährleistet. Alternativ können SGLang-Programme als Rechennetze kompiliert und mit einem Graph-Executor ausgeführt werden, was weitere Optimierungen ermöglicht. Diese Arbeit verwendet den Interpreter-Modus standardmäßig und diskutiert die Ergebnisse des Compiler-Modus im Anhang D. SGLang unterstützt offene Modelle mit seiner eigenen SGLang-Laufzeit (SRT) sowie API-Modelle wie OpenAI und Anthropic.

Programmier-Systeme für LLMs können in Hoch- und Niedrig-Level-Systeme unterteilt werden (z.B. LangChain, DSPy und LMQL, Guidance, SGLang). Hoch-Level-Systeme bieten vordefinierte oder automatisch generierte Prompts, wie DSPys Prompt-Optimizer. Niedrig-Level-Systeme ändern Prompts typischerweise nicht, sondern ermöglichen die direkte Manipulation von Prompts und Primitiven. SGLang ist ein Niedrig-Level-System, ähnlich wie LMQL und Guidance. Die folgende Tabelle vergleicht ihre Funktionen.

SGLang konzentriert sich stärker auf Laufzeit-Effizienz und kommt mit seiner eigenen ko-designten Laufzeit, die neuartige Optimierungen ermöglicht. Hoch-Level-Sprachen (z.B. DSPy) können in Niedrig-Level-Sprachen (z.B. SGLang) kompiliert werden. Die Integration von SGLang als Backend in DSPy für bessere Laufzeit-Effizienz wird später demonstriert.

Das obige Beispiel zeigt RadixAttention-Operationen mit einer LRU-Verdrängungs-Strategie über neun Zeitpunkte, wobei die dynamische Zuteilung und Verdrängung von Knoten in Reaktion auf verschiedene Anfragen veranschaulicht wird. Diese Anfragen umfassen zwei Chat-Sitzungen, eine Charge von Few-Shot-Learning-Anfragen und Self-Consistency-Sampling. Jede Baumkante trägt eine Beschriftung, die einen Teilstring oder eine Sequenz von Token darstellt. Die Knoten sind farblich kodiert, um verschiedene Zustände zu reflektieren: grün für neu hinzugefügte Knoten, blau für zwischengespeicherte Knoten, die während des Zeitpunkts zugegriffen werden, und rot für Knoten, die verdrängt wurden.

Schritt 1: Der Radix-Baum ist zunächst leer.

Schritt 2: Der Server verarbeitet eine eingehende Benutzer-Nachricht “Hallo” und antwortet mit der LLM-Ausgabe “Hi”. Der System-Prompt “Sie sind ein hilfreicher Assistent”, die Benutzer-Nachricht “Hallo!” und die LLM-Antwort “Hi!” werden zu einem einzigen Edge zusammengefasst, das mit einem neuen Knoten verknüpft ist.

Schritt 3: Eine neue Anfrage wird eingegangen, und der Server findet den Präfix der Anfrage (d.h. die erste Runde des Gesprächs) im Radix-Baum und wiederverwendet dessen KV-Cache. Die neue Runde wird als neuer Knoten dem Baum hinzugefügt.

Schritt 4: Eine neue Chat-Sitzung beginnt. Der Knoten aus Schritt 3 wird in zwei Knoten aufgeteilt, um es beiden Chat-Sitzungen zu ermöglichen, den System-Prompt zu teilen.

Schritt 5: Die zweite Chat-Sitzung wird fortgesetzt. Aufgrund von Speicher-Beschränkungen muss jedoch ein Knoten aus Schritt 4 verdrängt werden. Die neue Runde wird nach dem verbleibenden Knoten aus Schritt 4 hinzugefügt.

Schritt 6: Der Server erhält eine Few-Shot-Learning-Anfrage, verarbeitet sie und fügt sie dem Baum hinzu. Der Wurzel-Knoten wird geteilt, da die neue Anfrage keinen gemeinsamen Präfix mit bestehenden Knoten hat.

Schritt 7: Der Server erhält eine Charge von zusätzlichen Few-Shot-Learning-Anfragen. Diese Anfragen teilen die gleiche Menge von Few-Shot-Beispielen, sodass ein Knoten aus Schritt 6 geteilt wird, um die gemeinsame Nutzung zu ermöglichen.

Schritt 8: Der Server erhält eine neue Nachricht von der ersten Chat-Sitzung. Er verdrängt alle Knoten der zweiten Chat-Sitzung, da sie am wenigsten kürzlich verwendet wurden.

Schritt 9: Der Server erhält eine Anfrage, mehr Antworten für die Fragen in einem Knoten aus Schritt 8 zu sampeln, wahrscheinlich für Self-Consistency-Prompting. Um Platz für diese Anfragen zu schaffen, werden mehrere Knoten verdrängt.

Dieses Beispiel zeigt, wie RadixAttention die dynamische Zuteilung und Verdrängung von Knoten in Reaktion auf verschiedene Anfragen handhabt, um eine effiziente KV-Cache-Wiederverwendung und Speicher-Verwaltung zu gewährleisten.

SGLang: Bewertung und Ergebnisse

Ergebnisse auf offenen Modellen

Die Latenz- und Durchsatz-Ergebnisse sind in den folgenden Abbildungen dargestellt. SGLang verbessert den Durchsatz um bis zu 6,4-mal und reduziert die Latenz um bis zu 3,7-mal. Diese Verbesserungen resultieren aus der Wiederverwendung des KV-Caches, der Ausnutzung von Parallelismus innerhalb eines einzelnen Programms und der schnelleren eingeschränkten Decodierung.

In diesen Benchmarks liegt die Cache-Treffer-Rate zwischen 50% und 99%. Abbildung 13 (Anhang) listet die erreichten und optimalen Cache-Treffer-Raten für alle auf, was zeigt, dass SGLangs cache-bewusste Planung im Durchschnitt 96% der optimalen Treffer-Rate erreicht.

Ergebnisse auf größeren Modellen mit Tensor-Parallelismus

Größere Modelle, Mixtral-8x7B und Llama-70B, wurden mit Tensor-Parallelismus auf dem gleichen Satz von Benchmarks getestet, und die Ergebnisse werden in der folgenden Abbildung berichtet. Die Beschleunigung auf größeren Modellen zeigt eine ähnliche Tendenz wie die auf kleineren Modellen, was darauf hinweist, dass SGLangs Optimierung gut auf größere Modelle verallgemeinert.

Ergebnisse auf Multi-Modal-Modellen

SGLang hat native Unterstützung für Multi-Modal-Modelle mit den Bild- und Video-Primitiven. Die Optimierungen in diesem Artikel sind mit Multi-Modal-Modellen kompatibel. Für RadixAttention wird der Hash der Eingabe-Bilder berechnet und als Schlüssel im Radix-Baum verwendet, was die Wiederverwendung des KV-Caches der Bild-Tokens aus dem gleichen Bild ermöglicht. LLaVA-v1.5-7B (Bild) wurde auf llava-bench-in-the-wild und LLaVA-NeXT-34B (Video) auf ActivityNet ausgeführt. Da diese Modelle von anderen Baseline-Systemen nicht gut unterstützt werden, wurde die ursprüngliche Implementierung der Modell-Autoren in Hugging Face Transformers als Baseline verwendet. Wie in der folgenden Tabelle gezeigt, bietet SGLang einen Durchsatz von bis zu 6-mal höher auf diesen Benchmarks. In llava-bench-in-the-wild wurden mehrere Fragen zu dem gleichen Bild bearbeitet, und die SGLang-Laufzeit wiederverwendete den KV-Cache in diesem Fall.

Produktions-Einsatz

SGLang wurde in Chatbot Arena für offene Modelle eingesetzt. Aufgrund geringer Traffic für einige Modelle dient nur ein SGLang-Worker jedem. Nach einem Monat wurde eine RadixAttention-Cache-Treffer-Rate von 52,4% für LLaVA-Next-34B und 74,1% für Vicuna-33B beobachtet. Cache-Treffer kamen von gemeinsamen System-Nachrichten, häufig wiederverwendeten Beispiel-Bildern und Multi-Turn-Chat-Verlaufsprotokollen. Dies reduzierte die Latenz für das erste Token um durchschnittlich 1,7-mal für Vicuna-33B.

Abschließende Gedanken

In diesem Artikel haben wir über SGLang gesprochen, ein neu eingeführtes System, das darauf abzielt, die effiziente Ausführung von komplexen Sprachmodell-Programmen zu ermöglichen. SGLang besteht aus einer Frontend-Sprache und einer Laufzeit. Die Frontend-Sprache vereinfacht die Programmierung mit Primitiven für Generation und Parallelismus-Kontrolle, während die Laufzeit die Ausführung durch neuartige Optimierungen wie RadixAttention für KV-Cache-Wiederverwendung und komprimierte endliche Automaten für schnelleres strukturiertes Ausgabe-Decoding beschleunigt. Experimente zeigen, dass SGLang bis zu 6,4-mal höhere Durchsatzraten im Vergleich zu State-of-the-Art-Inferenz-Systemen auf verschiedenen großen Sprach- und Multimodal-Modellen erreicht, die Aufgaben wie Agenten-Kontrolle, logisches Denken, Few-Shot-Learning-Benchmarks, JSON-Decoding, Retrieval-augmented Generation-Pipelines und Multi-Turn-Chat bewältigen.

Ein Ingenieur von Beruf, ein Schriftsteller von Herzen. Kunal ist ein technischer Schriftsteller mit einer tiefen Liebe und einem tiefen Verständnis für KI und ML, der sich der Aufgabe widmet, komplexe Konzepte in diesen Bereichen durch seine ansprechenden und informativen Dokumentationen zu vereinfachen.