Connect with us

SGLang: Esecuzione Efficienti di Programmi di Modelli Linguistici Strutturati

Intelligenza artificiale

SGLang: Esecuzione Efficienti di Programmi di Modelli Linguistici Strutturati

mm
SGLang: Efficient Execution of Structured Language Model Programs

I grandi modelli linguistici (LLM) vengono utilizzati sempre più per compiti complessi che richiedono più chiamate di generazione, tecniche di prompt avanzate, flusso di controllo e input/output strutturati. Tuttavia, mancano sistemi efficienti per la programmazione e l’esecuzione di queste applicazioni. SGLang, un sistema appena introdotto, si propone di 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 la generazione e il controllo della 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 di output strutturati più veloce. Gli esperimenti dimostrano che SGLang raggiunge un throughput fino a 6,4 volte superiore rispetto ai sistemi di inferenza attuali su vari modelli linguistici e multimodali di grandi dimensioni, affrontando compiti come il controllo degli agenti, il ragionamento logico, le prove di apprendimento a pochi esempi, la decodifica JSON, le pipeline di generazione con recupero e chat a più turni.

I recenti progressi nelle capacità dei LLM hanno ampliato la loro utilità, consentendo loro di gestire una gamma più ampia di compiti generali e di funzionare come agenti autonomi. In queste applicazioni, i LLM partecipano alla pianificazione multi-turno, al ragionamento e all’interazione con ambienti esterni. Ciò è facilitato attraverso l’uso di strumenti, più modalità di input e varie tecniche di prompt, come l’apprendimento a pochi esempi, l’auto-coerenza, lo scheletro del pensiero e l’albero del pensiero. Questi nuovi casi d’uso richiedono più chiamate dipendenti ai LLM, indicando una tendenza all’uso di strutture multi-chiamata per completare compiti complessi.

Questo passaggio segna una transizione da una semplice chat a un uso più sofisticato e programmato dei LLM, in cui i programmi pianificano e controllano i processi di generazione dei LLM. Questi programmi sono denominati “Programmi di Modelli Linguistici” (LM Program). Le tecniche di prompt avanzate e i flussi di lavoro agente rientrano nell’ambito dei programmi LM. Ci sono due proprietà comuni dei programmi LM: (1) i programmi LM coinvolgono generalmente più chiamate ai LLM intervallate da un flusso di controllo per completare compiti complessi 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 in sistemi software esistenti.

In questo articolo, esploreremo in modo più approfondito il framework SGLang, analizzandone l’architettura, valutandone le prestazioni e confrontandolo con framework attuali. Quindi, iniziamo.

Introduzione a SGLang

Nonostante l’uso diffuso dei programmi LM, i sistemi attuali per esprimere ed eseguire questi programmi rimangono inefficienti. SGLang identifica due sfide principali associate all’uso efficiente dei programmi LM:

  • Complessità di programmazione: sviluppare programmi LM è tedioso e difficile a causa della natura non deterministica dei LLM. Ciò comporta una manipolazione estensiva di stringhe, un’ottimizzazione sperimentale dei prompt, un’analisi dell’output fragile e la gestione di più modalità di input e meccanismi di parallelismo. Questa complessità riduce notevolmente la leggibilità anche dei programmi più semplici.
  • Inefficienza di esecuzione: eseguire programmi LM è inefficiente a causa del calcolo ridondante e dell’uso della memoria. I motori di inferenza attuali, ottimizzati per ridurre la latenza e migliorare il throughput, mancano di una conoscenza diretta del carico di lavoro, risultando in inefficienze significative. Un esempio notevole è il riutilizzo della cache KV, che consiste in tensori intermedi riutilizzabili essenziali per l’inferenza generativa. I sistemi attuali mancano di meccanismi efficaci per facilitare il riutilizzo della cache KV tra più chiamate ai LLM che condividono un prefisso comune, portando a calcoli inutili e spreco di memoria. Inoltre, la decodifica vincolata per output strutturati, come la modalità JSON, è subottimale poiché i sistemi esistenti decodificano solo un token alla volta.

Per affrontare queste sfide, SGLang introduce un linguaggio di generazione strutturato per i LLM. L’idea principale è sfruttare sistematicamente la struttura multi-chiamata nei programmi LM per un’esecuzione efficiente. Come mostrato nella figura seguente, SGLang ha due parti: un linguaggio frontend e un runtime.

Il frontend semplifica la programmazione dei programmi LM, e il runtime accelera la loro esecuzione. Queste parti possono lavorare insieme per una migliore prestazione o funzionare in modo indipendente.

SGLang è un linguaggio specifico del dominio incorporato in Python, che fornisce primitive per la generazione (ad esempio, estendi, gen, seleziona) e il controllo della 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 un controllo appropriato sulla sincronizzazione e la parallelismo all’interno del 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 più chiamate di generazione. 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 radix, gestendo la cache KV come una cache tradizionale e utilizzando l’albero radix per un matching, inserimento ed evizione efficienti. Ciò consente al runtime di gestire vari pattern di riutilizzo in modo efficiente.
  • Macchina a stati finiti compressa: questa tecnica consente una decodifica vincolata più veloce per output strutturati. I sistemi esistenti seguono le vincoli solo per il prossimo token, potendo decodificare solo un token alla volta. Al contrario, SGLang analizza le vincoli e costruisce una macchina a stati finiti compressa per rappresentarle, comprimendo un percorso multi-token in un percorso a singolo passo quando possibile, consentendo la decodifica di più token contemporaneamente per una velocità maggiore.
  • Esecuzione speculativa dell’API: per modelli API-only come OpenAI’s GPT-4, SGLang introduce l’esecuzione speculativa dell’API per ottimizzare i programmi multi-chiamata.

Utilizzando SGLang, sono stati implementati vari applicazioni LLM, tra cui il controllo degli agenti, il ragionamento logico, le prove di apprendimento a pochi esempi, la decodifica JSON, le pipeline di generazione con recupero e chat a più turni e l’elaborazione multi-modale. 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 superiore su una vasta 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 di programmazione e metodologia

Il modello di programmazione SGLang viene introdotto attraverso un esempio di esecuzione, descrivendo le primitive del linguaggio e i modi di esecuzione, e delineando le opportunità di ottimizzazione del runtime. Questo modello semplifica le operazioni tediose 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 prompt branch-solve-merge.

La funzione multi_dimensional_judge accetta tre argomenti: `s`, `path`, e `essay`. 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 allo stato s per l’esecuzione utilizzando l’operatore +=. Inizialmente, la funzione aggiunge l’immagine e il saggio al prompt. Quindi, controlla se il saggio è relativo all’immagine utilizzando select, memorizzando il risultato in s[“related”]. Se relativo, il prompt viene forkato in tre copie per una valutazione parallela da diverse dimensioni, utilizzando gen per memorizzare i risultati in f[“judgment”]. Successivamente, unisce i giudizi, genera un riassunto e assegna un voto di lettera. Infine, restituisce i risultati in formato JSON, seguendo uno schema definito da un’espressione regolare vincolo regex. SGLang semplifica notevolmente questo programma, poiché un programma equivalente utilizzando un’interfaccia simile a OpenAI richiederebbe 2,1 volte più righe di codice a causa della manipolazione manuale di stringhe e del controllo della parallelismo.

SGLang fornisce primitive per il controllo dello stato del prompt, la generazione e la parallelismo, che possono essere utilizzate con la sintassi e le librerie di Python. Ecco le primitive:

gen: chiama un modello per generare e memorizza i risultati in una variabile con il nome specificato nel suo 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 con la probabilità più alta da un elenco.
  • += o extend: aggiunge una stringa al prompt.
  • [variable_name]: recupera i risultati di una generazione.
  • fork: crea fork paralleli dello stato del prompt.
  • join: riunisce lo stato del prompt.
  • image e video: accettano input di immagini e video.

Il modo più semplice per eseguire un programma SGLang è attraverso un interprete, dove un prompt viene trattato come un flusso asincrono. Primitive come extend, gen e select vengono inviate al flusso per l’esecuzione asincrona. Queste chiamate non bloccanti consentono al codice Python di continuare l’esecuzione senza attendere la fine della generazione, simile al lancio di kernel CUDA in modo asincrono. Ogni prompt è gestito da un esecutore di flusso in un thread di sfondo, abilitando la parallelismo all’interno del programma. Il recupero dei risultati della generazione bloccherà fino a quando non saranno pronti, garantendo una sincronizzazione corretta. In alternativa, i programmi SGLang possono essere compilati come grafi computazionali ed eseguiti con un esecutore di grafi, consentendo ulteriori ottimizzazioni. Questo articolo utilizza la modalità interprete per impostazione predefinita e discute i risultati della modalità del compilatore nell’Appendice D. SGLang supporta modelli con pesi aperti con il proprio SGLang Runtime (SRT), nonché modelli API come OpenAI e Anthropic.

I sistemi di programmazione per i LLM possono essere classificati come ad alto livello (ad esempio, LangChain, DSPy) e a basso livello (ad esempio, LMQL, Guidance, SGLang). I sistemi ad alto livello forniscono prompt predefiniti o generati automaticamente, come l’ottimizzatore di prompt di DSPy. I sistemi a basso livello non alterano generalmente i prompt, ma consentono la manipolazione diretta dei prompt e delle primitive. SGLang è un sistema a basso livello simile a LMQL e Guidance. La seguente tabella confronta le loro funzionalità.

SGLang si concentra maggiormente sull’efficienza del runtime e viene fornito con il proprio runtime progettato, consentendo nuove ottimizzazioni. I linguaggi ad alto livello (ad esempio, DSPy) possono essere compilati in linguaggi a basso livello (ad esempio, SGLang). L’integrazione di SGLang come backend in DSPy per una migliore efficienza del runtime viene dimostrata in seguito.

L’esempio sopra illustra le operazioni RadixAttention con una politica di evizione LRU in nove punti temporali, mostrando l’evoluzione dinamica dell’albero radix in risposta a vari richieste. Queste richieste includono due sessioni di chat, un batch di richieste di apprendimento a pochi esempi e campionamento di auto-coerenza. 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 nodi appena aggiunti, blu per nodi in cache acceduti durante il punto temporale e rosso per nodi che sono stati espulsi.

Passo 1: l’albero radix è inizialmente vuoto.

Passo 2: il server elabora un messaggio utente in ingresso “Ciao” e risponde con l’output LLM “Ciao!”. Il prompt del sistema “Sei un assistente utile”, il messaggio utente “Ciao!” e la risposta LLM “Ciao!” vengono consolidati nell’albero come un solo bordo collegato a un nuovo nodo.

Passo 3: arriva un nuovo prompt e il server trova il prefisso del prompt (ossia il primo turno della conversazione) nell’albero radix 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 dal Passo 3 viene 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, un nodo dal Passo 4 deve essere espulso. Il nuovo turno viene aggiunto dopo il nodo rimanente dal Passo 4.

Passo 6: il server riceve una richiesta di apprendimento a pochi esempi, l’elabora e la inserisce nell’albero. Il nodo radice viene diviso perché la nuova richiesta non condivide alcun prefisso con nodi esistenti.

Passo 7: il server riceve un batch di ulteriori richieste di apprendimento a pochi esempi. Queste richieste condividono gli stessi esempi, quindi un nodo dal Passo 6 viene diviso per abilitare la condivisione.

Passo 8: il server riceve un nuovo messaggio dalla prima sessione di chat. Espelle tutti i nodi della seconda sessione di chat poiché sono quelli meno recentemente utilizzati.

Passo 9: il server riceve una richiesta per campionare più risposte alle domande in un nodo dal Passo 8, probabilmente per prompt di auto-coerenza. Per fare spazio a queste richieste, vengono espulsi più nodi.

Questo esempio dimostra come RadixAttention gestisca l’allocazione dinamica e l’evizione dei nodi in risposta a diversi tipi di richieste, garantendo un riutilizzo efficiente della cache KV e una gestione della memoria.

SGLang: Valutazione e Risultati

Risultati sui modelli con pesi aperti

I risultati di latenza e throughput sono mostrati nelle figure seguenti. SGLang migliora il throughput fino a 6,4 volte e riduce la latenza fino a 3,7 volte. Questi miglioramenti derivano dal riutilizzo della cache KV, dalla sfruttamento della parallelismo all’interno di un singolo programma e da una decodifica vincolata più veloce.

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

Risultati sui modelli più grandi con parallelismo dei tensori

Sono stati testati modelli più grandi, Mixtral-8x7B e Llama-70B, con parallelismo dei tensori sullo stesso set di benchmark, e i risultati sono riportati nella figura seguente. L’accelerazione sui modelli più grandi mostra una tendenza simile a quella osservata sui modelli più piccoli, indicando che l’ottimizzazione di SGLang si generalizza bene ai modelli più grandi. Guidance e LMQL sono stati omessi a causa della mancanza di implementazioni efficienti del parallelismo dei tensori.

Risultati sui modelli multi-modali

SGLang ha un supporto nativo per modelli multi-modali con le primitive di immagine e video. Le ottimizzazioni in questo articolo sono compatibili con modelli multi-modali. Per RadixAttention, viene calcolato l’hash degli input delle immagini e utilizzato come chiave nell’albero radix, consentendo il riutilizzo della cache KV dei token delle immagini della 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 baseline, è stata utilizzata l’implementazione originale degli autori del modello in Hugging Face Transformers come baseline. Come mostrato nella tabella seguente, SGLang fornisce 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 SGLang ha riutilizzato la cache KV in questo caso.

Distribuzione in produzione

SGLang è stato distribuito in Chatbot Arena per servire modelli con pesi aperti. A causa del traffico basso per alcuni modelli, solo un lavoratore SGLang serve ciascuno. Dopo un mese, è stato osservato un tasso di hit della cache RadixAttention del 52,4% per LLaVA-Next-34B e del 74,1% per Vicuna-33B. I colpi della cache provenivano da messaggi di sistema comuni, esempi di immagini riutilizzati frequentemente e storie di chat a più turni. Ciò ha ridotto la latenza del primo token di 1,7 volte in media per Vicuna-33B.

Pensieri finali

In questo articolo, abbiamo parlato di SGLang, un sistema appena introdotto che si propone di risolvere il 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 la generazione e il controllo della 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 di output strutturati più veloce. Gli esperimenti dimostrano che SGLang raggiunge un throughput fino a 6,4 volte superiore rispetto ai sistemi di inferenza attuali su vari modelli linguistici e multimodali di grandi dimensioni, affrontando compiti come il controllo degli agenti, il ragionamento logico, le prove di apprendimento a pochi esempi, la decodifica JSON, le pipeline di generazione con recupero e chat a più turni.

Un ingegnere per professione, uno scrittore per passione. Kunal è uno scrittore tecnico con un profondo amore e comprensione di AI e ML, dedicato a semplificare concetti complessi in questi campi attraverso la sua documentazione coinvolgente e informativa.