Strumenti di IA 101
Configurazione dell’addestramento, del fine-tuning e dell’inferenza di LLM con GPU NVIDIA e CUDA
Il campo dell’intelligenza artificiale (AI) ha assistito a notevoli progressi negli ultimi anni, e al centro di tutto ciò si trova la potente combinazione di unità di elaborazione grafica (GPU) e piattaforma di calcolo parallelo.
Modelli come GPT, BERT, e più recentemente Llama, Mistral sono in grado di comprendere e generare testi simili a quelli umani con una fluidità e una coerenza senza precedenti. Tuttavia, l’addestramento di questi modelli richiede grandi quantità di dati e risorse computazionali, rendendo le GPU e CUDA strumenti indispensabili in questo sforzo.
Questa guida completa ti accompagnerà attraverso il processo di configurazione di una GPU NVIDIA (NVDA ) su Ubuntu, coprendo l’installazione di componenti software essenziali come il driver NVIDIA, CUDA Toolkit, cuDNN, PyTorch e altro.
L’ascesa dei framework AI accelerati da CUDA
L’apprendimento profondo accelerato da GPU è stato alimentato dallo sviluppo di framework AI popolari che sfruttano CUDA per il calcolo efficiente. Framework come TensorFlow, PyTorch e MXNet hanno un supporto integrato per CUDA, consentendo un’integrazione senza soluzione di continuità dell’accelerazione GPU nelle pipeline di apprendimento profondo.
Secondo lo studio NVIDIA Data Center Deep Learning Product Performance, i modelli di apprendimento profondo accelerati da CUDA possono raggiungere prestazioni fino a 100 volte più veloci rispetto alle implementazioni basate su CPU.
La tecnologia Multi-Instance GPU (MIG) di NVIDIA, introdotta con l’architettura Ampere, consente di suddividere una singola GPU in più istanze sicure, ognuna con le proprie risorse dedicate. Questa funzione consente una condivisione efficiente delle risorse GPU tra più utenti o carichi di lavoro, massimizzando l’utilizzo e riducendo i costi complessivi.
Accelerazione dell’inferenza LLM con NVIDIA TensorRT
Mentre le GPU sono state strumentali nell’addestramento degli LLM, un’inferenza efficiente è altrettanto cruciale per la distribuzione di questi modelli in ambienti di produzione. NVIDIA TensorRT, un ottimizzatore e runtime di inferenza di apprendimento profondo ad alte prestazioni, svolge un ruolo vitale nell’accelerazione dell’inferenza LLM sulle GPU abilitate CUDA.
Secondo i benchmark di NVIDIA, TensorRT può fornire fino a 8 volte di prestazioni di inferenza più veloci e un costo totale di proprietà 5 volte inferiore rispetto all’inferenza basata su CPU per grandi modelli linguistici come GPT-3.
L’impegno di NVIDIA per le iniziative open-source è stato una forza trainante dietro l’ampia adozione di CUDA nella comunità di ricerca AI. Progetti come cuDNN, cuBLAS e NCCL sono disponibili come librerie open-source, consentendo ai ricercatori e agli sviluppatori di sfruttare appieno le potenzialità di CUDA per il loro apprendimento profondo.
Installazione
Quando si utilizza lo sviluppo AI, utilizzare i driver e le librerie più recenti potrebbe non essere sempre la scelta migliore. Ad esempio, mentre il driver NVIDIA più recente (545.xx) supporta CUDA 12.3, PyTorch e altre librerie potrebbero non supportare ancora questa versione. Pertanto, useremo driver versione 535.146.02 con CUDA 12.2 per garantire la compatibilità.
Passaggi di installazione
1. Installazione del driver NVIDIA
Innanzitutto, identifica il modello della tua GPU. Per questa guida, useremo la GPU NVIDIA. Visita la pagina di download del driver NVIDIA, seleziona il driver appropriato per la tua GPU e prendi nota della versione del driver.
Per verificare la presenza di pacchetti GPU precompilati su Ubuntu, esegui:
sudo ubuntu-drivers list --gpgpu
Riavvia il computer e verifica l’installazione:
nvidia-smi
2. Installazione di CUDA Toolkit
CUDA Toolkit fornisce l’ambiente di sviluppo per la creazione di applicazioni accelerate da GPU ad alte prestazioni.
Per un setup non LLM/apprendimento profondo, puoi utilizzare:
sudo apt install nvidia-cuda-toolkit <p>Tuttavia, per garantire la compatibilità con BitsAndBytes, seguiremo questi passaggi:</p> [code language=&quot;BASH&quot;] <p>git clone https://github.com/TimDettmers/bitsandbytes.git cd bitsandbytes/ bash install_cuda.sh 122 ~/local 1</p>
Verifica l’installazione:
~/local/cuda-12.2/bin/nvcc --version
Imposta le variabili di ambiente:
<p>export CUDA_HOME=/home/roguser/local/cuda-12.2/ export LD_LIBRARY_PATH=/home/roguser/local/cuda-12.2/lib64 export BNB_CUDA_VERSION=122 export CUDA_VERSION=122</p>
3. Installazione di cuDNN
Scarica il pacchetto cuDNN dal sito web di NVIDIA Developer. Installalo con:
<p>sudo apt install ./cudnn-local-repo-ubuntu2204-8.9.7.29_1.0-1_amd64.deb</p>
Segui le istruzioni per aggiungere la keyring:
<p>sudo cp /var/cudnn-local-repo-ubuntu2204-8.9.7.29/cudnn-local-08A7D361-keyring.gpg /usr/share/keyrings/</p>
Installa le librerie cuDNN:
<p>sudo apt update sudo apt install libcudnn8 libcudnn8-dev libcudnn8-samples</p>
4. Configurazione dell’ambiente virtuale Python
Ubuntu 22.04 include Python 3.10. Installa venv:
<p>sudo apt-get install python3-pip sudo apt install python3.10-venv</p>
Crea e attiva l’ambiente virtuale:
<p>cd mkdir test-gpu cd test-gpu python3 -m venv venv source venv/bin/activate</p>
5. Installazione di BitsAndBytes da sorgente
Naviga nella directory di BitsAndBytes e costruisci da sorgente:
<p>cd ~/bitsandbytes CUDA_HOME=/home/roguser/local/cuda-12.2/ \ LD_LIBRARY_PATH=/home/roguser/local/cuda-12.2/lib64 \ BNB_CUDA_VERSION=122 \ CUDA_VERSION=122 \ make cuda12x</p> <p>CUDA_HOME=/home/roguser/local/cuda-12.2/ \ LD_LIBRARY_PATH=/home/roguser/local/cuda-12.2/lib64 \ BNB_CUDA_VERSION=122 \ CUDA_VERSION=122 \ python setup.py install</p>
6. Installazione di PyTorch
Installa PyTorch con il seguente comando:
<p>pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121</p>
7. Installazione di Hugging e Transformers
Installa le librerie transformers e accelerate:
<p>pip install transformers pip install accelerate</p>
Il potere dell’elaborazione parallela
Alla base, le GPU sono processori paralleli progettati per gestire migliaia di thread contemporaneamente in modo efficiente. Questa architettura le rende adatte per le attività computazionalmente intensive coinvolte nell’addestramento di modelli di apprendimento profondo, inclusi gli LLM. La piattaforma CUDA, sviluppata da NVIDIA, fornisce un ambiente software che consente agli sviluppatori di sfruttare appieno le potenzialità di queste GPU, consentendo loro di scrivere codice che possa sfruttare le capacità di elaborazione parallela del hardware.
Accelerazione dell’addestramento di LLM con GPU e CUDA.
L’addestramento di grandi modelli linguistici è un compito computazionalmente impegnativo che richiede l’elaborazione di grandi quantità di dati testuali e l’esecuzione di numerose operazioni matriciali. Le GPU, con le loro migliaia di core e alta larghezza di banda della memoria, sono ideali per questi compiti. Sfruttando CUDA, gli sviluppatori possono ottimizzare il loro codice per sfruttare le capacità di elaborazione parallela delle GPU, riducendo notevolmente il tempo necessario per l’addestramento degli LLM.
Ad esempio, l’addestramento di GPT-3, uno dei più grandi modelli linguistici fino ad oggi, è stato reso possibile grazie all’utilizzo di migliaia di GPU NVIDIA che eseguivano codice ottimizzato per CUDA. Ciò ha consentito al modello di essere addestrato su una quantità di dati senza precedenti, portando alla sua impressionante prestazione in compiti di linguaggio naturale.
<p>import torch
import torch.nn as nn
import torch.optim as optim
from transformers import GPT2LMHeadModel, GPT2Tokenizer</p>
<p># Carica il modello GPT-2 pre-addestrato e il tokenizer
model = GPT2LMHeadModel.from_pretrained(&#039;gpt2&#039;)
tokenizer = GPT2Tokenizer.from_pretrained(&#039;gpt2&#039;)</p>
<p># Sposta il modello sulla GPU se disponibile
device = torch.device(&quot;cuda&quot; if torch.cuda.is_available() else &quot;cpu&quot;)
model = model.to(device)</p>
<p># Definisci i dati di addestramento e gli iperparametri
train_data = [...] # I tuoi dati di addestramento
batch_size = 32
num_epochs = 10
learning_rate = 5e-5</p>
<p># Definisci la funzione di perdita e l'ottimizzatore
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=learning_rate)</p>
<p># Ciclo di addestramento
for epoch in range(num_epochs):
for i in range(0, len(train_data), batch_size):
# Prepara le sequenze di input e target
inputs, targets = train_data[i:i+batch_size]
inputs = tokenizer(inputs, return_tensors=&quot;pt&quot;, padding=True)
inputs = inputs.to(device)
targets = targets.to(device)</p>
<p># Passo forward
outputs = model(**inputs, labels=targets)
loss = outputs.loss</p>
<p># Passo backward e ottimizzazione
optimizer.zero_grad()
loss.backward()
optimizer.step()</p>
<p>print(f&#039;Epoch {epoch+1}/{num_epochs}, Loss: {loss.item()}&#039;)</p>
In questo esempio di codice, dimostriamo l’addestramento di un modello di linguaggio GPT-2 utilizzando PyTorch e le GPU abilitate CUDA. Il modello viene caricato sulla GPU (se disponibile) e il ciclo di addestramento sfrutta il parallelismo delle GPU per eseguire passi forward e backward efficienti, accelerando il processo di addestramento.
Librerie accelerate da CUDA per l’apprendimento profondo
Oltre alla piattaforma CUDA stessa, NVIDIA e la comunità open-source hanno sviluppato una serie di librerie accelerate da CUDA che consentono l’implementazione efficiente di modelli di apprendimento profondo, inclusi gli LLM. Queste librerie forniscono implementazioni ottimizzate di operazioni comuni, come moltiplicazioni di matrici, convoluzioni e funzioni di attivazione, consentendo agli sviluppatori di concentrarsi sull’architettura del modello e sul processo di addestramento piuttosto che sull’ottimizzazione a basso livello.
Una di queste librerie è cuDNN (CUDA Deep Neural Network library), che fornisce implementazioni altamente ottimizzate di routine standard utilizzate nei reti neurali profonde. Sfruttando cuDNN, gli sviluppatori possono accelerare notevolmente l’addestramento e l’inferenza dei loro modelli, raggiungendo guadagni di prestazioni di diversi ordini di grandezza rispetto alle implementazioni basate su CPU.
<p>import torch import torch.nn as nn import torch.nn.functional as F from torch.cuda.amp import autocast</p> <p>class ResidualBlock(nn.Module): def __init__(self, in_channels, out_channels, stride=1): super().__init__() self.conv1 = nn.Conv2d(in_channels, out_channels, kernel_size=3, stride=stride, padding=1, bias=False) self.bn1 = nn.BatchNorm2d(out_channels) self.conv2 = nn.Conv2d(out_channels, out_channels, kernel_size=3, stride=1, padding=1, bias=False) self.bn2 = nn.BatchNorm2d(out_channels) self.shortcut = nn.Sequential() if stride != 1 or in_channels != out_channels: self.shortcut = nn.Sequential( nn.Conv2d(in_channels, out_channels, kernel_size=1, stride=stride, bias=False), nn.BatchNorm2d(out_channels))</p> <p>def forward(self, x): with autocast(): out = F.relu(self.bn1(self.conv1(x))) out = self.bn2(self.conv2(out)) out += self.shortcut(x) out = F.relu(out) return out</p>
In questo esempio di codice, definiamo un blocco residuale per una rete neurale convoluzionale (CNN) utilizzando PyTorch. Il gestore di contesto autocast di PyTorch è utilizzato per abilitare l’addestramento a precisione mista, che può fornire guadagni di prestazioni significativi sulle GPU abilitate CUDA mantenendo un’alta accuratezza. La funzione F.relu è ottimizzata da cuDNN, garantendo un’esecuzione efficiente sulle GPU.
Addestramento multi-GPU e distribuito per la scalabilità
Mentre gli LLM e i modelli di apprendimento profondo continuano a crescere in termini di dimensioni e complessità, le esigenze computazionali per l’addestramento di questi modelli aumentano anch’esse. Per affrontare questa sfida, i ricercatori e gli sviluppatori hanno iniziato a utilizzare tecniche di addestramento multi-GPU e distribuito, che consentono di sfruttare la potenza di calcolo combinata di più GPU su più macchine.
CUDA e librerie associate, come NCCL (NVIDIA Collective Communications Library), forniscono primitive di comunicazione efficienti che consentono la trasferimento e la sincronizzazione dei dati tra più GPU, abilitando l’addestramento distribuito su una scala senza precedenti.
&lt;/pre&gt; import torch.distributed as dist <p>from torch.nn.parallel import DistributedDataParallel as DDP</p> <p># Inizializza l'addestramento distribuito dist.init_process_group(backend=&#039;nccl&#039;, init_method=&#039;...&#039;) local_rank = dist.get_rank() torch.cuda.set_device(local_rank)</p> <p># Crea il modello e spostalo sulla GPU model = MyModel().cuda()</p> <p># Avvolgi il modello con DDP model = DDP(model, device_ids=[local_rank])</p> <p># Ciclo di addestramento (distribuito) for epoch in range(num_epochs): for data in train_loader: inputs, targets = data inputs = inputs.cuda(non_blocking=True) targets = targets.cuda(non_blocking=True)</p> <p>outputs = model(inputs) loss = criterion(outputs, targets)</p> <p>optimizer.zero_grad() loss.backward() optimizer.step()</p>
In questo esempio, dimostriamo l’addestramento distribuito utilizzando il modulo DistributedDataParallel (DDP) di PyTorch. Il modello viene avvolto in DDP, che gestisce automaticamente la parallelizzazione dei dati, la sincronizzazione dei gradienti e la comunicazione tra più GPU utilizzando NCCL. Questo approccio consente di scalare efficientemente il processo di addestramento su più macchine, consentendo ai ricercatori e agli sviluppatori di addestrare modelli più grandi e complessi in un tempo ragionevole.
Distribuzione di modelli di apprendimento profondo con CUDA
Mentre le GPU e CUDA sono stati utilizzati principalmente per l’addestramento di modelli di apprendimento profondo, sono anche cruciali per l’inferenza efficiente. Mentre i modelli di apprendimento profondo diventano sempre più complessi e richiedono risorse, l’accelerazione GPU è essenziale per raggiungere prestazioni in tempo reale in ambienti di produzione.
La tecnologia TensorRT di NVIDIA è un ottimizzatore e runtime di inferenza di apprendimento profondo ad alte prestazioni che fornisce basse latenze e alte prestazioni di inferenza sulle GPU abilitate CUDA. TensorRT può ottimizzare e accelerare modelli addestrati in framework come TensorFlow, PyTorch e MXNet, consentendo la distribuzione efficiente su varie piattaforme, dalle sistemi embedded ai data center.
import tensorrt as trt <p># Carica il modello pre-addestrato model = load_model(...)</p> <p># Crea il motore TensorRT logger = trt.Logger(trt.Logger.INFO) builder = trt.Builder(logger) network = builder.create_network() parser = trt.OnnxParser(network, logger)</p> <p># Analizza e ottimizza il modello success = parser.parse_from_file(model_path) engine = builder.build_cuda_engine(network)</p> <p># Esegui l'inferenza sulla GPU context = engine.create_execution_context() inputs, outputs, bindings, stream = allocate_buffers(engine)</p> <p># Imposta i dati di input e esegui l'inferenza set_input_data(inputs, input_data) context.execute_async_v2(bindings=bindings, stream_handle=stream.ptr)</p> # Elabora l'output # ...
In questo esempio, dimostriamo l’utilizzo di TensorRT per la distribuzione di un modello di apprendimento profondo pre-addestrato su una GPU abilitata CUDA. Il modello viene analizzato e ottimizzato da TensorRT, che genera un motore di inferenza altamente ottimizzato e personalizzato per il modello e l’hardware specifico. Questo motore può quindi essere utilizzato per eseguire l’inferenza efficiente sulla GPU, sfruttando CUDA per il calcolo accelerato.
Conclusione
La combinazione di GPU e CUDA è stata strumentale nel guidare i progressi nei grandi modelli linguistici, nel riconoscimento del linguaggio, nella visione artificiale e in altri domini dell’apprendimento profondo. Sfruttando le capacità di elaborazione parallela delle GPU e le librerie ottimizzate fornite da CUDA, i ricercatori e gli sviluppatori possono addestrare e distribuire modelli sempre più complessi con alta efficienza.
Man mano che il campo dell’AI continua a evolversi, l’importanza delle GPU e di CUDA crescerà. Con hardware e ottimizzazioni software ancora più potenti, possiamo aspettarci ulteriori progressi nello sviluppo e nella distribuzione di sistemi AI, spingendo i limiti di ciò che è possibile.












