Best practice per l'ottimizzazione dell'inferenza di modelli linguistici di grandi dimensioni con GPU su Google Kubernetes Engine (GKE)


Google Kubernetes Engine (GKE) fornisce un controllo granulare per l'inferenza dei modelli linguistici di grandi dimensioni (LLM) con prestazioni e costi ottimali. Questa guida descrive le best practice per ottimizzare l'inferenza e la pubblicazione di LLM aperti con GPU su GKE utilizzando i framework di pubblicazione vLLM e inferenza di generazione di testo (TGI).

Per un elenco di controllo riepilogativo di tutte le best practice, consulta il riepilogo dell'elenco di controllo.

Obiettivi

Questa guida è rivolta ai clienti di Generative AI, agli utenti GKE nuovi o esistenti, agli ingegneri ML e agli ingegneri LLMOps (DevOps) interessati a ottimizzare i loro workload LLM utilizzando le GPU con Kubernetes.

Al termine di questa guida, potrai:

  • Scegli tecniche di ottimizzazione LLM post-addestramento, tra cui quantizzazione, parallelismo di tensori e ottimizzazione della memoria.
  • Valuta i compromessi di alto livello quando prendi in considerazione queste tecniche di ottimizzazione.
  • Esegui il deployment di modelli LLM aperti in GKE utilizzando framework di pubblicazione come vLLM o TGI con le impostazioni di ottimizzazione abilitate.

Panoramica delle tecniche di ottimizzazione della pubblicazione di modelli LLM

A differenza dei carichi di lavoro non AI, i carichi di lavoro LLM in genere presentano una latenza più elevata e un throughput inferiore a causa della loro dipendenza dalle operazioni di moltiplicazione di matrici. Per migliorare le prestazioni di inferenza degli LLM, puoi utilizzare acceleratori hardware specializzati (ad esempio GPU e TPU) e framework di pubblicazione ottimizzati.

Puoi applicare una o più delle seguenti best practice per ridurre la latenza del carico di lavoro LLM, migliorando al contempo il throughput e l'efficienza in termini di costi:

Gli esempi in questa guida utilizzano il modello LLM Gemma 7B insieme ai framework di pubblicazione vLLM o TGI per applicare queste best practice. Tuttavia, i concetti e le funzionalità descritti sono applicabili alla maggior parte dei modelli LLM aperti.

Prima di iniziare

Prima di provare gli esempi in questa guida, completa queste attività preliminari:

  1. Segui le istruzioni riportate in queste guide per accedere al modello Gemma, preparare l'ambiente e creare e configurare le risorse Google Cloud:

    Assicurati di salvare il token di accesso a Hugging Face nel tuo secret Kubernetes.

  2. Clona il repository di esempi https://github.com/GoogleCloudPlatform/kubernetes-engine-samples/ nel tuo ambiente di sviluppo locale.

  3. Cambia la directory di lavoro in /kubernetes-engine-samples/ai-ml/llm-serving-gemma/.

Best practice: quantizzazione

La quantizzazione è una tecnica analoga alla compressione delle immagini con perdita di dati che riduce le dimensioni del modello rappresentando i pesi in formati di precisione inferiore (8 bit o 4 bit), riducendo così i requisiti di memoria. Tuttavia, come la compressione delle immagini, la quantizzazione comporta un compromesso: la riduzione delle dimensioni del modello può comportare una minore precisione.

Esistono vari metodi di quantizzazione, ognuno con i propri vantaggi e svantaggi. Alcuni, come AWQ e GPTQ, richiedono la prequantizzazione e sono disponibili su piattaforme come Hugging Face o Kaggle. Ad esempio, se applichi GPTQ al modello Llama-2 13B e AWQ al modello Gemma 7B, puoi pubblicare i modelli su una singola GPU L4 anziché su due GPU L4 senza quantizzazione.

Puoi anche eseguire la quantizzazione utilizzando strumenti come AutoAWQ e AutoGPTQ. Questi metodi possono migliorare la latenza e il throughput. Al contrario, le tecniche che utilizzano EETQ e la biblioteca bitsandbytes per la quantizzazione non richiedono modelli prequantizzati, pertanto possono essere una scelta appropriata quando le versioni prequantizzate non sono disponibili.

La tecnica di quantizzazione migliore da utilizzare dipende dai tuoi scopi specifici e dalla compatibilità della tecnica con il framework di pubblicazione che vuoi utilizzare. Per saperne di più, consulta la guida alla quantizzazione di Hugging Face.

Seleziona una di queste schede per vedere un esempio di applicazione della quantizzazione utilizzando i framework TGI o vLLM:

TGI

GKE supporta queste opzioni di quantizzazione con TGI:

  • awq
  • gptq
  • eetq
  • bitsandbytes
  • bitsandbytes-nf4
  • bitsandbytes-fp4

I metodi di quantizzazione AWQ e GPTQ richiedono modelli prequantizzati, mentre la quantizzazione EETQ e bitsandbytes può essere applicata a qualsiasi modello. Per scoprire di più su queste opzioni, consulta questo articolo su AbbracciAmore.

Per utilizzare la quantizzazione, imposta il parametro -–quantize all'avvio del server di modelli.

Lo snippet seguente mostra come ottimizzare Gemma 7B con la quantizzazione bitsandbytes utilizzando TGI su GKE.

args:
- --model-id=$(MODEL_ID)
- --num-shard=2
- --quantize=bitsandbytes

Per applicare questa configurazione, utilizza il seguente comando:

kubectl apply -f tgi/tgi-7b-bitsandbytes.yaml

vLLM

GKE supporta le seguenti opzioni di quantizzazione con vLLM:

Per utilizzare la quantizzazione del modello con vLLM, i modelli devono essere prequantizzati. Quando avvii il runtime, imposta il parametro –quantization.

Il seguente snippet mostra come ottimizzare il modello Gemma 7B con quantizzazione awq utilizzando vLLM su GKE:

args:
- --model=$(MODEL_ID)
- --tensor-parallel-size=1
- --quantization=awq
env:
- name: MODEL_ID
  value: google/gemma-7b-AWQ
resources:
  requests:
    nvidia.com/gpu: 1
  limits:
    nvidia.com/gpu: 1

Per applicare questa configurazione, utilizza il seguente comando:

kubectl apply -f vllm/vllm-7b-awq.yaml

Migliora la latenza utilizzando la quantizzazione della cache KV

Puoi utilizzare la quantizzazione della cache KV FP8 E5M2 per ridurre in modo significativo l'utilizzo di memoria della cache KV e migliorare la latenza, in particolare per dimensioni dei batch elevate. Tuttavia, questo riduce la precisione dell'inferenza.

Per attivare la quantizzazione della cache KV E5M2 FP8, imposta il parametro --kv-cache-dtype fp8_e5m2:

args:
- --model=$(MODEL_ID)
- --tensor-parallel-size=1
- --kv-cache-dtype=fp8_e5m2
- --max-model-len=1200
resources:
  requests:
    cpu: "2"
    memory: "25Gi"
    ephemeral-storage: "25Gi"
    nvidia.com/gpu: 1
  limits:
    cpu: "2"
    memory: "25Gi"
    ephemeral-storage: "25Gi"
    nvidia.com/gpu: 1

Per applicare questa configurazione, utilizza il seguente comando:

kubectl apply -f vllm/vllm-7b-kvcache.yaml

Best practice: parallelismo tensoriale

Il parallismo tensoriale è una tecnica che distribuisce il carico di calcolo su più GPU, ed è essenziale quando esegui modelli di grandi dimensioni che superano la capacità di memoria di una singola GPU. Questo approccio può essere più conveniente in quanto consente di utilizzare più GPU a prezzi accessibili anziché una sola GPU costosa. Inoltre, può migliorare il throughput dell'inferenza del modello. Il parallelismo tensoriale sfrutta il fatto che le operazioni tensoriali possono essere eseguite in modo indipendente su blocchi di dati più piccoli.

Per scoprire di più su questa tecnica, consulta la guida sul parallelismo dei tensori di Hugging Face.

Seleziona una di queste schede per vedere un esempio di applicazione del parallelismo dei tensori utilizzando i framework TGI o vLLM:

TGI

Con TGI, il runtime di pubblicazione utilizzerà tutte le GPU disponibili per il pod per impostazione predefinita. Puoi impostare il numero di GPU da utilizzare specificando il parametro --num-shard con il numero di GPU come valore.

Consulta la documentazione di Hugging Face per l'elenco dei modelli supportati per il parallelismo di tensori.

Lo snippet seguente mostra come ottimizzare il modello Gemma 7B ottimizzato per le istruzioni utilizzando il parallelismo tensoriale e due GPU L4:

args:
- --model-id=$(MODEL_ID)
- --num-shard=2

Per applicare questa configurazione, utilizza il seguente comando:

kubectl apply -f tgi/tgi-7b-it-tensorparallelism.yaml

Nei cluster GKE Autopilot, l'esecuzione di questo comando crea un pod con requisiti minimi di risorse di 21 vCPU e 78 GiB di memoria.

vLLM

vLLM supporta l'inferenza parallela di tensori distribuiti. vLLM attiva la funzionalità per impostazione predefinita se è disponibile più di una GPU.

Lo snippet seguente mostra come ottimizzare il modello Gemma 7B ottimizzato per le istruzioni utilizzando il parallelismo tensoriale e due GPU L4:

args:
- --model=$(MODEL_ID)
- --tensor-parallel-size=2

Per applicare questa configurazione, utilizza il seguente comando:

kubectl apply -f vllm/vllm-7b-it-tensorparallelism.yaml

Nei cluster GKE Autopilot, l'esecuzione di questo comando crea un pod con requisiti minimi di risorse di 21 vCPU e 78 GiB di memoria.

Best practice: ottimizzazione della memoria del modello

L'ottimizzazione dell'utilizzo della memoria degli LLM è fondamentale per un'inferenza efficiente. Questa sezione illustra le strategie di ottimizzazione del livello di attenzione, come l'attenzione paginata e l'attenzione lampo. Queste strategie migliorano l'efficienza della memoria, consentendo sequenze di input più lunghe e tempi di inattività della GPU ridotti. Questa sezione descrive anche come regolare le dimensioni di input e output del modello in base alle limitazioni di memoria e ottimizzare per framework di pubblicazione specifici.

Ottimizzazione del livello di attenzione

I livelli di self-attention consentono ai modelli di comprendere il contesto nelle attività di elaborazione del linguaggio, poiché il significato delle parole può variare a seconda del contesto. Tuttavia, questi livelli memorizzano i pesi, le chiavi (K) e i valori (V) dei token di input nella vRAM della GPU. Pertanto, con l'allungarsi della sequenza di input, si verifica una crescita quadratica delle dimensioni e del tempo di calcolo.

L'utilizzo della cache KV è particolarmente utile quando si hanno a che fare con sequenze di input lunghe, in cui l'overhead dell'auto-attenzione può diventare significativo. Questo approccio di ottimizzazione riduce l'elaborazione computazionale a una complessità lineare.

Le tecniche specifiche per ottimizzare i meccanismi di attenzione negli LLM includono:

  • Attenzione a pagine: l'attenzione a pagine migliora la gestione della memoria per modelli di grandi dimensioni e sequenze di input lunghe utilizzando tecniche di paging, simili alla memoria virtuale del sistema operativo. In questo modo, si riducono efficacemente la frammentazione e la duplicazione nella cache KV, consentendo sequenze di input più lunghe senza esaurire la memoria della GPU.
  • Flash attention: Flash attention riduce i colli di bottiglia della memoria GPU riducendo al minimo i trasferimenti di dati tra la RAM GPU e la cache L1 durante la generazione dei token. In questo modo, viene eliminato il tempo di inattività dei core di calcolo, migliorando notevolmente le prestazioni di inferenza e addestramento delle GPU.

Ottimizzazione delle dimensioni di input e output del modello

I requisiti di memoria dipendono dalle dimensioni di input e output. Un output più lungo e un contesto più ampio richiedono più risorse, mentre un output più breve e meno contesto possono far risparmiare utilizzando una GPU più piccola e meno costosa.

Seleziona una di queste schede per vedere un esempio di ottimizzazione dei requisiti di memoria di input e output del modello nei framework TGI o vLLM:

TGI

Il runtime di pubblicazione TGI controlla i requisiti di memoria durante l'avvio e non si avvia se l'impronta di memoria massima possibile del modello non rientra nella memoria GPU disponibile. Questo controllo elimina gli arresti anomali dovuti a esaurimento della memoria (OOM) su workload che richiedono molta memoria.

GKE supporta i seguenti parametri TGI per ottimizzare i requisiti di memoria del modello:

Il seguente snippet mostra come puoi pubblicare un modello Gemma 7B ottimizzato per le istruzioni con una singola GPU L4, con le impostazioni dei parametri--max-total-tokens=3072, --max-batch-prefill-tokens=512, --max-input-length=512:

args:
- --model-id=$(MODEL_ID)
- --num-shard=1
- --max-total-tokens=3072 
- --max-batch-prefill-tokens=512
- --max-input-length=512
env:
- name: MODEL_ID
  value: google/gemma-7b

Per applicare questa configurazione, utilizza il seguente comando:

kubectl apply -f tgi/tgi-7b-token.yaml

vLLM

In vLLM, configura la lunghezza del contesto del modello, che influisce direttamente sulle dimensioni della cache KV e sui requisiti di RAM della GPU. Lunghezza del contesto inferiore consente l'utilizzo di GPU più convenienti. Il valore predefinito è il numero massimo di token accettati dal modello. Se necessario, limita la lunghezza massima del contesto con --max-model-len MAX_MODEL_LEN.

Ad esempio, il modello ottimizzato per le istruzioni Gemma 7B, con una lunghezza del contesto predefinita di 8192, supera la capacità di memoria di una singola GPU NVIDIA L4. Per eseguire il deployment su un livello L4, limita la lunghezza combinata di prompt e output impostando --max-model-len su un valore inferiore a 640. Questo aggiustamento consente di eseguire il modello su una singola GPU L4 nonostante la lunghezza predefinita del contesto sia elevata.

Per eseguire il deployment con il limite di token modificato, utilizza lo snippet seguente:

args:
- --model=$(MODEL_ID)
- --tensor-parallel-size=1
- --max-model-len=600

Per applicare questa configurazione, utilizza il seguente comando:

kubectl apply -f vllm/vllm-7b-token.yaml

Riepilogo elenco di controllo

Obiettivo di ottimizzazione Esercitati
Latenza
  • Dai la priorità alle GPU potenti: valuta la possibilità di eseguire l'upgrade a GPU con capacità di calcolo e throughput I/O della memoria superiori.
  • Esplora la quantizzazione: tecniche come AWQ possono migliorare la latenza, ma tieni presente i potenziali compromessi in termini di precisione.
Velocità effettiva
  • Esegui la scalabilità orizzontale: aumenta il numero di repliche di servizio (pod) per distribuire il carico di lavoro.
  • Utilizza il parallelismo tensoriale: per i modelli di grandi dimensioni che superano la capacità di una singola GPU, utilizza il parallelismo tensoriale per distribuire i calcoli su più GPU. Per i modelli più piccoli, valuta la possibilità di utilizzare più repliche con parallelismo tensoriale pari a 1 per evitare il sovraccarico.
  • Esegui richieste collettive e quantizzazione: combina le richieste ed esplora tecniche di quantizzazione che mantengono un'accuratezza accettabile.
Convenienza
  • Scegli modelli più piccoli: seleziona i modelli all'interno di una famiglia che si adattano alle tue limitazioni di risorse e al tuo budget.
  • Applica la quantizzazione: utilizza la quantizzazione per ridurre i requisiti di memoria, in particolare quando hai a che fare con modelli più grandi.
  • Limita la lunghezza del contesto: limita la lunghezza del contesto per ridurre ulteriormente l'utilizzo della memoria e consentire l'esecuzione su GPU più piccole e più convenienti.

Passaggi successivi