Connect with us

SGLang: Wydajna Wykonywanie Programów Modeli Językowych o Strukturyzowanej Strukturze

Sztuczna inteligencja

SGLang: Wydajna Wykonywanie Programów Modeli Językowych o Strukturyzowanej Strukturze

mm
SGLang: Efficient Execution of Structured Language Model Programs

Duże modele językowe (LLM) są coraz częściej wykorzystywane do zadań złożonych wymagających wielu wywołań generacyjnych, zaawansowanych technik promptowania, sterowania przepływem i strukturyzowanych danych wejściowych/wyjściowych. Jednak wydajne systemy do programowania i wykonywania tych aplikacji są niedostateczne. SGLang, nowo wprowadzony system, ma za zadanie rozwiązać ten problem, zapewniając wydajne wykonywanie złożonych programów modeli językowych. SGLang składa się z frontendu i środowiska wykonawczego. Frontend ułatwia programowanie dzięki prymitywom generacji i kontroli równoległości, podczas gdy środowisko wykonawcze przyspiesza wykonywanie dzięki nowym optymalizacjom, takim jak RadixAttention do ponownego wykorzystania pamięci podręcznej KV i sprężone maszyny stanowe dla szybszego dekodowania strukturyzowanych danych wyjściowych. Eksperymenty dowodzą, że SGLang osiąga nawet 6,4-krotnie wyższą wydajność w porównaniu z systemami inferencyjnymi na różnych dużych modelach językowych i multimodalnych, rozwiązując zadania takie jak kontrola agenta, rozumowanie logiczne, benchmarki uczenia few-shot, dekodowanie JSON, potok generacji z uzupełnieniem i rozmowę wieloobrotową.

Ostatnie postępy w możliwościach LLM rozszerzyły ich przydatność, umożliwiając im obsługę szerszego zakresu zadań ogólnych i funkcjonowanie jako autonomiczne agenty. W tych aplikacjach LLM angażują się w planowanie wieloetapowe, rozumowanie i interakcję z zewnętrznymi środowiskami. Ułatwia to korzystanie z narzędzi, wielu modalności wejściowych i różnych technik promptowania, takich jak few-shot learning, samospójność, szkielet myśli i drzewo myśli. Te nowe przypadki użycia wymagają wielu, często zależnych, wywołań generacyjnych LLM, wskazując na tendencję do korzystania ze struktur wielowyzwań do wykonania zadań złożonych.

Ta zmiana oznacza przejście od prostej rozmowy do bardziej zaawansowanego programowego wykorzystania LLM, gdzie programy planują i kontrolują procesy generacyjne LLM. Te programy są określane jako “Programy Modeli Językowych” (LM Programy). Zaawansowane techniki promptowania i przepływy agentykowe mieszczą się w zakresie LM programów. Istnieją dwie wspólne właściwości LM programów: (1) LM programy zazwyczaj obejmują wiele wywołań LLM przemieszanych z kontrolą przepływu w celu wykonania zadań złożonych i poprawy ogólnej jakości. (2) LM programy otrzymują strukturyzowane dane wejściowe i wytwarzają strukturyzowane dane wyjściowe, umożliwiając kompozycję LM programów i integrację z istniejącymi systemami oprogramowania.

W tym artykule będziemy głębiej analizować ramy SGLang, eksplorując jego architekturę, analizując jego wydajność i porównując go z ramami stanu sztuki. Zatem zacznijmy.

Wprowadzenie do SGLang

Pomimo powszechnego użycia LM programów, obecne systemy do wyrażania i wykonywania ich pozostają niewydajne. SGLang identyfikuje dwa podstawowe wyzwania związane z wydajnym użyciem LM programów:

  • Złożoność programowania: Rozwój LM programów jest uciążliwy i trudny ze względu na nieprzewidywalną naturę LLM. Obejmuje to obszerną manipulację ciągami, eksperymentalne dostosowywanie promptów, kruche parsowanie danych wyjściowych, obsługę wielu modalności wejściowych i wdrażanie mechanizmów równoległości. Ta złożoność znacznie redukuje czytelność nawet prostych programów.
  • Niewydajność wykonania: Wykonywanie LM programów jest niewydajne ze względu na zbędne obliczenia i użycie pamięci. Systemy inferencyjne stanu sztuki, zoptymalizowane w celu zmniejszenia opóźnień i poprawy wydajności, nie posiadają bezpośredniej wiedzy o obciążeniu, co skutkuje znacznymi niewydajnościami. Godny uwagi przykład stanowi ponowne wykorzystanie pamięci podręcznej Key-Value (KV), która składa się z ponownie wykorzystywanych pośrednich tensorów niezbędnych do inferencji generacyjnej. Obecne systemy nie posiadają skutecznych mechanizmów ułatwiających ponowne wykorzystanie pamięci podręcznej KV między wieloma wywołaniami LLM, które dzielą wspólny prefiks, prowadząc do niepotrzebnych obliczeń i marnowania pamięci. Dodatkowo, dekodowanie ograniczone dla strukturyzowanych danych wyjściowych, takich jak tryb JSON, jest podoptymalne, ponieważ istniejące systemy dekodują tylko jeden token na raz.

Aby rozwiązać te wyzwania, SGLang wprowadza Strukturalny Język Generacyjny dla LLM. Podstawowa idea polega na systematycznym wykorzystaniu struktury wielowyzwań w LM programach do wydajnego wykonania. Jak pokazano na poniższym rysunku, SGLang składa się z dwóch części: frontendu i środowiska wykonawczego.

Frontend ułatwia programowanie LM programów, a środowisko wykonawcze przyspiesza ich wykonanie. Te części mogą współpracować w celu uzyskania lepszej wydajności lub funkcjonować niezależnie.

SGLang jest językiem specyficznym dla domeny, osadzonym w Pythonie, zapewniającym prymitywy generacji (np. extend, gen, select) i kontroli równoległości (np. fork, join). Jest on kompatybilny z kontrolą przepływu Pythona i bibliotekami, umożliwiając użytkownikom łatwe rozwijanie zaawansowanych przepływów promptowania z natywną składnią Pythona. SGLang obejmuje interpreter i kompilator. Interpreter zarządza stanem promptu jako strumieniem i przesyła operacje prymitywne do strumienia do asynchronicznego wykonania, zapewniając właściwą kontrolę nad synchronizacją i równoległością wewnątrz programu. Dodatkowo, programy SGLang mogą być śledzone i skompilowane w celu dalszych optymalizacji. Środowisko wykonawcze SGLang proponuje kilka nowych optymalizacji w celu przyspieszenia wykonania LM programów:

  • RadixAttention: Ta technika umożliwia automatyczne ponowne wykorzystanie pamięci podręcznej KV między wieloma wywołaniami generacyjnymi. W istniejących silnikach inferencyjnych pamięć podręczna KV żądania jest odrzucana po przetworzeniu, uniemożliwiając ponowne wykorzystanie między wieloma wywołaniami i spowalniając wykonanie. SGLang utrzymuje pamięć podręczną LRU pamięci podręcznej KV wewnątrz drzewa radix, zarządzając pamięcią podręczną KV jako tradycyjną pamięcią podręczną i wykorzystując drzewo radix do wydajnego dopasowania, wstawienia i usunięcia. Pozwala to środowisku wykonawczemu na efektywne obsługę różnych wzorców ponownego wykorzystania.
  • Sprężona Maszyna Stanowa: Ta technika umożliwia szybsze dekodowanie ograniczone dla strukturyzowanych danych wyjściowych. Istniejące systemy przestrzegają ograniczeń tylko dla następnego tokenu, umożliwiając dekodowanie tylko jednego tokenu na raz. Zamiast tego, SGLang analizuje ograniczenia i tworzy sprężoną maszynę stanową do ich reprezentowania, kompresując wielotokenowy ścieżkę do jednego kroku, gdy jest to możliwe, umożliwiając dekodowanie wielu tokenów na raz w celu zwiększenia szybkości.
  • Wykonanie Spekulatywne API: Dla modeli API, takich jak OpenAI’s GPT-4, SGLang wprowadza wykonanie spekulatywne API w celu optymalizacji programów wielowyzwań.

Korzystając z SGLang, zaimplementowano różne aplikacje LLM, w tym kontrolę agenta, rozumowanie logiczne, benchmarki few-shot learning, dekodowanie JSON, potoki generacji z uzupełnieniem, rozmowę wieloobrotową i przetwarzanie multimodalne. Wydajność została przetestowana na modelach, w tym Llama-7B/70B, Mistral-8x7B, LLaVA-v1.5-7B (obraz) i LLaVA-NeXT-34B (wideo) na NVIDIA A10G i A100 GPU. Wyniki eksperymentów pokazują, że SGLang osiąga nawet 6,4-krotnie wyższą wydajność w szerokim zakresie obciążeń, modeli i konfiguracji sprzętu, w porównaniu z istniejącymi systemami programowania i inferencji, w tym Guidance, vLLM i LMQL.

SGLang: Model Programowania i Metodologia

Model programowania SGLang jest wprowadzony za pomocą uruchomionego przykładu, opisując jego prymitywy językowe i tryby wykonania, oraz zarysowując możliwości optymalizacji czasu wykonania. Ten model upraszcza uciążliwe operacje w przepływach wielowyzwań (np. manipulację ciągami, wywołania API, specyfikację ograniczeń, równoległość) poprzez zapewnienie elastycznych i komponowalnych prymitywów. SGLang jest językiem specyficznym dla domeny, osadzonym w Pythonie. Poniższy rysunek pokazuje program, który ocenia esej o obrazie za pomocą metody promptowania gałęzi-rozwiązania-połączenia.

Funkcja multi_dimensional_judge przyjmuje trzy argumenty: `s`, `path` i `essay`. s zarządza stanem promptu, path to ścieżka pliku obrazu, a essay to tekst eseju. Nowe ciągi i prymitywy SGLang mogą być dołączane do stanu s do wykonania za pomocą operatora +=. Po raz pierwszy, funkcja dodaje obraz i esej do promptu. Następnie sprawdza, czy esej jest związany z obrazem za pomocą select, przechowując wynik w s[“related”]. Jeśli są związane, prompt jest rozwidlony w trzy kopie do równoległej oceny z różnych wymiarów, za pomocą gen do przechowywania wyników w f[“judgment”]. Następnie, łączy oceny, generuje podsumowanie i przypisuje ocenę literową. Na koniec zwraca wyniki w formacie JSON, postępując zgodnie ze schematem zdefiniowanym przez wyrażenie regularne regex. SGLang znacznie upraszcza ten program, ponieważ równoważny program korzystający z interfejsu API OpenAI wymagałby 2,1-krotnie więcej linii kodu ze względu na ręczną manipulację ciągami i kontrolę równoległości.

SGLang zapewnia prymitywy do kontroli stanu promptu, generacji i równoległości, które mogą być używane z składnią Pythona i bibliotekami. Oto prymitywy:

gen: Wywołuje model w celu generacji i przechowuje wyniki w zmiennej o nazwie określonej w jego pierwszym argumencie. Obsługuje argument `regex` do ograniczenia danych wyjściowych do postaci gramatyki zdefiniowanej przez wyrażenie regularne (np. schemat JSON).

  • select: Wywołuje model w celu wyboru najwyższej prawdopodobieństwa opcji z listy.
  • += lub extend: Dołącza ciąg do promptu.
  • [nazwa_zmiennej]: Pobiera wyniki generacji.
  • fork: Tworzy równoległe rozwidlenia stanu promptu.
  • join: Połącza stan promptu.
  • image i video: Przyjmują dane wejściowe obrazu i wideo.

Najprostszy sposób wykonania programu SGLang to za pomocą interpretera, gdzie prompt jest traktowany jako asynchroniczny strumień. Prymitywy, takie jak extend, gen i select, są przesyłane do strumienia do asynchronicznego wykonania. Te nieblokujące wywołania pozwalają kodowi Pythona na kontynuowanie wykonywania bez oczekiwania na zakończenie generacji, podobnie jak uruchamianie jąder CUDA w sposób asynchroniczny. Każdy prompt jest zarządzany przez wykonawcę strumienia w wątku tła, umożliwiając równoległość wewnątrz programu. Pobieranie wyników generacji zablokuje wykonanie do momentu, gdy będą gotowe, zapewniając poprawną synchronizację. Alternatywnie, programy SGLang mogą być skompilowane jako grafy obliczeniowe i wykonane z wykonawcą grafu, umożliwiając dalsze optymalizacje. Ten artykuł używa trybu interpretera domyślnie i omawia wyniki trybu kompilatora w Dodatku D. SGLang obsługuje modele o otwartych wagach za pomocą własnego środowiska wykonawczego SGLang (SRT), a także modele API, takie jak OpenAI i Anthropic.

Systemy programowania dla LLM można klasyfikować jako wysokopoziomowe (np. LangChain, DSPy) i niskopoziomowe (np. LMQL, Guidance, SGLang). Systemy wysokopoziomowe zapewniają predefiniowane lub automatycznie generowane prompty, takie jak optymalizator promptów DSPy. Systemy niskopoziomowe zwykle nie modyfikują promptów, ale pozwalają na bezpośrednią manipulację promptami i prymitywami. SGLang jest systemem niskopoziomowym, podobnym do LMQL i Guidance. Poniższa tabela porównuje ich funkcje.

SGLang koncentruje się bardziej na wydajności środowiska wykonawczego i jest wyposażony w współprojektowane środowisko wykonawcze, umożliwiające nowe optymalizacje. Języki wysokopoziomowe (np. DSPy) mogą być skompilowane do języków niskopoziomowych (np. SGLang). Demonstruje się integrację SGLang jako backendu w DSPy w celu poprawy wydajności środowiska wykonawczego.

Powyższy przykład ilustruje operacje RadixAttention z zasadą wyrzucania LRU w dziewięciu punktach czasowych, pokazując dynamiczną ewolucję drzewa radix w odpowiedzi na różne żądania. Żądania te obejmują dwie sesje czatu, partię pytań few-shot i próbkowanie samospójności. Każda krawędź drzewa niesie etykietę wskazującą podciąg lub sekwencję tokenów. Węzły są kodowane kolorami, aby odzwierciedlić różne stany: zielony dla nowo dodanych węzłów, niebieski dla węzłów pamięci podręcznej dostępu w czasie punktu i czerwony dla węzłów, które zostały usunięte.

Krok 1: Drzewo radix jest początkowo puste.

Krok 2: Serwer przetwarza przychodzącą wiadomość użytkownika “Hello” i odpowiada dane wyjściowe LLM “Hi”. System prompt “Jesteś pomocnym asystentem”, wiadomość użytkownika “Hello!” i odpowiedź LLM “Hi!” są skonsolidowane w drzewie jako pojedyncza krawędź połączona z nowym węzłem.

Krok 3: Nowy prompt przychodzi, a serwer znajduje prefiks promptu (tj. pierwszy obrót rozmowy) w drzewie radix i ponownie wykorzystuje jego pamięć podręczną KV. Nowy obrót jest dołączony do drzewa jako nowy węzeł.

Krok 4: Nowa sesja czatu rozpoczyna się. Węzeł z kroku 3 jest podzielony na dwa węzły, aby umożliwić dwóm sesjom czatu dzielenie systemu promptu.

Krok 5: Druga sesja czatu kontynuuje się. Jednak ze względu na ograniczenia pamięci, węzeł z kroku 4 musi być usunięty. Nowy obrót jest dołączony po pozostałym węźle z kroku 4.

Krok 6: Serwer otrzymuje zapytanie few-shot, przetwarza je i wstawia do drzewa. Węzeł korzeniowy jest podzielony, ponieważ nowe zapytanie nie dzieli prefiksu z istniejącymi węzłami.

Krok 7: Serwer otrzymuje partię dodatkowych zapytań few-shot. Zapytania te dzielą ten sam zestaw przykładów few-shot, więc węzeł z kroku 6 jest podzielony, aby umożliwić dzielenie.

Krok 8: Serwer otrzymuje nową wiadomość z pierwszej sesji czatu. Wyjmuje wszystkie węzły z drugiej sesji czatu, ponieważ są one najmniej używane.

Krok 9: Serwer otrzymuje żądanie próbkowania więcej odpowiedzi na pytania w węźle z kroku 8, prawdopodobnie do celów samospójności. Aby zrobić miejsce dla tych żądań, wiele węzłów jest usuniętych.

Ten przykład pokazuje, jak RadixAttention obsługuje dynamiczne przydzielanie i usuwanie węzłów w odpowiedzi na różne typy żądań, zapewniając wydajne ponowne wykorzystanie pamięci podręcznej KV i zarządzanie pamięcią.

SGLang: Ocena i Wyniki

Wyniki na Modelach Open-Weight

Wyniki opóźnienia i wydajności są pokazane na poniższych rysunkach. SGLang poprawia wydajność o maksymalnie 6,4-krotnie i redukuje opóźnienie o maksymalnie 3,7-krotnie. Te poprawy wynikają z ponownego wykorzystania pamięci podręcznej KV, wykorzystania równoległości wewnątrz programu i szybszego dekodowania ograniczonego.

Na tych benchmarkach wskaźnik trafień w pamięci podręcznej wynosi od 50% do 99%. Rysunek 13 (w Dodatku) wymienia osiągnięte i optymalne wskaźniki trafień w pamięci podręcznej dla wszystkich z nich, pokazując, że harmonogramowanie świadome pamięci podręcznej SGLang zbliża się do 96% optymalnego wskaźnika trafień w pamięci podręcznej średnio.

Wyniki na Większych Modelach z Równoległością Tensorów

Większe modele, Mixtral-8x7B i Llama-70B, zostały przetestowane z równoległością tensorów na tym samym zestawie benchmarków, a wyniki są raportowane na poniższym rysunku. Przyspieszenie na większych modelach pokazuje tendencję podobną do tej obserwowanej na mniejszych modelach, wskazując, że optymalizacja SGLang dobrze skaluje się do większych modeli. Guidance i LMQL zostały pominięte ze względu na brak wydajnych implementacji równoległości tensorów.

Wyniki na Modelach Multimodalnych

SGLang ma natywną obsługę modeli multimodalnych za pomocą prymitywów obrazu i wideo. Optymalizacje w tym artykule są kompatybilne z modelami multimodalnymi. Dla RadixAttention, hash danych wejściowych obrazu jest obliczany i używany jako klucz w drzewie radix, umożliwiając ponowne wykorzystanie pamięci podręcznej KV tokenów obrazu z tego samego obrazu. LLaVA-v1.5-7B (obraz) został uruchomiony na llava-bench-in-the-wild, a LLaVA-NeXT-34B (wideo) na ActivityNet. Ponieważ te modele nie są dobrze obsługiwane przez inne systemy bazowe, użyto oryginalnej implementacji autorów modelu w Hugging Face Transformers jako bazę. Jak pokazano w poniższej tabeli, SGLang zapewnia wydajność o maksymalnie 6-krotnie wyższą na tych benchmarkach. W llava-bench-in-the-wild, wiele pytań o ten sam obraz było obsługiwanych, a środowisko wykonawcze SGLang ponownie wykorzystywało pamięć podręczną KV w tym przypadku.

Wdrożenie Produkcyjne

SGLang został wdrożony w Chatbot Arena do obsługi modeli open-weight. Ze względu na niski ruch dla niektórych modeli, tylko jeden robot SGLang obsługuje każdy. Po miesiącu zaobserwowano 52,4% wskaźnik trafień w pamięci podręcznej RadixAttention dla LLaVA-Next-34B i 74,1% dla Vicuna-33B. Trafienia w pamięci podręcznej pochodziły z wspólnych wiadomości systemowych, często ponownie wykorzystywanych przykładów obrazów i historii rozmów wieloobrotowych. To zmniejszyło opóźnienie pierwszego tokenu o średnio 1,7-krotnie dla Vicuna-33B.

Końcowe Myśli

W tym artykule omówiliśmy SGLang, nowo wprowadzony system, który ma na celu zapewnienie wydajnego wykonania złożonych programów modeli językowych. SGLang składa się z frontendu i środowiska wykonawczego. Frontend ułatwia programowanie dzięki prymitywom generacji i kontroli równoległości, podczas gdy środowisko wykonawcze przyspiesza wykonanie dzięki nowym optymalizacjom, takim jak RadixAttention do ponownego wykorzystania pamięci podręcznej KV i sprężone maszyny stanowe dla szybszego dekodowania strukturyzowanych danych wyjściowych. Eksperymenty dowodzą, że SGLang osiąga nawet 6,4-krotnie wyższą wydajność w porównaniu z systemami inferencyjnymi na różnych dużych modelach językowych i multimodalnych, rozwiązując zadania takie jak kontrola agenta, rozumowanie logiczne, benchmarki few-shot learning, dekodowanie JSON, potoki generacji z uzupełnieniem i rozmowę wieloobrotową.

"Inżynier z zawodu, pisarz z serca". Kunal jest technicznym pisarzem z głęboką miłością i zrozumieniem AI i ML, poświęconym uproszczeniu złożonych pojęć w tych dziedzinach poprzez swoje angażujące i informacyjne dokumentacje.