Modelli e piattaforme di IA
Il framework di inferenza di Microsoft porta i modelli linguistici grandi a 1 bit sui dispositivi locali
Il 17 ottobre 2024, Microsoft ha annunciato BitNet.cpp (MSFT ), un framework di inferenza progettato per eseguire modelli linguistici grandi quantizzati a 1 bit (LLM). BitNet.cpp rappresenta un notevole progresso nell’ambito dell’intelligenza artificiale generale, consentendo il deploy efficiente di LLM a 1 bit su CPU standard, senza richiedere costose GPU. Questo sviluppo democratizza l’accesso agli LLM, rendendoli disponibili su una vasta gamma di dispositivi e offrendo nuove possibilità per le applicazioni di intelligenza artificiale sui dispositivi.
Comprendere i modelli linguistici grandi a 1 bit
I modelli linguistici grandi (LLM) hanno tradizionalmente richiesto risorse computazionali significative a causa dell’utilizzo di numeri in virgola mobile ad alta precisione (tipicamente FP16 o BF16) per i pesi del modello. Questa necessità ha reso il deploy degli LLM costoso e intensivo in termini di energia.
In sostanza, i modelli linguistici grandi a 1 bit utilizzano tecniche di quantizzazione estreme per rappresentare i pesi del modello utilizzando solo tre valori possibili: -1, 0 e 1, da cui il termine “1,58 bit” (poiché richiede leggermente più di un bit per codificare tre stati).
Sistema di pesi ternari
Il concetto
La quantizzazione a 1 bit in BitNet.cpp è un sistema di pesi ternari. BitNet opera con solo tre valori possibili per ogni parametro:
- -1 (negativo)
- 0 (neutro)
- 1 (positivo)
Ciò si traduce in una richiesta di archiviazione di circa 1,58 bit per parametro, da cui il nome BitNet b1.58. La riduzione drastica della larghezza del bit dei parametri comporta una riduzione impressionante dell’utilizzo della memoria e della complessità computazionale, poiché la maggior parte delle moltiplicazioni in virgola mobile vengono sostituite con semplici addizioni e sottrazioni.
Fondamento matematico
La quantizzazione a 1 bit comporta la trasformazione dei pesi e delle attivazioni nella loro rappresentazione ternaria attraverso i seguenti passaggi:
1. Binarizzazione dei pesi
La binarizzazione dei pesi comporta la centralizzazione intorno alla media (α), con conseguente rappresentazione ternaria. La trasformazione è espressa matematicamente come:
Wf=Sign(W−α)
Dove:
- W è la matrice dei pesi originale.
- α è la media dei pesi.
- Sign(x) restituisce +1 se x > 0 e -1 altrimenti.
2. Quantizzazione dell’attivazione
La quantizzazione dell’attivazione assicura che gli input siano limitati a una larghezza di bit specificata:
x^e=Quant(x)=Clip(γx×Qb,−Qb+ϵ,Qb−ϵ)
Dove:
- Qb = 2(b−1)2^{(b-1)} è il livello di quantizzazione massimo per la larghezza di bit b.
- γ è il valore assoluto massimo di x (denotato come ∣∣x∣∣∞).
- ε è un numero piccolo per prevenire il overflow durante i calcoli.
3. Operazione BitLinear
Il livello BitLinear sostituisce le moltiplicazioni matriciali tradizionali con un’operazione semplificata:
y=Wf×x^e×(Qbβγ)
Dove:
- β è un fattore di scala utilizzato per minimizzare gli errori di approssimazione.
- γ scala le attivazioni.
- Q_b è il fattore di quantizzazione.
Questa trasformazione consente calcoli efficienti mantenendo le prestazioni del modello.
Implicazioni sulle prestazioni
Efficienza della memoria
Il sistema di pesi ternari riduce notevolmente le esigenze di memoria:
- Modelli LLM tradizionali: 16 bit per peso
- BitNet.cpp: 1,58 bit per peso
Questa riduzione si traduce in un risparmio di memoria del 90% circa rispetto ai modelli a 16 bit tradizionali, consentendo di inserire modelli più grandi all’interno degli stessi vincoli hardware.
1. Velocità di inferenza: più veloce su entrambe le CPU
La velocità di inferenza è rappresentata come il numero di token elaborati per secondo. Ecco una panoramica delle osservazioni:
- Su Apple M2 Ultra: BitNet.cpp raggiunge fino a 5,07x di velocizzazione per modelli più grandi (30B) rispetto a Llama.cpp, con un picco di velocità di 593,43 token per secondo per un modello da 125M, che è un 1,37x di velocizzazione. Per modelli più grandi come il 3,8B e il 7B, BitNet.cpp mantiene una velocità superiore a 84,77 token per secondo, mostrando la sua efficienza su diverse scale.
- Su Intel i7-13700H: BitNet.cpp raggiunge miglioramenti di velocità ancora più drammatici. Alla dimensione del modello 7B, BitNet.cpp consegna un incredibile 5,68x di velocizzazione rispetto a Llama.cpp. Per modelli più piccoli come il 125M, elabora 389,08 token per secondo, che è 2,37x più veloce di Llama.cpp.
2. Efficienza energetica: un gioco da cambiare per i dispositivi edge
I grafici forniti mostrano anche confronti di costo energetico, che mostrano una riduzione significativa del consumo di energia per token elaborato:
- Su Apple M2 Ultra: i risparmi di energia di BitNet.cpp sono sostanziali. Per il modello da 700M, consuma 55,4% meno energia per token rispetto a Llama.cpp, scendendo da 0,314 a 0,140. Questa tendenza continua per modelli più grandi, con il modello da 70B che mostra una riduzione del 70,0% del consumo di energia.
- Su Intel i7-13700H: BitNet.cpp consegna 71,9% di risparmi di energia per il modello da 700M, con un consumo che scende da 1,367 a 0,384. Sebbene i dati di energia per il modello da 70B in Llama.cpp non siano disponibili, BitNet.cpp rimane efficiente, con un consumo di energia a 17,33 per il modello da 70B.
3. Superare il benchmark di velocità di lettura umana
Una delle informazioni più interessanti da questi grafici è il riferimento alla velocità di lettura umana, segnata a 5-7 token per secondo. Questa linea rossa mostra che entrambe le implementazioni, in particolare BitNet.cpp, possono superare agevolmente le velocità di lettura umane anche per i modelli più grandi:
- Su Apple M2 Ultra, BitNet.cpp supera la velocità di lettura umana per tutte le dimensioni dei modelli, con la velocità più bassa di 8,67 token per secondo per un modello da 70B.
- Su Intel i7-13700H, il modello da 100B raggiunge 1,70 token per secondo, toccando quasi il limite inferiore della velocità di lettura umana, mentre tutti i modelli più piccoli superano questo benchmark.
Considerazioni sull’addestramento
Stimatore diretto (STE)
Poiché la quantizzazione a 1 bit introduce funzioni non differenziabili, l’addestramento coinvolge una tecnica specializzata nota come Stimatore diretto (STE). In questo approccio, i gradienti fluiscono inalterati attraverso i punti non differenziabili. Ecco un’implementazione semplificata in Python:
class StraightThroughEstimator(Function): @staticmethod def forward(ctx, input): return input.sign() <p>@staticmethod def backward(ctx, grad_output): return grad_output
Addestramento a precisione mista
Per mantenere la stabilità durante l’addestramento, viene utilizzata la precisione mista:
- Pesi e attivazioni: quantizzati a precisione a 1 bit.
- Gradiente e stati dell’ottimizzatore: archiviati in precisione più alta.
- Pesi latenti: mantenuti in alta precisione per facilitare aggiornamenti precisi durante l’addestramento.
Strategia di apprendimento con tasso di apprendimento elevato
Una sfida unica con i modelli a 1 bit è che piccoli aggiornamenti potrebbero non influenzare i pesi binarizzati. Per mitigare questo, il tasso di apprendimento viene aumentato, garantendo una convergenza più rapida e una migliore ottimizzazione rispetto agli approcci tradizionali.
Quantizzazione e normalizzazione di gruppo
BitNet.cpp introduce la quantizzazione e normalizzazione di gruppo per migliorare il parallelismo del modello. Invece di calcolare i parametri per l’intera matrice dei pesi, BitNet divide i pesi e le attivazioni in più gruppi (G).
Questo raggruppamento consente un’elaborazione parallela efficiente senza ulteriore comunicazione tra gruppi, abilitando l’addestramento e l’inferenza di modelli su larga scala.
Note sull’implementazione e ottimizzazioni
Ottimizzazione CPU
BitNet.cpp sfrutta diverse ottimizzazioni a basso livello per raggiungere le prestazioni CPU di picco:
- Operazioni vettorizzate: utilizza istruzioni SIMD per eseguire manipolazioni di bit in modo efficiente.
- Accesso alla memoria friendly con la cache: struttura i dati per minimizzare gli errori di cache.
- Elaborazione parallela: distribuisce il carico di lavoro su più core CPU in modo efficace.
Ecco un esempio di una funzione chiave che implementa la quantizzazione e l’inferenza in BitNet:
Modelli supportati
La versione attuale di BitNet.cpp supporta i seguenti modelli LLM a 1 bit disponibili su Hugging Face:
- bitnet_b1_58-large (0,7 miliardi di parametri)
- bitnet_b1_58-3B (3,3 miliardi di parametri)
- Llama3-8B-1.58-100B-tokens (8,0 miliardi di parametri)
Questi modelli sono disponibili pubblicamente per dimostrare le capacità di inferenza del framework. Sebbene non siano stati addestrati o rilasciati ufficialmente da Microsoft, illustrano la versatilità del framework.
Guida all’installazione
Per iniziare a utilizzare BitNet.cpp, segui i passaggi seguenti:
Prerequisiti
- Python >= 3.9
- CMake >= 3.22
- Clang >= 18
- Conda (altamente consigliato)
Per gli utenti Windows, è necessario installare Visual Studio con i seguenti componenti abilitati:
- Sviluppo desktop con C++
- Strumenti C++-CMake per Windows
- Git per Windows
- Compilatore C++-Clang per Windows
- Supporto MS-Build per il set di strumenti LLVM (Clang)
Per gli utenti Debian/Ubuntu, è disponibile uno script di installazione automatico:
Installazione passo dopo passo
- Clona il repository:
- Installa le dipendenze:
- Compila e prepara il progetto: puoi scaricare direttamente un modello da Hugging Face e convertirlo in formato quantizzato:
In alternativa, puoi scaricare e convertire manualmente il modello:
Eseguire l’inferenza con BitNet.cpp
Per eseguire l’inferenza utilizzando il framework, utilizza il seguente comando:
Spiegazione:
-mspecifica il percorso del file del modello.-pdefinisce il testo del prompt.-nimposta il numero di token da prevedere.-tempregola la casualità del campionamento (temperatura) durante l’inferenza.
Esempio di output
Dettagli tecnici di BitNet.cpp
Layer BitLinear
BitNet.cpp implementa un’architettura Transformer modificata, sostituendo le moltiplicazioni matriciali standard con operazioni BitLinear. Questo approccio centralizza i pesi a zero prima della quantizzazione e li scala per ridurre gli errori di approssimazione. La funzione di trasformazione chiave è la seguente:
<p># Funzione di binarizzazione per pesi a 1 bit def binarize_weights(W): alpha = W.mean() W_binarized = np.sign(W - alpha) return W_binarized</p>
La combinazione di pesi centralizzati e scala assicura che l’errore di quantizzazione rimanga minimo, preservando così le prestazioni.
Impatto sull’industria
BitNet.cpp potrebbe avere implicazioni di vasta portata per il deploy dei modelli linguistici grandi:
- Accessibilità: consente ai modelli LLM di funzionare su dispositivi standard, democratizzando l’accesso all’intelligenza artificiale potente.
- Efficienza dei costi: riduce la necessità di costose GPU, abbassando la barriera per l’adozione.
- Efficienza energetica: salva energia sfruttando l’inferenza basata su CPU standard.
- Innovazione: apre nuove possibilità per le applicazioni di intelligenza artificiale sui dispositivi, come la traduzione linguistica in tempo reale, gli assistenti vocali e le applicazioni focalizzate sulla privacy senza dipendenza dal cloud.
Sfide e direzioni future
Sebbene i modelli LLM a 1 bit mostrino promesse, diverse sfide rimangono. Tra queste vi sono lo sviluppo di modelli robusti a 1 bit per compiti diversi, l’ottimizzazione dell’hardware per il calcolo a 1 bit e l’incoraggiamento degli sviluppatori ad adottare questo nuovo paradigma. Inoltre, esplorare la quantizzazione a 1 bit per compiti di visione artificiale o audio rappresenta una direzione futura emozionante.
Conclusione
Il lancio di BitNet.cpp da parte di Microsoft rappresenta un notevole progresso. Abilitando l’inferenza efficiente a 1 bit su CPU standard, BitNet.cpp crea l’accessibilità e la sostenibilità dell’intelligenza artificiale. Questo framework prepara il terreno per modelli LLM più portatili e a basso costo, spingendo i limiti di ciò che è possibile con l’intelligenza artificiale sui dispositivi.














