Modelli e piattaforme di IA
Ottimizzazione della distribuzione di LLM: vLLM PagedAttention e il futuro della gestione efficiente dell’AI
I modelli linguistici di grandi dimensioni (LLM) che vengono distribuiti su applicazioni del mondo reale presentano sfide uniche, in particolare in termini di risorse computazionali, latenza e costo-efficienza. In questa guida completa, esploreremo il panorama della gestione di LLM, con un focus particolare su vLLM (modello linguistico vettoriale), una soluzione che sta ridisegnando il modo in cui distribuiamo e interagiamo con questi potenti modelli.
Le sfide della gestione di modelli linguistici di grandi dimensioni
Prima di addentrarci in soluzioni specifiche, esaminiamo le principali sfide che rendono la gestione di LLM un compito complesso:
Risorse computazionali
Gli LLM sono noti per il loro enorme numero di parametri, che varia da miliardi a centinaia di miliardi. Ad esempio, GPT-3 vanta 175 miliardi di parametri, mentre modelli più recenti come GPT-4 sono stimati avere ancora più parametri. Questa enorme dimensione si traduce in significative esigenze computazionali per l’inferenza.
Esempio:
Consideriamo un modello LLM relativamente modesto con 13 miliardi di parametri, come LLaMA-13B. Anche questo modello richiede:
– Circa 26 GB di memoria solo per archiviare i parametri del modello (supponendo una precisione a 16 bit)
– Altra memoria per attivazioni, meccanismi di attenzione e calcoli intermedi
– Sostanziale potenza di calcolo GPU per l’inferenza in tempo reale
Latenza
In molte applicazioni, come ad esempio chatbot o generazione di contenuti in tempo reale, la bassa latenza è cruciale per una buona esperienza utente. Tuttavia, la complessità degli LLM può portare a tempi di elaborazione significativi, specialmente per sequenze più lunghe.
Esempio:
Immaginiamo un chatbot di servizio clienti alimentato da un LLM. Se ogni risposta richiede diversi secondi per essere generata, la conversazione sembrerà innaturale e frustrante per gli utenti.
Costo
L’hardware necessario per eseguire gli LLM su larga scala può essere estremamente costoso. Le GPU di fascia alta o le TPU sono spesso necessarie, e il consumo energetico di questi sistemi è sostanziale.
Esempio:
Eseguire un cluster di GPU NVIDIA A100 (spesso utilizzate per l’inferenza di LLM) può costare migliaia di dollari al giorno in tariffe di calcolo cloud.
Approcci tradizionali alla gestione di LLM
Prima di esplorare soluzioni più avanzate, esaminiamo brevemente alcuni approcci tradizionali alla gestione di LLM:
Distribuzione semplice con Hugging Transformers
La libreria Hugging Face Transformers fornisce un modo semplice per distribuire gli LLM, ma non è ottimizzata per la gestione ad alto throughput.
Esempio di codice:
from transformers import AutoModelForCausalLM, AutoTokenizer
import torch
<p>model_name = "meta-llama/Llama-2-13b-hf"
model = AutoModelForCausalLM.from_pretrained(model_name, device_map="auto")
tokenizer = AutoTokenizer.from_pretrained(model_name)</p>
<p>def generate_text(prompt, max_length=100):
inputs = tokenizer(prompt, return_tensors="pt").to(model.device)
outputs = model.generate(**inputs, max_length=max_length)
return tokenizer.decode(outputs[0], skip_special_tokens=True)</p>
<p>print(generate_text("Il futuro dell'AI è"))
Sebbene questo approccio funzioni, non è adatto per applicazioni ad alto traffico a causa del suo utilizzo inefficiente delle risorse e della mancanza di ottimizzazioni per la gestione.
Utilizzo di TorchServe o framework simili
Framework come TorchServe offrono capacità di gestione più robuste, tra cui bilanciamento del carico e versioning del modello. Tuttavia, non affrontano le sfide specifiche della gestione di LLM, come la gestione efficiente della memoria per modelli di grandi dimensioni.
Comprensione della gestione della memoria nella gestione di LLM
La gestione efficiente della memoria è critica per la gestione di modelli linguistici di grandi dimensioni (LLM) a causa delle estese risorse computazionali richieste. Le immagini seguenti illustrano vari aspetti della gestione della memoria, che sono integrali per ottimizzare le prestazioni di LLM.
Memoria segmentata vs. memoria paginata
Queste due immagini confrontano le tecniche di gestione della memoria segmentata e paginata, comunemente utilizzate nei sistemi operativi (OS).
- Memoria segmentata: Questa tecnica divide la memoria in segmenti diversi, ciascuno corrispondente a un programma o processo diverso. Ad esempio, in un contesto di gestione di LLM, segmenti diversi potrebbero essere allocati a vari componenti del modello, come la tokenizzazione, l’incorporazione e i meccanismi di attenzione. Ciascun segmento può crescere o diminuire in modo indipendente, offrendo flessibilità ma potenzialmente portando a frammentazione se i segmenti non vengono gestiti correttamente.
- Memoria paginata: Qui, la memoria viene divisa in pagine di dimensioni fisse, che vengono mappate sulla memoria fisica. Le pagine possono essere scambiate dentro e fuori come necessario, consentendo un utilizzo efficiente delle risorse di memoria. Nella gestione di LLM, ciò può essere cruciale per la gestione delle grandi quantità di memoria richieste per l’archiviazione dei pesi del modello e dei calcoli intermedi.
Gestione della memoria in OS vs. vLLM
Questa immagine contrasta la gestione della memoria tradizionale dei sistemi operativi con l’approccio di gestione della memoria utilizzato in vLLM.
- Gestione della memoria OS: Nei sistemi operativi tradizionali, i processi (ad esempio, Processo A e Processo B) vengono allocati pagine di memoria (Pagina 0, Pagina 1, ecc.) nella memoria fisica. Questa allocazione può portare a frammentazione nel tempo man mano che i processi richiedono e rilasciano memoria.
- Gestione della memoria vLLM: Il framework vLLM utilizza una cache KV (Key-Value) per gestire la memoria in modo più efficiente. Le richieste (ad esempio, Richiesta A e Richiesta B) vengono allocate blocchi della cache KV (Blocco KV 0, Blocco KV 1, ecc.). Questo approccio aiuta a minimizzare la frammentazione e ottimizza l’utilizzo della memoria, consentendo una gestione del modello più rapida ed efficiente.
Meccanismo di attenzione in LLM
Il meccanismo di attenzione è un componente fondamentale dei modelli transformer, comunemente utilizzati per gli LLM. Questa immagine illustra la formula di attenzione e i suoi componenti:
- Query (Q): Un nuovo token nel passo del decoder o l’ultimo token che il modello ha visto.
- Key (K): Contesto precedente a cui il modello deve prestare attenzione.
- Value (V): Somma ponderata sul contesto precedente.
La formula calcola i punteggi di attenzione prendendo il prodotto scalare della query con le chiavi, scalando per la radice quadrata della dimensione della chiave, applicando una funzione softmax e infine prendendo il prodotto scalare con i valori. Questo processo consente al modello di concentrarsi sulle parti rilevanti della sequenza di input durante la generazione di ciascun token.
Confronto della gestione della velocità di servizio
Questa immagine presenta un confronto della velocità di servizio tra diversi framework (HF, TGI e vLLM) utilizzando modelli LLaMA su diverse configurazioni hardware.
- LLaMA-13B, A100-40GB: vLLM raggiunge una velocità di servizio 14-24 volte superiore rispetto a HuggingFace Transformers (HF) e 2,2-2,5 volte superiore rispetto a HuggingFace Text Generation Inference (TGI).
- LLaMA-7B, A10G: Si osservano tendenze simili, con vLLM che supera notevolmente sia HF che TGI.
vLLM: una nuova architettura di servizio di LLM
vLLM, sviluppato da ricercatori alla UC Berkeley, rappresenta un notevole passo avanti nella tecnologia di servizio di LLM. Esploriamo le sue caratteristiche chiave e innovazioni:
PagedAttention
Al cuore di vLLM si trova PagedAttention, un algoritmo di attenzione innovativo ispirato alla gestione della memoria virtuale nei sistemi operativi. Ecco come funziona:
– Partizionamento della cache KV: Invece di archiviare l’intera cache KV in modo contiguo nella memoria, PagedAttention la divide in blocchi di dimensioni fisse.
– Archiviazione non contigua: Questi blocchi possono essere archiviati in modo non contiguo nella memoria, consentendo una gestione della memoria più flessibile.
– Allocazione su richiesta: I blocchi vengono allocati solo quando necessario, riducendo lo spreco di memoria.
– Condivisione efficiente: Molteplici sequenze possono condividere blocchi, consentendo ottimizzazioni per tecniche come il campionamento parallelo e la ricerca a fascio.
Illustrazione:
“`
Cache KV tradizionale:
[Token 1 KV][Token 2 KV][Token 3 KV]…[Token N KV]
(Allocazione della memoria contigua)
PagedAttention Cache KV:
[Block 1] -> Indirizzo fisico A
[Block 2] -> Indirizzo fisico C
[Block 3] -> Indirizzo fisico B
…
(Allocazione della memoria non contigua)
“`
Questo approccio riduce notevolmente la frammentazione della memoria e consente un utilizzo molto più efficiente della memoria della GPU.
Batching continuo
vLLM implementa il batching continuo, che elabora le richieste in modo dinamico man mano che arrivano, anziché attendere di formare lotti di dimensioni fisse. Ciò porta a una latenza inferiore e a una velocità di servizio superiore.
Esempio:
Immaginiamo un flusso di richieste in arrivo:
“`
Time 0ms: Richiesta A arriva
Time 10ms: Inizia l’elaborazione della Richiesta A
Time 15ms: Richiesta B arriva
Time 20ms: Inizia l’elaborazione della Richiesta B (in parallelo con A)
Time 25ms: Richiesta C arriva
…
“`
Con il batching continuo, vLLM può iniziare a elaborare ogni richiesta immediatamente, anziché attendere di raggrupparle in lotti predefiniti.
Campionamento parallelo efficiente
Per applicazioni che richiedono più campioni di output per prompt (ad esempio, assistenti di scrittura creativa), le capacità di condivisione della memoria di vLLM brillano. Può generare più output riutilizzando la cache KV per prefissi condivisi.
Esempio di codice utilizzando vLLM:
from vllm import LLM, SamplingParams
<p>llm = LLM(model="meta-llama/Llama-2-13b-hf")
prompts = ["Il futuro dell'AI è"]</p>
<p># Genera 3 campioni per prompt
sampling_params = SamplingParams(n=3, temperature=0.8, max_tokens=100)
outputs = llm.generate(prompts, sampling_params)</p>
<p>for output in outputs:
print(f"Prompt: {output.prompt}")
for i, out in enumerate(output.outputs):
print(f"Campionamento {i + 1}: {out.text}")</p>
Questo codice genera efficientemente più campioni per il prompt dato, sfruttando le ottimizzazioni di vLLM.
Test delle prestazioni di vLLM
Per apprezzare veramente l’impatto di vLLM, esaminiamo alcune comparazioni delle prestazioni:
Confronto della velocità di servizio
Sulla base delle informazioni fornite, vLLM supera notevolmente altre soluzioni di servizio:
– Fino a 24 volte più veloce rispetto a Hugging Face Transformers
– 2,2-3,5 volte più veloce rispetto a Hugging Face Text Generation Inference (TGI)
Illustrazione:
“`
Velocità di servizio (Token/secondo)
|
| ****
| ****
| ****
| **** ****
| **** **** ****
| **** **** ****
|————————
HF TGI vLLM
“`
Efficienza della memoria
L’algoritmo PagedAttention di vLLM comporta un utilizzo quasi ottimale della memoria:
– Solo circa il 4% di spreco di memoria, rispetto al 60-80% nei sistemi tradizionali
– Questa efficienza consente di servire modelli più grandi o di gestire più richieste concorrenti con lo stesso hardware
Iniziare con vLLM
Ora che abbiamo esplorato i benefici di vLLM, esaminiamo il processo di configurazione e utilizzo nel vostro progetto.
6.1 Installazione
L’installazione di vLLM è semplice utilizzando pip:
!pip install vllm
6.2 Utilizzo di base per l’inferenza offline
Ecco un semplice esempio di utilizzo di vLLM per la generazione di testo offline:
from vllm import LLM, SamplingParams
<p># Inizializza il modello
llm = LLM(model="meta-llama/Llama-2-13b-hf")</p>
<p># Prepara i prompt
prompts = [
"Scrivi una breve poesia sull'intelligenza artificiale:",
"Spiega il calcolo quantistico in termini semplici:"</p>
<p># Imposta i parametri di campionamento
sampling_params = SamplingParams(temperature=0.8, max_tokens=100)</p>
<p># Genera le risposte
outputs = llm.generate(prompts, sampling_params)</p>
<p># Stampa i risultati
for output in outputs:
print(f"Prompt: {output.prompt}")
print(f"Testo generato: {output.outputs[0].text}\n")</p>
Questo script dimostra come caricare un modello, impostare i parametri di campionamento e generare testo per più prompt.
6.3 Configurazione di un server vLLM
Per la gestione online, vLLM fornisce un server API compatibile con OpenAI. Ecco come configurarlo:
1. Avvia il server:
python -m vllm.entrypoints.openai.api_server --model meta-llama/Llama-2-13b-hf
2. Interroga il server utilizzando curl:
curl http://localhost:8000/v1/completions \
-H "Content-Type: application/json" \
-d '{
"model": "meta-llama/Llama-2-13b-hf",
"prompt": "I benefici dell'intelligenza artificiale includono:",
"max_tokens": 100,
"temperature": 0.7
}'
Questo setup consente di servire il vostro LLM con un’interfaccia compatibile con l’API di OpenAI, facilitando l’integrazione in applicazioni esistenti.
Argomenti avanzati su vLLM
Sebbene vLLM offra miglioramenti significativi nella gestione di LLM, ci sono ulteriori considerazioni e argomenti avanzati da esplorare:
7.1 Quantizzazione del modello
Per una gestione ancora più efficiente, specialmente su hardware con memoria limitata, possono essere utilizzate tecniche di quantizzazione. Sebbene vLLM stesso non supporti attualmente la quantizzazione, può essere utilizzato in congiunzione con modelli quantizzati:
from transformers import AutoModelForCausalLM, AutoTokenizer import torch <p># Carica un modello quantizzato model_name = "meta-llama/Llama-2-13b-hf" model = AutoModelForCausalLM.from_pretrained(model_name, device_map="auto", load_in_8bit=True) tokenizer = AutoTokenizer.from_pretrained(model_name)</p> <p># Utilizza il modello quantizzato con vLLM from vllm import LLM</p> <p>llm = LLM(model=model, tokenizer=tokenizer)
7.2 Inferenza distribuita
Per modelli estremamente grandi o applicazioni ad alto traffico, potrebbe essere necessaria l’inferenza distribuita su più GPU o macchine. Sebbene vLLM non supporti nativamente questo, può essere integrato in sistemi distribuiti utilizzando framework come Ray:
import ray
from vllm import LLM
<p>@ray.remote(num_gpus=1)
class DistributedLLM:
def __init__(self, model_name):
self.llm = LLM(model=model_name)</p>
<p>def generate(self, prompt, params):
return self.llm.generate(prompt, params)</p>
<p># Inizializza LLM distribuiti
llm1 = DistributedLLM.remote("meta-llama/Llama-2-13b-hf")
llm2 = DistributedLLM.remote("meta-llama/Llama-2-13b-hf")</p>
<p># Utilizzali in parallelo
result1 = llm1.generate.remote("Prompt 1", sampling_params)
result2 = llm2.generate.remote("Prompt 2", sampling_params)</p>
<p># Recupera i risultati
print(ray.get([result1, result2]))
7.3 Monitoraggio e osservabilità
Quando si serve LLM in produzione, il monitoraggio è cruciale. Sebbene vLLM non fornisca un monitoraggio integrato, può essere integrato con strumenti come Prometheus e Grafana:
from prometheus_client import start_http_server, Summary
from vllm import LLM
# Definisce metriche
REQUEST_TIME = Summary(‘request_processing_seconds’, ‘Tempo impiegato per l\’elaborazione della richiesta’)
# Inizializza vLLM
llm = LLM(model=”meta-llama/Llama-2-13b-hf”)
# Espone metriche
start_http_server(8000)
# Utilizza il modello con monitoraggio
@REQUEST_TIME.time()
def process_request(prompt):
return llm.generate(prompt)
# Il vostro ciclo di servizio qui
Conclusione
La gestione efficiente di modelli linguistici di grandi dimensioni è un compito complesso ma cruciale nell’era dell’AI. vLLM, con il suo innovativo algoritmo PagedAttention e la sua implementazione ottimizzata, rappresenta un notevole passo avanti nella gestione di LLM, rendendola più accessibile e conveniente.
Improvement della velocità di servizio, riduzione dello spreco di memoria e abilitazione di opzioni di servizio più flessibili, vLLM apre nuove possibilità per l’integrazione di potenti modelli linguistici in una vasta gamma di applicazioni. Sia che siate alla costruzione di un chatbot, di un sistema di generazione di contenuti o di qualsiasi altra applicazione alimentata da NLP, la comprensione e l’utilizzo di strumenti come vLLM saranno fondamentali per il successo.
















