Unisciti alla beta di Hanzo e ottieni il 50% di sconto su qualsiasi piano a pagamento

BitDelta: servire 14 modelli Zen da un solo cluster GPU

Come la compressione delta a 1 bit di BitDelta permette di servire 14 varianti di modelli Zen da un'infrastruttura GPU condivisa: la matematica, l'architettura e i compromessi.

In questa pagina

L'economia di servire varianti di modello sottoposte a fine-tuning è ostile per impostazione.

Un modello da 70B parametri in BF16 richiede 140GB di memoria GPU. Se avete 14 varianti con fine-tuning di quel modello — ciascuna con un profilo di capacità diverso, una specializzazione di dominio o una messa a punto del comportamento — l'architettura di serving ingenua richiede 14 carichi di modello separati: 1.96TB di memoria GPU, 14 processi di inferenza separati, 14x il costo dell'infrastruttura. Per ogni variante oltre la prima, pagate il prezzo pieno per ciò che è quasi interamente conoscenza di base duplicata.

BitDelta (arXiv:2402.10193) rompe questa economia. L'idea è elegante e la matematica è semplice.

Il fondamento matematico

Sia W_base la matrice dei pesi del modello di base e W_ft la matrice dei pesi corrispondente in una variante con fine-tuning. Il delta comportamentale è:

Δ = W_ft - W_base

BitDelta comprime questo delta a una precisione di 1 bit:

W_ft ≈ W_base + α · sign(Δ)

Dove α è un fattore di scala per matrice, calcolato come il valore assoluto medio di Δ:

α = (1/n) · ||Δ||_1 = mean(|Δ_ij|)

L'operazione di segno riduce ogni delta scalare a un solo bit, positivo o negativo. Il fattore di scala α restituisce la magnitudine. Insieme, l'approssimazione cattura l'informazione direzionale dell'aggiornamento di fine-tuning a 1/32 del costo di archiviazione di un delta a precisione piena (1 bit contro 32 bit per elemento di parametro).

Perché funziona? Il fine-tuning di un grande modello preaddestrato produce delta che, nel valore atteso, sono piccoli rispetto ai pesi di base. Il modello di base cattura la struttura dominante della lingua; il fine-tuning cattura modifiche comportamentali sopra quella struttura. Quando il delta è piccolo, il segno di ogni elemento è la rappresentazione a massima informazione di quell'elemento: la magnitudine è circa uniforme sulla matrice delta, quindi α · sign(Δ) è un codice efficiente.

Si può dirlo in modo più formale. Se modelliamo Δ_ij come una variabile aleatoria di Laplace con posizione 0 e scala b:

Δ_ij ~ Laplace(0, b)
|E[sign(Δ) | |Δ|]| = 1 for all nonzero Δ

Il segno preserva perfettamente l'informazione direzionale. La perdita di informazione è solo nella variazione di magnitudine, che l'unico fattore di scala α recupera in parte. Per i delta di fine-tuning in cui la variazione di magnitudine è bassa (modifica del comportamento, non trasferimento di conoscenza), l'approssimazione è stretta.

Riduzione di memoria: per un modello da 70B in BF16, i pesi di base occupano 140GB. Il delta di ogni variante con fine-tuning, in BF16 pieno, aggiungerebbe altri 140GB. Nel formato BitDelta, ogni delta occupa circa 8.75GB (70B bit = 8.75GB, più gli scalari α, trascurabili). Il cluster di 14 varianti passa da 14 × 140GB = 1.96TB a 140GB + 14 × 8.75GB ≈ 262GB, una riduzione dell'87%.

Architettura di serving

┌─────────────────────────────────────────────────────────────────┐
│                    GPU CLUSTER (8x H100, 80GB each)             │
│                                                                  │
│  ┌──────────────────────────────────────────────────────────┐   │
│  │              BASE WEIGHTS (140GB, HBM resident)          │   │
│  │  W_base for zen4-70b: attention, FFN, embeddings, norms  │   │
│  └──────────────────────────────┬───────────────────────────┘   │
│                                 │ shared read                    │
│  ┌──────────────────────────────▼───────────────────────────┐   │
│  │           DELTA CACHE (≈8.75GB per variant)              │   │
│  │  zen4-coder:    α_coder,    sign(Δ_coder)    [8.75GB]    │   │
│  │  zen4-ultra:    α_ultra,    sign(Δ_ultra)    [8.75GB]    │   │
│  │  zen4-math:     α_math,     sign(Δ_math)     [8.75GB]    │   │
│  │  zen4-writer:   α_writer,   sign(Δ_writer)   [8.75GB]    │   │
│  │  zen4-legal:    α_legal,    sign(Δ_legal)    [8.75GB]    │   │
│  │  zen4-medical:  α_medical,  sign(Δ_medical)  [8.75GB]    │   │
│  │  ...14 total...                                           │   │
│  └──────────────────────────────┬───────────────────────────┘   │
│                                 │                                │
│  ┌──────────────────────────────▼───────────────────────────┐   │
│  │              REQUEST ROUTER                               │   │
│  │  model_id → delta_load → reconstruct W_ft → inference    │   │
│  └──────────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────────┘

Al momento dell'inferenza, la pipeline di serving è:

  1. La richiesta arriva con model: "zen4-coder"
  2. Il router individua la voce della cache dei delta per zen4-coder
  3. Il peso effettivo si ricostruisce al volo: W_eff = W_base + α_coder · sign(Δ_coder)
  4. Si esegue il passaggio in avanti con i pesi ricostruiti
  5. Si restituisce la risposta

Il passo 3 è il cammino critico. La ricostruzione è un kernel CUDA fuso che legge insieme i pesi di base e i bit del delta, e ricostruisce il peso effettivo nei registri o nella memoria condivisa senza materializzare l'intero W_eff in HBM. Ricostruzione e moltiplicazione di matrici si possono fondere in un solo passaggio del kernel: il peso ricostruito non esiste mai per intero come tensore indipendente.

# Simplified reconstruction kernel (conceptual)
def fused_bitdelta_linear(
    x: torch.Tensor,          # input activations [batch, seq, d_in]
    w_base: torch.Tensor,     # base weight [d_out, d_in] in HBM
    delta_sign: torch.Tensor, # 1-bit delta [d_out, d_in] packed as int8
    alpha: torch.Tensor,      # scale factor [d_out] or scalar
) -> torch.Tensor:
    # In practice: single CUDA kernel, no intermediate w_eff allocation
    # Conceptually equivalent to:
    delta_unpacked = unpack_bits(delta_sign).to(w_base.dtype)  # {-1, +1}
    w_eff = w_base + alpha.unsqueeze(1) * delta_unpacked
    return torch.nn.functional.linear(x, w_eff)

L'approccio del kernel fuso tiene il consumo di picco di banda HBM a circa 1.5x i pesi del modello di base, e non a 2x, perché il delta si legge in formato di bit impacchettato e si spacchetta nella SRAM veloce durante l'operazione fusa.

Profilo di latenza

In pratica, nel cluster di serving Zen a 14 varianti:

VarianteP50 TTFTP99 TTFTrispetto alla base
zen4 (base)18ms42ms1.0x
zen4-coder21ms48ms1.17x
zen4-ultra22ms51ms1.22x
zen4-math21ms47ms1.17x
zen4-writer20ms45ms1.11x

Il sovraccarico del tempo al primo token dovuto alla ricostruzione BitDelta è di 2-4ms, trascurabile rispetto al costo di prefill per lunghezze di prompt tipiche. Il kernel di ricostruzione è vincolato dal calcolo, non dalla memoria, quindi scala bene con la generazione sulla GPU.

Il throughput di generazione (token/secondo) è identico tra il modello di base e qualsiasi variante BitDelta, perché la ricostruzione avviene solo nella fase di prefill. Una volta popolata la cache KV, il passo di generazione autoregressiva usa i percorsi matmul standard.

Qualità: cosa si perde nella compressione a 1 bit

La risposta onesta: una quantità piccola, delimitata con cura.

Il degrado di qualità di BitDelta dipende dal compito. Sui compiti ben rappresentati nei dati di fine-tuning, il delta comportamentale è forte: il segno cattura in modo affidabile la direzione dominante del gradiente. Sui compiti alla periferia della distribuzione di fine-tuning, il delta è più debole: l'approssimazione introduce più rumore rispetto al segnale.

Empiricamente, sui modelli Zen valutati sulle loro suite di compiti obiettivo:

ModelloPrecisione pienaBitDeltaDelta
zen4-coder (HumanEval)72.4%71.1%-1.3%
zen4-math (MATH-500)68.9%67.8%-1.1%
zen4-writer (WritingBench)84.2%83.7%-0.5%
zen4-legal (LegalBench)61.3%60.1%-1.2%

Il degrado di 1-1.3% sulle suite di compiti primarie è il prezzo di una riduzione di memoria dell'87%. Per tutte le varianti Zen, questo compromesso è accettabile: la differenza di qualità resta sotto la soglia di un cambiamento di comportamento percepibile dall'utente nei casi d'uso tipici.

MonoSoup: complemento SVD di un singolo checkpoint

MonoSoup (arXiv:2602.09689) affronta un problema complementare: quando esistono più checkpoint con fine-tuning dello stesso modello di base, come si fondono in un solo checkpoint senza caricarli tutti insieme?

MonoSoup applica la SVD al vettore di compito (Δ = W_ft - W_base) per individuare le componenti di rango basso che catturano la maggior informazione comportamentale, poi le aggrega fra i checkpoint:

For checkpoints {W_1, W_2, ..., W_k}:
  Δ_i = W_i - W_base
  [U_i, S_i, Vh_i] = truncated_SVD(Δ_i, rank=r)
  τ_i = U_i · diag(S_i) · Vh_i  # rank-r approximation

MonoSoup merge:
  W_merged = W_base + (1/k) · Σ τ_i

Il troncamento SVD al rango r agisce come un passo di denoising: il fine-tuning tende a modificare un modello lungo un piccolo numero di direzioni ad alta varianza nello spazio dei pesi. Catturare quelle direzioni con SVD troncata e fonderle cattura l'essenza comportamentale di ogni checkpoint, senza il rumore delle direzioni a valori singolari più bassi.

Usiamo MonoSoup durante le esecuzioni di addestramento di Zen come passo di media dei checkpoint: ogni 1000 passi di gradiente, fondiamo il checkpoint corrente con il migliore precedente tramite MonoSoup, invece della media ingenua. Questo riduce il rumore di addestramento e produce curve di perdita più lisce, con una qualità finale migliore.

K-Merge: gestione online di adattatori LoRA per l'edge

K-Merge (arXiv:2510.13537) risolve un'altra variante del problema di serving: all'edge, dove lo storage è vincolato a un budget fisso di B gigabyte, come si servono K adattatori LoRA in modo efficiente?

K-Merge mantiene un pool di adattatori fusi sotto il vincolo che lo storage totale non superi mai B. Quando si richiede un adattatore nuovo che non è nel pool:

  1. Si calcola la similarità a coppie tra il nuovo adattatore e tutti i membri esistenti del pool (similarità del coseno sui vettori di compito)
  2. Si individua l'adattatore esistente più simile
  3. Si fonde il nuovo adattatore in quello esistente più simile tramite OPCM (fusione per proiezione ortogonale)
  4. Se la qualità dell'adattatore fuso su entrambi i compiti supera la soglia, si espelle l'originale e si memorizza la versione fusa

La politica garantisce che il pool contenga al massimo K adattatori che consumano il budget B, massimizzando la copertura di tutti gli adattatori richiesti tramite una fusione ragionata.

Per il deployment di Zen all'edge (Hanzo Desktop, mobile), K-Merge abilita famiglie di modelli servite in locale che altrimenti supererebbero i limiti di storage del dispositivo. Un dispositivo con 16GB di storage di modello disponibile può servire 8 profili comportamentali distinti mantenendo un pool K-Merge che fonde su richiesta gli adattatori simili.

Dettagli di infrastruttura

Formato di storage del delta: matrici di segno a 1 bit memorizzate come tensori int8 impacchettati (8 bit per byte, così uno strato da 70B parametri con un peso 4096×4096 si impacchetta in 4096×512 int8). I fattori di scala α sono memorizzati come float32, uno per dimensione di uscita, e ammontano a circa lo 0.1% dello storage del delta.

Comportamento di avvio a freddo: quando il delta di una variante non è in cache nella SRAM della GPU (per esempio, la variante non è stata richiesta da 10 minuti), la prima richiesta paga una penalità di latenza di carico del delta di circa 200ms per un modello da 70B, il tempo per trasferire 8.75GB da NVMe a HBM alle velocità di lettura sequenziale di NVMe. Dopo la prima richiesta, il delta resta nella cache dei delta della SRAM della GPU finché la politica LRU non lo espelle.

Politica di espulsione della cache: LRU con peso sul tasso di richieste. Le varianti con tasso di richieste alto restano fissate; quelle a basso traffico possono essere espulse. In pratica, le prime 6 varianti per volume di traffico sono sempre residenti in cache; le 8 restanti condividono lo spazio di cache rimanente con espulsione LRU.

Instradamento delle richieste: il router di serving segue le richieste in volo per variante e impedisce il blocco di testa di linea: se il delta di zen4-coder è in caricamento, le richieste zen4-coder in arrivo si mettono in coda, invece di far partire ciascuna un carico indipendente.

L'esito: 14 varianti di modelli Zen servite da nodi 8x H100 che in precedenza avrebbero richiesto nodi 112x H100 per una capacità equivalente. La riduzione del costo dell'infrastruttura non è incrementale. BitDelta rende economicamente sostenibile la famiglia di modelli multi-tenant.


I modelli Zen sono disponibili su zenlm.org. Accesso all'API su hanzo.ai.

Commenti

Accedi con il tuo Hanzo ID per commentare.

Caricamento dei commenti…

Leggi di più