QLoRA
QLoRA esegue il fine-tuning degli LLM addestrando adapter LoRA su un modello base congelato memorizzato in formato NF4 a 4 bit, consentendo di usare un modello da 65 miliardi di parametri su una singola GPU da 48 GB.
QLoRA (adattamento quantizzato a basso rango) è un metodo di fine-tuning che congela un modello linguistico di grandi dimensioni (LLM) preaddestrato a precisione a 4 bit e vi addestra piccoli adattatori LoRA, propagando all'indietro il gradiente attraverso i pesi quantizzati fino agli adattatori. Presentato da Dettmers et al. nel documento su QLoRA del 2023, ha ridotto la memoria GPU necessaria per il fine-tuning di un modello da 65 miliardi di parametri da oltre 780 GB a meno di 48 GB, abbastanza per una singola GPU workstation, mantenendo le stesse prestazioni sul compito del fine-tuning a 16 bit. I modelli Guanaco del documento, sottoposti a fine-tuning in questo modo, hanno raggiunto il 99,3% del punteggio di ChatGPT nel benchmark Vicuna dopo 24 ore di addestramento su una singola GPU.
Come funziona QLoRA#
QLoRA combina un modello di base congelato e compresso con tre tecniche per risparmiare memoria:
- NormalFloat a 4 bit (NF4): ogni peso viene memorizzato come codice a 4 bit che punta a uno di 16 livelli posizionati sui quantili di una distribuzione normale; ogni blocco di 64 pesi usa una scala di massimo assoluto. I pesi del modello preaddestrato hanno una distribuzione approssimativamente normale, quindi i livelli basati sui quantili offrono maggiore risoluzione vicino allo zero, dove si trovano la maggior parte dei pesi.
- Doppia quantizzazione: le scale per blocco vengono a loro volta quantizzate a 8 bit in blocchi di 256, riducendo l'overhead da 0,5 a circa 0,127 bit per parametro e consentendo di risparmiare circa 3 GB su un modello 65B.
- Ottimizzatori con paging: gli stati dell'ottimizzatore risiedono nella memoria unificata NVIDIA e vengono trasferiti nella RAM della CPU durante i picchi di memoria, per esempio con sequenze lunghe durante il checkpointing del gradiente, così l'addestramento non si interrompe per errori di memoria insufficiente.
Nel passaggio forward, ogni layer congelato dequantizza i propri pesi NF4 in bfloat16 appena prima della moltiplicazione tra matrici e aggiunge l'output del percorso LoRA. La retropropagazione calcola i gradienti attraverso i pesi dequantizzati, ma aggiorna solo le due matrici LoRA a basso rango. Il documento ha rilevato che, per eguagliare il fine-tuning completo, era necessario applicare gli adattatori a ogni layer lineare del blocco Transformer:
Lo snippet seguente crea i livelli NF4 come fa la libreria bitsandbytes e li confronta con 16 livelli equidistanti applicati a blocchi di pesi con distribuzione normale:
import torch
torch.manual_seed(0)
normal = torch.distributions.Normal(0.0, 1.0)
# NF4: 16 levels at quantiles of a normal distribution, with an exact zero (as built in bitsandbytes)
positive = normal.icdf(torch.linspace(0.9677083, 0.5, 9)[:-1])
negative = -normal.icdf(torch.linspace(0.9677083, 0.5, 8)[:-1])
nf4 = torch.cat([negative, torch.zeros(1), positive]).sort().values
nf4 = nf4 / nf4.max()
int4 = torch.linspace(-1, 1, 16) # evenly spaced 4-bit levels for comparison
weights = torch.randn(4096, 64) * 0.02 # pretrained weights are roughly normal, in blocks of 64
absmax = weights.abs().amax(dim=1, keepdim=True) # one scale per block
for name, levels in (("NF4", nf4), ("INT4", int4)):
codes = ((weights / absmax)[..., None] - levels).abs().argmin(dim=-1) # 4-bit index per weight
restored = levels[codes] * absmax
print(f"{name} mean squared error: {(restored - weights).pow(2).mean():.3e}")Stampa un errore quadratico medio di 3.395e-06 per NF4 e di 4.053e-06 per i livelli equidistanti: a parità di 16 livelli e di scale per blocco, la spaziatura per quantili di NF4 riduce di circa il 16% l'errore di ricostruzione sui pesi con distribuzione normale. In pratica, Hugging Face Transformers carica un modello in NF4 tramite bitsandbytes usando un BitsAndBytesConfig che imposta load_in_4bit=True, bnb_4bit_quant_type="nf4", bnb_4bit_use_double_quant=True e bnb_4bit_compute_dtype=torch.bfloat16, mentre la libreria PEFT applica gli adattatori LoRA.
QLoRA e metodi correlati#
- LoRA: addestra lo stesso tipo di adattatori su un modello di base mantenuto a precisione a 16 bit. I soli pesi a 16 bit di un modello 65B occupano circa 130 GB, mentre NF4 li memorizza in circa un quarto dello spazio.
- Fine-tuning efficiente in termini di parametri (PEFT): la famiglia di metodi che addestrano un piccolo insieme di parametri, tra cui LoRA, QLoRA, gli adattatori e il prompt tuning.
- Quantizzazione del modello dopo l'addestramento: comprime un modello finito per l'inferenza. Spesso, un modello adattato con QLoRA viene unito ai pesi a 16 bit e poi quantizzato di nuovo per la distribuzione.
- Addestramento con consapevolezza della quantizzazione (QAT): simula l'arrotondamento a bassa precisione nel passaggio forward mentre tutti i pesi vengono addestrati, così il modello distribuito mantiene la propria accuratezza a bassa precisione. Ultralytics YOLO26 esegue QAT INT8 quando viene addestrato con
quantize=8, inserendo quantizzazione fittizia nei layer convoluzionali e lineari tramite NVIDIA ModelOpt, affinché i pesi appresi mantengano le proprie prestazioni dopo l'esportazione INT8.
Compromessi#
QLoRA sacrifica la velocità per risparmiare memoria: dequantizzare ogni peso congelato a ogni passaggio forward e backward rende una fase di addestramento più lenta rispetto a LoRA a 16 bit. Gli adattatori sono abbastanza piccoli da poter essere archiviati e sostituiti per ogni attività, ma per unirli al modello di base occorre prima dequantizzarlo a 16 bit. Per il modello 65B, i pesi a 4 bit e le scale a doppia quantizzazione occupano circa 34 GB, lasciando il resto di una GPU da 48 GB disponibile per adattatori, attivazioni e stati dell'ottimizzatore con paging.










