Intelligenza Artificiale
SGLang: esecuzione efficiente di programmi di modelli linguistici strutturati
I modelli linguistici di grandi dimensioni (LLM) sono sempre più utilizzati per compiti complessi che richiedono chiamate a generazione multipla, tecniche di prompt avanzate, flusso di controllo e input/output strutturati. Tuttavia mancano sistemi efficienti per programmare ed eseguire queste applicazioni. SGLang, un sistema di recente introduzione, mira a risolvere questo problema fornendo un'esecuzione efficiente di programmi di modelli linguistici complessi. SGLang comprende un linguaggio frontend e un runtime. Il frontend semplifica la programmazione con primitive per il controllo della generazione e del parallelismo, mentre il runtime accelera l'esecuzione attraverso nuove ottimizzazioni come RadixAttention per il riutilizzo della cache KV e macchine a stati finiti compressi per una decodifica dell'output strutturato più rapida. Gli esperimenti dimostrano che SGLang raggiunge un throughput fino a 6.4 volte superiore rispetto ai sistemi di inferenza all'avanguardia su vari modelli linguistici e multimodali di grandi dimensioni, affrontando compiti come il controllo degli agenti, il ragionamento logico, i benchmark di apprendimento "low-shot", la decodifica JSON, il recupero - pipeline di generazione aumentata e chat multi-turno.
I recenti progressi nelle capacità LLM hanno ampliato la loro utilità, consentendo loro di gestire una gamma più ampia di attività generali e di funzionare come agenti autonomi. In queste applicazioni, gli LLM si impegnano nella pianificazione, nel ragionamento e nell'interazione a più livelli con ambienti esterni. Ciò è facilitato dall'utilizzo degli strumenti, da molteplici modalità di input e da varie tecniche di suggerimento, come l'apprendimento a poche riprese, l'autocoerenza, lo scheletro del pensiero e l'albero del pensiero. Questi nuovi casi d’uso richiedono chiamate di generazione LLM multiple, spesso dipendenti, indicando una tendenza all’utilizzo di strutture multi-chiamata per completare attività complesse.
Questo cambiamento segna una transizione dalla semplice chat a un utilizzo programmatico più sofisticato degli LLM, in cui i programmi pianificano e controllano i processi di generazione degli LLM. Questi programmi sono indicati come "Programmi modello linguistico" (programmi LM). Le tecniche di prompt avanzate e i flussi di lavoro degli agenti rientrano nell'ambito dei programmi LM. Esistono due proprietà comuni dei programmi LM: (1) I programmi LM in genere comportano più chiamate LLM intervallate da un flusso di controllo per completare attività complesse e migliorare la qualità complessiva. (2) I programmi LM ricevono input strutturati e producono output strutturati, consentendo la composizione di programmi LM e l'integrazione nei sistemi software esistenti.
In questo articolo, approfondiremo il framework SGLang, esploreremo la sua architettura, analizzeremo le sue prestazioni e lo confronteremo con i framework più avanzati. Quindi iniziamo.
Un'introduzione a SGLang
Nonostante l’uso diffuso dei programmi LM, gli attuali sistemi per esprimerli ed eseguirli rimangono inefficienti. SGLang identifica due sfide principali associate all’uso efficiente dei programmi LM:
- Complessità di programmazione: lo sviluppo di programmi LM è noioso e difficile a causa della natura non deterministica dei LLM. Ciò comporta un'ampia manipolazione delle stringhe, un'ottimizzazione sperimentale dei prompt, un'analisi fragile dell'output, la gestione di più modalità di input e l'implementazione di meccanismi di parallelismo. Questa complessità riduce significativamente la leggibilità anche di programmi semplici.
- Inefficienza di esecuzione: l'esecuzione di programmi LM è inefficiente a causa del calcolo ridondante e dell'utilizzo della memoria. I motori di inferenza all’avanguardia, ottimizzati per ridurre la latenza e migliorare il throughput, mancano di una conoscenza diretta del carico di lavoro, con conseguenti inefficienze significative. Un esempio notevole è il riutilizzo della cache Key-Value (KV), che consiste in tensori intermedi riutilizzabili essenziali per l'inferenza generativa. I sistemi attuali non dispongono di meccanismi efficaci per facilitare il riutilizzo della cache KV su più dispositivi LLM chiamate che condividono un prefisso comune, il che comporta calcoli non necessari e memoria sprecata. Inoltre, la decodifica vincolata per output strutturati, come la modalità JSON, non è ottimale poiché i sistemi esistenti decodificano solo un token alla volta.
Per affrontare queste sfide, SGLang introduce un linguaggio di generazione strutturato per LLM. L'idea centrale è sfruttare sistematicamente la struttura multi-chiamata nei programmi LM per un'esecuzione efficiente. Come mostrato nella figura seguente, SGLang è composto da due parti: un linguaggio front-end e un runtime back-end.

Il front-end semplifica la programmazione dei programmi LM e il runtime ne accelera l'esecuzione. Queste parti possono funzionare insieme per prestazioni migliori o funzionare in modo indipendente.
SGLang è un linguaggio specifico del dominio incorporato in Python, che fornisce primitive per la generazione (ad esempio, extend, gen, select) e il controllo del parallelismo (ad esempio, fork, join). È compatibile con il flusso di controllo e le librerie di Python, consentendo agli utenti di sviluppare facilmente flussi di lavoro di prompt avanzati con la sintassi nativa di Python. SGLang include un interprete e un compilatore. L'interprete gestisce lo stato del prompt come un flusso e invia operazioni primitive al flusso per l'esecuzione asincrona, garantendo il controllo adeguato sulla sincronizzazione e sul parallelismo intra-programma. Inoltre, i programmi SGLang possono essere tracciati e compilati per ulteriori ottimizzazioni. Il runtime di SGLang propone diverse nuove ottimizzazioni per accelerare l'esecuzione dei programmi LM:
- RadixAttention: questa tecnica consente il riutilizzo automatico della cache KV tra chiamate di più generazioni. Nei motori di inferenza esistenti, la cache KV di una richiesta viene scartata dopo l'elaborazione, impedendo il riutilizzo tra più chiamate e rallentando l'esecuzione. SGLang mantiene una cache LRU della cache KV all'interno di un albero radice, gestendo la cache KV come una cache tradizionale e utilizzando l'albero radice per una corrispondenza, un inserimento e una eliminazione efficienti. Ciò consente al runtime di gestire in modo efficiente vari modelli di riutilizzo.
- Macchina a stati finiti compressa: questa tecnica consente una decodifica vincolata più rapida per output strutturati. I sistemi esistenti seguono i vincoli solo per il token successivo, rendendoli in grado di decodificare un token alla volta. Invece, SGLang analizza i vincoli e costruisce una macchina a stati finiti compressa per rappresentarli, comprimendo un percorso multi-token in un percorso a passaggio singolo quando possibile, consentendo la decodifica di più token contemporaneamente per una maggiore velocità.
- Esecuzione speculativa API: per modelli solo API come GPT-4 di OpenAI, SGLang introduce l'esecuzione speculativa dell'API per ottimizzare i programmi multi-chiamata.
Utilizzando SGLang, sono state implementate varie applicazioni LLM, tra cui controllo degli agenti, ragionamento logico, benchmark di apprendimento a scatti, decodifica JSON, pipeline di generazione aumentata con recupero, chat multi-turno ed elaborazione multi-modalità. Le prestazioni sono state testate su modelli tra cui Llama-7B/70B, Mistral-8x7B, LLaVA-v1.5-7B (immagine) e LLaVA-NeXT-34B (video) su GPU NVIDIA A10G e A100. I risultati sperimentali mostrano che SGLang raggiunge un throughput fino a 6.4 volte più elevato in un'ampia gamma di carichi di lavoro, modelli e configurazioni hardware, rispetto ai sistemi di programmazione e inferenza esistenti, tra cui Guidance, vLLM e LMQL.
SGLang: modello e metodologia di programmazione
Il modello di programmazione SGLang viene introdotto attraverso un esempio in esecuzione, descrivendo le primitive del linguaggio e le modalità di esecuzione e delineando le opportunità di ottimizzazione del runtime. Questo modello semplifica le operazioni noiose nei flussi di lavoro multi-chiamata (ad esempio, manipolazione di stringhe, chiamate API, specifica di vincoli, parallelismo) fornendo primitive flessibili e componibili. SGLang è un linguaggio specifico del dominio incorporato in Python. La figura seguente mostra un programma che valuta un saggio su un'immagine utilizzando il metodo di richiesta branch-solve-merge.

La funzione giudice_multi_dimensionale accetta tre argomenti: "s", "percorso" e "saggio".. s gestisce lo stato del prompt, path è il percorso del file immagine e essay è il testo del saggio. Nuove stringhe e primitive SGLang possono essere aggiunte agli state per l'esecuzione utilizzando il metodo += operatore. Innanzitutto, la funzione aggiunge l'immagine e il saggio al prompt. Quindi controlla se il saggio è correlato all'immagine utilizzando select, memorizzando il risultato s[“correlato”]. Se correlato, il prompt viene suddiviso in tre copie per la valutazione parallela da diverse dimensioni, utilizzando gen per archiviare i risultati f[“giudizio”]. Successivamente unisce i giudizi, genera un riepilogo e assegna un voto in lettere. Infine, restituisce i risultati in formato JSON, seguendo uno schema definito da un vincolo di espressione regolare regex. SGLang semplifica notevolmente questo programma, poiché un programma equivalente che utilizza un'interfaccia simile a API OpenAI richiederebbe 2.1 volte più righe di codice a causa della manipolazione manuale delle stringhe e del controllo del parallelismo.
SGLang fornisce primitive per il controllo dello stato, della generazione e del parallelismo del prompt, che possono essere utilizzate con la sintassi e le librerie Python. Ecco le primitive:
genere: Chiama un modello per generare e archivia i risultati in una variabile con il nome specificato nel primo argomento. Supporta un argomento "regex" per vincolare l'output a seguire una grammatica definita da un'espressione regolare (ad esempio, uno schema JSON).
- select: chiama un modello per scegliere l'opzione di probabilità più alta da un elenco.
- += o extend: aggiunge una stringa al prompt.
- [nome_variabile]: recupera i risultati di una generazione.
- fork: crea fork paralleli dello stato prompt.
- join: si unisce nuovamente allo stato prompt.
- immagine e video: acquisisci input di immagini e video.
Il modo più semplice per eseguire un programma SGLang è tramite un interprete, dove un prompt viene trattato come un flusso asincrono. Ai primitivi piace estendi, genera e seleziona vengono inviati al flusso per l'esecuzione asincrona. Queste chiamate non bloccanti consentono al codice Python di continuare l'esecuzione senza attendere il completamento della generazione, in modo simile all'avvio asincrono dei kernel CUDA. Ogni prompt è gestito da un esecutore di flusso in un thread in background, consentendo il parallelismo intra-programma. Il recupero dei risultati della generazione verrà bloccato finché non saranno pronti, garantendo la corretta sincronizzazione. In alternativa, i programmi SGLang possono essere compilati come grafici computazionali ed eseguiti con un esecutore di grafici, consentendo ulteriori ottimizzazioni. Questo documento utilizza la modalità interprete per impostazione predefinita e discute i risultati della modalità compilatore nell'Appendice D. SGLang supporta modelli open-weight con il proprio SGLang Runtime (SRT), nonché modelli API come OpenAI e modelli antropici.
I sistemi di programmazione per LLM possono essere classificati come di alto livello (ad esempio, LangChain, DSPy) e di basso livello (ad esempio, LMQL, Guidance, SGLang). I sistemi di alto livello forniscono prompt predefiniti o generati automaticamente, come l'ottimizzatore di prompt di DSPy. I sistemi di basso livello in genere non alterano i prompt ma consentono la manipolazione diretta di prompt e primitive. SGLang è un sistema di basso livello simile a LMQL e Guidance. La tabella seguente confronta le loro caratteristiche.

SGLang si concentra maggiormente sull'efficienza del runtime e viene fornito con un proprio runtime co-progettato, consentendo nuove ottimizzazioni. I linguaggi di alto livello (ad esempio DSPy) possono essere compilati in linguaggi di basso livello (ad esempio SGLang). L'integrazione di SGLang come backend in DSPy per una migliore efficienza di runtime verrà dimostrata più avanti.

L'esempio sopra illustra le operazioni di RadixAttention con una politica di eliminazione LRU in nove punti temporali, mostrando l'evoluzione dinamica dell'albero radice in risposta a varie richieste. Queste richieste includono due sessioni di chat, un batch di domande di apprendimento di poche fasi e un campionamento di autocoerenza. Ogni bordo dell'albero porta un'etichetta che denota una sottostringa o una sequenza di token. I nodi sono codificati a colori per riflettere stati diversi: verde per i nodi appena aggiunti, blu per i nodi memorizzati nella cache a cui si accede durante il punto temporale e rosso per i nodi che sono stati sfrattati.
Passo 1: L'albero radice è inizialmente vuoto.
Passo 2: Il server elabora un messaggio utente in entrata "Hello" e risponde con l'output LLM "Hi". Il sistema richiede "Sei un assistente utile", il messaggio utente "Ciao!" e la risposta LLM "Ciao!" sono consolidati nell'albero come un singolo bordo collegato a un nuovo nodo.
Passo 3: Arriva un nuovo prompt e il server trova il prefisso del prompt (cioè il primo turno della conversazione) nell'albero radice e riutilizza la sua cache KV. Il nuovo turno viene aggiunto all'albero come un nuovo nodo.
Passo 4: Inizia una nuova sessione di chat. Il nodo del passaggio 3 è diviso in due nodi per consentire alle due sessioni di chat di condividere il prompt del sistema.
Passo 5: La seconda sessione di chat continua. Tuttavia, a causa dei limiti di memoria, è necessario eliminare un nodo del passaggio 4. Il nuovo turno viene aggiunto dopo il nodo rimanente del passaggio 4.
Passo 6: Il server riceve una query di apprendimento di tipo "some-shot", la elabora e la inserisce nell'albero. Il nodo radice è diviso perché la nuova query non condivide alcun prefisso con i nodi esistenti.
Passo 7: Il server riceve un batch di query aggiuntive di apprendimento "low-shot". Queste query condividono lo stesso set di esempi di poche riprese, quindi un nodo del passaggio 6 viene suddiviso per consentire la condivisione.
Passo 8: Il server riceve un nuovo messaggio dalla prima sessione di chat. Elimina tutti i nodi dalla seconda sessione di chat poiché sono stati utilizzati meno di recente.
Passo 9: Il server riceve una richiesta per campionare più risposte alle domande in un nodo del passaggio 8, probabilmente per una richiesta di coerenza interna. Per fare spazio a queste richieste, vengono sfrattati più nodi.
Questo esempio dimostra come RadixAttention gestisce l'allocazione dinamica e l'eliminazione dei nodi in risposta a diversi tipi di richieste, garantendo un efficiente riutilizzo della cache KV e una gestione della memoria.
SGLang: valutazione e risultati
Risultati sui modelli a peso aperto
I risultati di latenza e throughput sono mostrati nelle figure seguenti. SGLang migliora il throughput fino a 6.4× e riduce la latenza fino a 3.7×. Questi miglioramenti derivano dal riutilizzo della cache KV, dallo sfruttamento del parallelismo all'interno di un singolo programma e da una decodifica vincolata più rapida.

Su questi benchmark, il tasso di successo della cache varia dal 50% al 99%. La Figura 13 (Appendice) elenca i tassi di successo della cache raggiunti e ottimali per tutti, mostrando che la pianificazione cache-aware di SGLang si avvicina in media al 96% del tasso di successo ottimale.

Risultati su modelli più grandi con parallelismo tensoriale
Modelli più grandi, Mixtral-8x7B e Llama-70B, sono stati testati con parallelismo tensoriale sullo stesso set di benchmark e i risultati sono riportati nella figura seguente. L'accelerazione sui modelli più grandi mostra un andamento simile a quello osservato sui modelli più piccoli, indicando che l'ottimizzazione di SGLang si generalizza bene a modelli più grandi. Guidance e LMQL sono stati omessi a causa della mancanza di implementazioni efficienti del parallelismo tensoriale.

Risultati su modelli multimodali
SGLang supporta nativamente modelli multimodali con primitive immagine e video. Le ottimizzazioni presentate in questo articolo sono compatibili con i modelli multimodali. Per RadixAttention, l'hash delle immagini di input viene calcolato e utilizzato come chiave nell'albero radix, consentendo il riutilizzo della cache KV dei token immagine dalla stessa immagine. LLaVA-v1.5-7B (immagine) è stato eseguito su llava-bench-in-the-wild e LLaVA-NeXT-34B (video) su ActivityNet. Poiché questi modelli non sono ben supportati da altri sistemi di base, è stata utilizzata come base l'implementazione originale degli autori del modello in Hugging Face Transformers. Come mostrato nella tabella seguente, SGLang offre un throughput fino a 6 volte superiore su questi benchmark. In llava-bench-in-the-wild, sono state gestite più domande sulla stessa immagine e, in questo caso, il runtime di SGLang ha riutilizzato la cache KV.

Distribuzione di produzione
SGLang è stato implementato in Chatbot Arena per servire modelli a peso aperto. A causa del basso traffico per alcuni modelli, serve solo un lavoratore SGLang ciascuno. Dopo un mese, è stata osservata una percentuale di riscontri della cache RadixAttention del 52.4% per LLaVA-Next-34B e del 74.1% per Vicuna-33B. Gli accessi alla cache provenivano da messaggi di sistema comuni, immagini di esempio riutilizzate di frequente e cronologie di chat a più turni. Ciò ha ridotto la latenza del primo token in media di 1.7× per Vicuna-33B.

Considerazioni finali
In questo articolo abbiamo parlato di SGLang, un sistema di recente introduzione, che mira a risolvere questo problema fornendo un'esecuzione efficiente di programmi di modelli linguistici complessi. SGLang comprende un linguaggio frontend e un runtime. Il frontend semplifica la programmazione con primitive per il controllo della generazione e del parallelismo, mentre il runtime accelera l'esecuzione attraverso nuove ottimizzazioni come RadixAttention per il riutilizzo della cache KV e macchine a stati finiti compressi per una decodifica dell'output strutturato più rapida. Gli esperimenti dimostrano che SGLang raggiunge un throughput fino a 6.4 volte superiore rispetto ai sistemi di inferenza all'avanguardia su vari modelli linguistici e multimodali di grandi dimensioni, affrontando compiti come il controllo degli agenti, il ragionamento logico, i benchmark di apprendimento "low-shot", la decodifica JSON, il recupero - pipeline di generazione aumentata e chat multi-turno.












