Seguici sui social

Ottimizzazione della memoria per l'inferenza e la messa a punto di modelli linguistici di grandi dimensioni

Intelligenza Artificiale

Ottimizzazione della memoria per l'inferenza e la messa a punto di modelli linguistici di grandi dimensioni

mm
Memoria per l'inferenza di modelli linguistici di grandi dimensioni

I modelli linguistici di grandi dimensioni (LLM) come GPT-4, Bloom e LLaMA hanno raggiunto notevoli capacità scalando fino a miliardi di parametri. Tuttavia, l'implementazione di questi enormi modelli per l'inferenza o la messa a punto è impegnativa a causa dei loro immensi requisiti di memoria. In questo blog tecnico esploreremo le tecniche per stimare e ottimizzare il consumo di memoria durante l'inferenza LLM e la messa a punto tra varie configurazioni hardware.

Comprensione dei requisiti di memoria

La memoria richiesta per caricare un LLM è determinata principalmente dal numero di parametri e dalla precisione numerica utilizzata per archiviare i parametri. Una semplice regola pratica è:

  • Il caricamento di un modello con X miliardi di parametri richiede circa 4 GB di VRAM in 32-bit precisione del galleggiante
  • Il caricamento di un modello con X miliardi di parametri richiede circa 2 GB di VRAM in 16-bit precisione bfloat16/float16

Ad esempio, il caricamento del modello GPT-175 con parametro 3B richiederebbe circa 350 GB di VRAM con precisione bfloat16. Ad oggi, le più grandi GPU disponibili in commercio come NVIDIA A100 e H100 offrono solo 80 GB di VRAM, richiedendo tecniche di parallelismo del tensore e di parallelismo del modello.

Durante l'inferenza, l'impronta della memoria è dominata dai parametri del modello e dai tensori di attivazione temporanei prodotti. Una stima di alto livello per l'utilizzo massimo della memoria durante l'inferenza è la somma della memoria richiesta per caricare i parametri del modello e della memoria per le attivazioni.

Quantificazione della memoria di inferenza

Quantifichiamo i requisiti di memoria per l'inferenza utilizzando il modello OctoCode, che ha circa 15 miliardi di parametri in formato bfloat16 (~ 31 GB). Useremo il Libreria Transformers per caricare il modello e generare testo:

from transformers import AutoModelForCausalLM, AutoTokenizer, pipeline
import torch

model = AutoModelForCausalLM.from_pretrained("bigcode/octocoder",
torch_dtype=torch.bfloat16,
device_map="auto",
pad_token_id=0)
tokenizer = AutoTokenizer.from_pretrained("bigcode/octocoder")
pipe = pipeline("text-generation", model=model, tokenizer=tokenizer)

prompt = "Question: Please write a Python function to convert bytes to gigabytes.\n\nAnswer:"
result = pipe(prompt, max_new_tokens=60)[0]["generated_text"][len(prompt):]

def bytes_to_gigabytes(bytes):
return bytes / 1024 / 1024 / 1024

bytes_to_gigabytes(torch.cuda.max_memory_allocated())

Produzione:

29.0260648727417

L'utilizzo massimo della memoria della GPU è di circa 29 GB, in linea con la nostra stima di 31 GB per il caricamento dei parametri del modello in formato bfloat16.

Ottimizzazione della memoria di inferenza con la quantizzazione

Sebbene bfloat16 sia la precisione comune utilizzata per l'addestramento degli LLM, i ricercatori hanno scoperto che quantizzare i pesi del modello su tipi di dati a precisione inferiore come interi a 8 bit (int8) o interi a 4 bit può ridurre significativamente l'utilizzo della memoria con una perdita minima di precisione per attività di inferenza come generazione del testo.

Vediamo il risparmio di memoria derivante dalla quantizzazione a 8 e 4 bit del modello OctoCode:

</div>
# 8-bit quantization
model = AutoModelForCausalLM.from_pretrained("bigcode/octocoder", load_in_8bit=True, 
pad_token_id=0)
pipe = pipeline("text-generation", model=model, tokenizer=tokenizer)
result = pipe(prompt, max_new_tokens=60)[0]["generated_text"][len(prompt):]
bytes_to_gigabytes(torch.cuda.max_memory_allocated())</pre>
Produzione:
15.219234466552734
# 4-bit quantization
model = AutoModelForCausalLM.from_pretrained("bigcode/octocoder", load_in_4bit=True,
low_cpu_mem_usage=True, pad_token_id=0)
pipe = pipeline("text-generation", model=model, tokenizer=tokenizer)
result = pipe(prompt, max_new_tokens=60)[0]["generated_text"][len(prompt):]
bytes_to_gigabytes(torch.cuda.max_memory_allocated())

Produzione:

9.543574333190918

Con la quantizzazione a 8 bit, il requisito di memoria scende da 31 GB a 15 GB, mentre la quantizzazione a 4 bit lo riduce ulteriormente a soli 9.5 GB! Ciò consente di eseguire il modello OctoCode con parametri 15B su GPU consumer come RTX 3090 (VRAM da 24 GB).

Tuttavia, è importante notare che una quantizzazione più aggressiva, come quella a 4 bit, può talvolta comportare un degrado della precisione rispetto alla precisione a 8 bit o bfloat16. Esiste un compromesso tra risparmio di memoria e precisione che gli utenti dovrebbero valutare in base al loro caso d'uso.

La quantizzazione è una tecnica potente che può consentire l'implementazione LLM in ambienti con risorse limitate come istanze cloud, dispositivi edge o persino telefoni cellulari riducendo drasticamente l'ingombro della memoria.

Stima della memoria per la messa a punto

Mentre la quantizzazione viene utilizzata principalmente per un'inferenza efficiente, tecniche come il parallelismo del tensore e il parallelismo del modello sono cruciali per la gestione dei requisiti di memoria durante l'addestramento o ritocchi di grandi modelli linguistici.

Il consumo massimo di memoria durante la regolazione fine è in genere 3-4 volte superiore all'inferenza a causa dei requisiti di memoria aggiuntivi per:

  • Sfumature
  • Stati dell'ottimizzatore
  • Attivazioni dal passaggio in avanti archiviate per la propagazione all'indietro

Una stima prudente è che la messa a punto di un LLM con X miliardi di parametri richiede circa 4 * (2X) = 8 GB di VRAM con precisione bfloat16.

Ad esempio, la messa a punto del modello LLaMA con parametri 7B richiederebbe circa 7 * 8 = 56 GB di VRAM per GPU con precisione bfloat16. Ciò supera la capacità di memoria delle attuali GPU, rendendo necessarie tecniche di messa a punto distribuite.

Tecniche di fine tuning distribuito

Sono stati proposti diversi metodi di regolazione fine distribuita per superare i vincoli di memoria della GPU per modelli di grandi dimensioni:

  1. Parallelismo dei dati: L'approccio classico del parallelismo dei dati replica l'intero modello su più GPU suddividendo e distribuendo i batch di dati di training. Ciò riduce il tempo di addestramento in modo lineare con il numero di GPU, ma non riduce i requisiti di memoria di picco su ciascuna GPU.
  2. ZeRO Fase 3: una forma avanzata di parallelismo dei dati che suddivide i parametri del modello, i gradienti e gli stati dell'ottimizzatore tra le GPU. Riduce la memoria rispetto al classico parallelismo dei dati mantenendo solo i dati partizionati richiesti su ciascuna GPU durante le diverse fasi di training.
  3. Parallelismo tensoriale: invece di replicare il modello, il parallelismo tensore divide i parametri del modello in righe o colonne e li distribuisce tra le GPU. Ogni GPU opera su un insieme partizionato di parametri, gradienti e stati di ottimizzazione, con conseguente notevole risparmio di memoria.
  4. Parallelismo delle condutture: questa tecnica suddivide i livelli del modello tra diverse GPU/worker, con ciascun dispositivo che esegue un sottoinsieme di livelli. Le attivazioni vengono passate tra i lavoratori, riducendo i picchi di memoria ma aumentando il sovraccarico di comunicazione.

La stima dell'utilizzo della memoria per questi metodi distribuiti non è banale poiché la distribuzione di parametri, gradienti, attivazioni e stati dell'ottimizzatore varia a seconda delle tecniche. Inoltre, diversi componenti come il corpo del trasformatore e la testa di modellazione del linguaggio possono mostrare comportamenti diversi di allocazione della memoria.

La soluzione LLMem

I ricercatori hanno recentemente proposto LLMem, una soluzione che stima accuratamente il consumo di memoria della GPU quando si applicano metodi di ottimizzazione distribuiti a LLM su più GPU.

Stima dell'utilizzo della memoria della GPU per la messa a punto di LLM preaddestrato

Stima dell'utilizzo della memoria della GPU per la messa a punto di LLM preaddestrato

LLMem considera fattori come la ricombinazione dei parametri prima del calcolo (ZeRO Stage 3), la raccolta dell'output nel passaggio all'indietro (parallelismo del tensore) e le diverse strategie di allocazione della memoria per il corpo del trasformatore e la testa di modellazione del linguaggio.

I risultati sperimentali mostrano che LLMem può stimare il picco di utilizzo della memoria GPU per la messa a punto di LLM su una singola GPU con tassi di errore fino all'1.6%, superando il tasso di errore medio di DNNMem all'avanguardia di 42.6%. Quando si applicano metodi di regolazione fine distribuiti a LLM con oltre un miliardo di parametri su più GPU, LLMem raggiunge un impressionante tasso di errore medio di 3.0%.

Stimando accuratamente in anticipo i requisiti di memoria, LLMem può aiutare gli utenti a selezionare il metodo di regolazione fine distribuito più efficiente che evita problemi di memoria insufficiente riducendo al minimo i tempi di formazione.

Tecniche emergenti

Sebbene la quantizzazione, il parallelismo tensore e il parallelismo dei modelli siano tecniche consolidate, i ricercatori continuano a esplorare nuovi metodi per ampliare i confini di un addestramento e di un'implementazione LLM efficienti.

  1. LoRA e QLoRA: Queste tecniche prevedono l'addestramento di un modulo adattatore residuo più piccolo per aggiornare il LLM pre-addestrato con nuove conoscenze, anziché perfezionare direttamente l'enorme numero di parametri. Ciò può comportare un notevole risparmio di memoria, pur mantenendo la maggior parte delle prestazioni del modello.
  2. FlashAttenzione: Il meccanismo di auto-attenzione è un collo di bottiglia della memoria e del calcolo nei modelli con trasformatore. FlashAttention si avvicina all'attenzione standard con complessità lineare, riducendo i requisiti di memoria da quadratici a lineari nella lunghezza della sequenza di input.
  3. Miscela di esperti: questo approccio instrada in modo condizionale ciascun campione di dati di input a un modello esperto specializzato invece di elaborarlo attraverso l'intero modello. Questa scarsità dinamica può far risparmiare memoria attivando solo un sottoinsieme di esperti per ciascun campione.
  4. Chirurgia a modello invertito: I ricercatori hanno esplorato la compressione del modello chirurgico rimuovendo in modo iterativo componenti meno importanti come le teste di attenzione per bilanciare memoria/velocità con precisione.
  5. Scarico: infine, le tecniche che scaricano parametri, stati dell'ottimizzatore o attivazioni sulla RAM o sul disco della CPU possono integrare la memoria GPU limitata per i modelli di grandi dimensioni.

Questi metodi all'avanguardia illustrano il vivace ecosistema di ricerca incentrato sulla democratizzazione di una formazione LLM efficiente e sull'implementazione in diversi ambienti hardware.

Conclusione

I requisiti di memoria dei modelli linguistici di grandi dimensioni pongono sfide significative per la loro adozione diffusa nelle applicazioni del mondo reale. Comprendendo le tecniche di stima della memoria e sfruttando la quantizzazione, le strategie di formazione distribuite e le innovazioni emergenti, possiamo ottimizzare le implementazioni LLM su dispositivi con risorse limitate.

Strumenti come LLMem aprono la strada verso una stima accurata della memoria, consentendo agli utenti di selezionare la configurazione di ottimizzazione più adatta. Man mano che l'hardware si evolve e la ricerca avanza, possiamo anticipare formazione e inferenza LLM più efficienti, guidando il progresso nell'elaborazione del linguaggio naturale e nell'intelligenza artificiale.

Trovare il giusto equilibrio tra capacità del modello, accuratezza e utilizzo delle risorse sarà fondamentale per sbloccare tutto il potenziale di modelli linguistici di grandi dimensioni in diversi domini e casi d’uso. Adottando tecniche di ottimizzazione della memoria, ci avviciniamo a un futuro in cui l'intelligenza artificiale con linguaggio all'avanguardia sarà accessibile, scalabile e sostenibile.

Ho trascorso gli ultimi cinque anni immergendomi nell'affascinante mondo del Machine Learning e del Deep Learning. La mia passione e competenza mi hanno portato a contribuire a oltre 50 diversi progetti di ingegneria del software, con un focus particolare su AI/ML. La mia continua curiosità mi ha anche attirato verso l'elaborazione del linguaggio naturale, un campo che non vedo l'ora di esplorare ulteriormente.