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

Google Kubernetes Engine (GKE) offre un controllo granulare per l'inferenza di modelli linguistici di grandi dimensioni (LLM) con prestazioni e costi ottimali. Questa guida descrive le best practice per l'ottimizzazione dell'inferenza e della gestione degli LLM aperti con GPU su GKE utilizzando i framework di gestione vLLM e Text Generation Inference (TGI).

Per un elenco di controllo riepilogativo di tutte le best practice, consulta l'Elenco di controllo di riepilogo.

Per una panoramica consolidata di tutte le best practice di GKE, consulta Best practice per GKE.

Obiettivi

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

Al termine di questa guida, potrai:

  • Scegliere le tecniche di ottimizzazione LLM post-addestramento, tra cui la quantizzazione, il parallelismo dei tensori e l'ottimizzazione della memoria.
  • Valutare i compromessi di alto livello quando prendi in considerazione queste tecniche di ottimizzazione.
  • Eseguire il deployment di modelli LLM aperti su GKE utilizzando framework di gestione come vLLM o TGI con le impostazioni di ottimizzazione attivate.

Panoramica delle tecniche di ottimizzazione della gestione degli LLM

A differenza dei workload non AI, i workload LLM in genere presentano una latenza più elevata e una velocità effettiva inferiore a causa della loro dipendenza dalle operazioni di moltiplicazione delle matrici. Per migliorare le prestazioni dell'inferenza LLM, puoi utilizzare acceleratori hardware specializzati (ad esempio GPU e TPU) e framework di gestione ottimizzati.

Puoi applicare una o più delle seguenti best practice per ridurre la latenza dei workload LLM migliorando al contempo la velocità effettiva e l'efficienza in termini di costi:

Gli esempi in questa guida utilizzano l'LLM Gemma 7B insieme ai framework di gestione vLLM o TGI per applicare queste best practice; tuttavia, i concetti e le funzionalità descritti sono applicabili alla maggior parte degli LLM aperti più diffusi.

Prima di iniziare

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

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

    Assicurati di salvare il token di accesso a Hugging Face nel secret di 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 riduzione della precisione.

Esistono vari metodi di quantizzazione, ognuno con i propri vantaggi e svantaggi. Alcuni, come AWQ e GPTQ, richiedono la pre-quantizzazione 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 erogare 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 la velocità effettiva. Al contrario, le tecniche che utilizzano EETQ e la bitsandbytes libreria per la quantizzazione non richiedono modelli pre-quantizzati, quindi possono essere una scelta adatta quando le versioni pre-quantizzate non sono disponibili.

La tecnica di quantizzazione migliore da utilizzare dipende dai tuoi obiettivi specifici e dalla compatibilità della tecnica con il framework di gestione 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 pre-quantizzati, mentre la quantizzazione EETQ e bitsandbytes può essere applicata a qualsiasi modello. Per saperne di più su queste opzioni, consulta questo articolo di Hugging Face.

Per utilizzare la quantizzazione, imposta il -–quantize parametro quando avvii il server dei modelli.

Il seguente snippet 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 queste opzioni di quantizzazione con vLLM:

Per utilizzare la quantizzazione dei modelli con vLLM, i modelli devono essere pre-quantizzati. Quando avvii il runtime, imposta il parametro –quantization.

Il seguente snippet mostra come ottimizzare il modello Gemma 7B con la 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

Migliorare la latenza utilizzando la quantizzazione della cache KV

Puoi utilizzare la quantizzazione della cache KV FP8 E5M2 per ridurre significativamente il footprint della memoria della cache KV e migliorare la latenza, soprattutto per le dimensioni dei batch di grandi dimensioni. Tuttavia, ciò riduce la precisione dell'inferenza.

Per attivare la quantizzazione della cache KV FP8 E5M2, 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 dei tensori

Il parallelismo dei tensori è una tecnica che distribuisce il carico di calcolo su più GPU, essenziale quando esegui modelli di grandi dimensioni che superano la capacità di memoria di una singola GPU. Questo approccio può essere più conveniente in termini di costi, in quanto ti consente di utilizzare più GPU economiche anziché una singola GPU costosa. Può anche migliorare la velocità effettiva dell'inferenza del modello. Il parallelismo dei tensori sfrutta il fatto che le operazioni sui tensori possono essere eseguite in modo indipendente su blocchi di dati più piccoli.

Per saperne di più su questa tecnica, consulta la guida al 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 gestione utilizzerà per impostazione predefinita tutte le GPU disponibili per il pod. 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 dei tensori.

Il seguente snippet mostra come ottimizzare il modello Gemma 7B ottimizzato per le istruzioni utilizzando il parallelismo dei tensori 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 dei tensori distribuiti. vLLM abilita la funzionalità per impostazione predefinita se sono disponibili più GPU.

Il seguente snippet mostra come ottimizzare il modello Gemma 7B ottimizzato per le istruzioni utilizzando il parallelismo dei tensori 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 della memoria utilizzata degli LLM è fondamentale per un'inferenza efficiente. Questa sezione introduce le strategie di ottimizzazione del livello di attenzione, come l'attenzione paginata e l'attenzione flash. Queste strategie migliorano l'efficienza della memoria, consentendo sequenze di input più lunghe e riducendo il tempo di inattività della GPU. Questa sezione descrive anche come regolare le dimensioni di input e output del modello per adattarle ai vincoli di memoria e ottimizzarle per framework di gestione specifici.

Ottimizzazione del livello di attenzione

I livelli di auto-attenzione consentono ai modelli di comprendere il contesto nelle attività di elaborazione del linguaggio, poiché i significati delle parole possono 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, man mano che la sequenza di input si allunga, si verifica una crescita quadratica delle dimensioni e del tempo di calcolo.

L'utilizzo della memorizzazione nella cache KV è particolarmente utile quando si utilizzano sequenze di input lunghe, in cui l'overhead dell'auto-attenzione può diventare significativo. Questo approccio di ottimizzazione riduce l'elaborazione computazionale alla complessità lineare.

Le tecniche specifiche per l'ottimizzazione dei meccanismi di attenzione negli LLM includono:

  • Attenzione paginata: L'attenzione paginata migliora la gestione della memoria per i modelli di grandi dimensioni e le sequenze di input lunghe utilizzando tecniche di paging, simili alla memoria virtuale del sistema operativo. Ciò riduce efficacemente la frammentazione e la duplicazione nella cache KV, consentendo sequenze di input più lunghe senza esaurire la memoria della GPU.
  • Attenzione flash: l'attenzione flash riduce i colli di bottiglia della memoria della GPU riducendo al minimo i trasferimenti di dati tra la RAM della GPU e la cache L1 durante la generazione dei token. In questo modo si elimina il tempo di inattività per i core di calcolo, migliorando significativamente le prestazioni di inferenza e addestramento per le 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 un contesto più piccolo possono ridurre i costi utilizzando una GPU più piccola ed economica.

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 erogazione TGI verifica i requisiti di memoria durante l'avvio e non viene avviato se il footprint massimo possibile della memoria del modello non rientra nella memoria GPU disponibile. Questo controllo elimina gli arresti anomali per memoria insufficiente (OOM) nei workload che richiedono molta memoria.

GKE supporta i seguenti parametri TGI per l'ottimizzazione dei requisiti di memoria del modello:

Il seguente snippet mostra come erogare 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 finestra contestuale del modello, che influisce direttamente sulle dimensioni della cache KV e sui requisiti di RAM della GPU. Lunghezze di contesto più piccole consentono l'utilizzo di GPU più economiche. Il valore predefinito è il numero massimo di token accettati dal modello. Se necessario, limita la lunghezza massima della finestra contestuale con --max-model-len MAX_MODEL_LEN.

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

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

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

Elenco di controllo di riepilogo

Obiettivo di ottimizzazione Esercitazioni
Latenza
  • Dai la priorità alle GPU potenti: valuta l'upgrade a GPU con funzionalità di calcolo e velocità effettiva di I/O della memoria più elevate.
  • 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 gestione (pod) per distribuire il carico di lavoro.
  • Utilizza il parallelismo dei tensori: per i modelli di grandi dimensioni che superano la capacità di una singola GPU, utilizza il parallelismo dei tensori per distribuire i calcoli su più GPU. Per i modelli più piccoli valuta la possibilità di utilizzare più repliche con parallelismo dei tensori di `1` per evitare l'overhead.
  • Raggruppa le richieste batch e quantizza: combina le richieste ed esplora le tecniche di quantizzazione che mantengono una precisione accettabile.
Efficienza in termini di costi
  • Scegli modelli più piccoli: seleziona i modelli all'interno di una famiglia che si adattano ai vincoli di risorse e al budget.
  • Applica la quantizzazione: utilizza la quantizzazione per ridurre i requisiti di memoria, soprattutto quando utilizzi modelli più grandi.
  • Limita la finestra contestuale: limita la finestra contestuale per ridurre ulteriormente la memoria utilizzata e consentire l'esecuzione su GPU più piccole ed economiche.

Passaggi successivi