Token, parametri, reti: i conti veri
Le stesse cose del poster introduttivo, con le formule, le dimensioni delle matrici e i byte. Ogni stadio ha un conto svolto su un modello reale: quanti parametri, dove stanno, quanta memoria occupano, quante operazioni servono per produrre un token.
Tutti i numeri di questa pagina sono calcolati su una configurazione reale, tipica dei modelli aperti da 8 miliardi di parametri.
teste = 32 · teste KV = 8 · dhead = 128
dff = 14336 · vocabolario = 128.256
Tutto è un array multidimensionale
Un tensore è un array con una forma e un tipo. Tutta la rete è una sequenza di trasformazioni di forma, e sapere leggere una forma è metà del mestiere: quando qualcosa non funziona, quasi sempre non tornano le forme.
È un float[][][] contiguo in memoria con la forma dichiarata a parte. La differenza pratica: le operazioni sono definite sull'intero array, non sui singoli elementi, e girano su hardware che ne esegue migliaia in parallelo.
BPE: come si costruisce un vocabolario
Il vocabolario non è scelto da un linguista: è il risultato di un algoritmo di compressione avidamente semplice, eseguito una volta sul corpus prima di addestrare qualsiasi cosa.
Prima, un'espressione regolare divide il testo in parole, spazi, punteggiatura e numeri: le fusioni non attraversano mai questi confini. Poi parti dai 256 byte possibili. Conta tutte le coppie adiacenti nel corpus. Fondi la coppia più frequente in un simbolo nuovo. Ripeti finché il vocabolario non raggiunge la dimensione voluta. Fine: non c'è altro.
Embedding: una riga pescata da una tabella
La matrice degli embedding ha forma [V, d] — una riga per token del vocabolario. «Calcolare l'embedding» è un accesso a indice, non una moltiplicazione: costo nullo, ma i numeri dentro la riga sono parametri appresi come tutti gli altri.
La similarità si misura con il coseno dell'angolo fra due vettori: cos(a,b) = a·b / (‖a‖‖b‖), un numero fra −1 e 1. Non si usa la distanza euclidea perché conta la direzione, non la lunghezza — e la lunghezza in questi spazi codifica più che altro la frequenza del token.
Questi sono gli embedding di ingresso, prima di qualsiasi strato: codificano il token isolato, non il suo significato nel contesto.
Gli embedding usati per la ricerca semantica sono un'altra cosa: vengono dall'uscita di un modello, dopo tutti gli strati, e rappresentano una frase intera.
La posizione va aggiunta a mano
L'attenzione è una somma pesata: senza maschera, cambiando l'ordine dei token il risultato non cambia. Senza un'informazione di posizione, «il cane morde l'uomo» e «l'uomo morde il cane» sarebbero identici per la rete.
Le 128 dimensioni di ogni testa si trattano a coppie, come punti su un piano. Ogni coppia viene ruotata di un angolo proporzionale alla posizione del token: θi·m per il token in posizione m. Frequenze diverse per coppie diverse — le prime ruotano velocissime, le ultime quasi ferme.
L'attenzione riceve la distanza relativa fra due token, non le loro posizioni assolute. Un'informazione che generalizza: la relazione «tre token più indietro» è la stessa all'inizio e alla fine del documento.
Lo strato lineare: y = Wx
Più del 90% dei parametri (qui il 93%; il resto è la tabella degli embedding, che si legge per indice) sta dentro matrici usate così. Ogni riga di W è un rilevatore: il suo prodotto scalare con x misura quanto x somiglia a quella riga. Una moltiplicazione matrice-vettore è un banco di migliaia di rilevatori applicati insieme.
W di forma [m, n] applicata a x di forma [n] produce y di forma [m]. Costa m·n moltiplicazioni e altrettante somme: 2·m·n FLOP. E contiene esattamente m·n parametri. Da qui la regola pratica di tutto il campo: ogni parametro costa 2 FLOP per ogni token che lo attraversa.
Perché serve una non linearità
Due strati lineari in fila sono ancora uno strato lineare: W₂(W₁x) = (W₂W₁)x. Senza qualcosa di non lineare in mezzo, cento strati collassano matematicamente in una sola matrice — e la profondità non comprerebbe nulla.
Una funzione applicata elemento per elemento — nessun parametro, costo trascurabile — che spezza la linearità e permette al modello di rappresentare condizioni: «questa caratteristica conta solo se quell'altra è presente». Le interazioni fra concetti nascono qui.
Azzera il negativo. Semplicissima, per anni lo standard. Difetto: un neurone che finisce sempre in negativo smette di ricevere gradiente e resta morto.
Due varianti morbide quasi uguali (Φ è la ripartizione della normale): attorno allo zero non taglia di netto ma sfuma. Derivata continua, gradiente più docile, risultati migliori.
Due proiezioni: una fa da segnale, l'altra da saracinesca che lo lascia passare o no, moltiplicando elemento per elemento. Costa una matrice in più — ecco perché l'MLP ne ha tre e non due.
Con due matrici (GELU classica) si usava dff = 4d. Con SwiGLU le matrici sono tre, e per non gonfiare il conto si riduce dff a circa 8⁄3·d — qui 4096 · 8/3 ≈ 10.923, e l'MLP resterebbe due terzi dello strato. Qui si è scelto di più: 8/3·d × 1,3 ≈ 14.200, arrotondato a 14336 (multiplo di 1024). Risultato: l'MLP è l'81% dello strato e il 70% del modello.
Attenzione, riga per riga
Una formula sola, cinque operazioni. Vale la pena percorrerla con le forme accanto: è il punto in cui la maggior parte delle spiegazioni salta un passaggio.
Il prodotto scalare di due vettori casuali a d componenti di varianza 1 ha varianza d: con d = 128 i punteggi arriverebbero a ±11 e oltre. La softmax su valori così grandi diventa quasi uno 0/1 secco, e il gradiente si annulla. Dividere per √d riporta la varianza a 1.
Le implementazioni moderne calcolano l'attenzione a blocchi, tenendo in memoria veloce solo un riquadro per volta e aggiornando la softmax in modo incrementale. Stesso risultato esatto, memoria lineare invece che quadratica: è ciò che ha reso praticabili i contesti lunghi.
Teste multiple e il costo della cache
Le 4096 dimensioni si dividono in 32 teste da 128, ognuna con le proprie proiezioni. Girano in parallelo e cercano cose diverse. Il conto dei parametri non cambia: cambia cosa si può rappresentare.
In generazione, chiavi e valori dei token già visti si tengono in memoria per non ricalcolarli. Quella cache cresce con la lunghezza del contesto e con il numero di teste — e con contesti lunghi diventa più grande dei pesi del modello. GQA: 32 teste di query condividono 8 gruppi di chiavi e valori. Quattro volte meno cache.
100.000 token → ≈ 52 GB
100.000 token → ≈ 13 GB
Salvare un riassunto
Multi-head Latent Attention (DeepSeek-V3, Kimi K2) non salva K e V ma un vettore latente compresso, da cui si ricostruiscono. In DeepSeek-V3 sono 512 + 64 numeri per strato: 576 × 61 × 2 B ≈ 70 KB per token, meno del nostro 8B per un modello da 671 G. Sul nostro: 36 KiB, 3,7 GB a 100.000 token.
Guardare solo vicino
Gli strati locali vedono solo gli ultimi 1.000-4.000 token e hanno una cache di dimensione fissa. Gemma 3 ne alterna cinque, con finestra di 1.024, a ogni strato globale. Sul nostro modello: 5 strati globali e 27 locali, ≈ 2,2 GB a 100.000 token invece di 13.
Attenzione lineare
Tre strati su quattro usano attenzione lineare (Gated DeltaNet e varianti, in Qwen3-Next e Kimi Linear), con uno stato fisso invece di una cache che cresce. Solo 8 strati su 32 restano pieni: 32 KiB per token, 3,3 GB a 100.000 token.
La memoria della scheda è divisa fra pesi e cache di tutti gli utenti serviti insieme. Meno cache per utente significa più utenti sulla stessa scheda, e quindi un costo per richiesta più basso. Quasi tutte le scelte architetturali degli ultimi anni — GQA, MLA, attenzione a finestra o ibrida — nascono da questa aritmetica.
Il blocco, nell'ordine esatto
Trentadue blocchi identici nella forma, tutti con pesi diversi. L'ordine delle operazioni non è arbitrario: la variante pre-norm che vedi qui è ciò che ha reso addestrabili le reti profonde con molti meno accorgimenti.
Connessione residua
Ogni blocco somma il proprio contributo invece di sostituire. Il gradiente trova così una via diretta dall'uscita all'ingresso: senza, oltre una decina di strati non si addestra più niente.
x / RMS(x) · g
Riporta il vettore a una scala fissa dividendolo per la radice della media dei quadrati, poi lo riscala con un vettore appreso g di 4096 valori. Nessuna sottrazione della media: meno operazioni della LayerNorm classica, stessa efficacia.
Normalizzare prima
Nel transformer originale la norma stava dopo la somma. Spostarla prima rende l'addestramento molto più stabile e il riscaldamento del learning rate molto meno critico. Resta comunque in uso: poche migliaia di passi con learning rate crescente.
Poiché tutto si somma, il vettore che sale è una lavagna su cui ogni blocco scrive. Gli strati non comunicano solo con il vicino: uno strato profondo può leggere qualcosa scritto trenta strati prima. È l'osservazione su cui si fonda gran parte della ricerca sull'interpretabilità.
Molti modelli recenti (OLMo 2, Qwen3, Gemma 3) aggiungono una RMSNorm anche su Q e K, per testa, prima del prodotto scalare: i punteggi dell'attenzione non possono più crescere senza limite, e l'addestramento regge learning rate più alti. Costa 2 × 128 parametri per strato, 8.192 su tutto il modello.
Encoder e decoder: la differenza è una maschera
Le tre famiglie di transformer usano gli stessi blocchi dello stadio precedente. Ciò che cambia è la M nella formula dell'attenzione: se è tutta zeri, ogni token vede l'intera sequenza; se è triangolare, vede solo il passato. Da questa singola scelta discende tutto il resto.
Il lettore e lo scrittore. Chi legge ha la pagina intera davanti e può andare avanti e indietro: capisce meglio, ma non produce niente di nuovo. Chi scrive vede solo quello che ha già messo sul foglio: sa meno, ma può continuare. La traduzione ha bisogno di entrambi.
Legge tutto insieme
Nessuna maschera: attenzione bidirezionale. Si addestra nascondendo token a caso in mezzo al testo e chiedendo di indovinarli.
Uscita: un vettore per token, o uno per l'intera frase. Serve per: embedding, ricerca semantica, reranker, classificazione. Non sa generare.
Legge, poi scrive
Due pile. L'encoder digerisce l'ingresso senza maschera; il decoder genera con maschera causale e in più una cross-attention: le query vengono da lui, chiavi e valori dall'encoder.
Serve per: traduzione, trascrizione, compiti con ingresso e uscita di natura diversa. È l'architettura del transformer originale.
Scrive e basta
Maschera causale ovunque, una pila sola. Ingresso e uscita sono la stessa sequenza: il prompt è semplicemente la parte già scritta.
Ha vinto perché un solo obiettivo scala, ogni compito si può riformulare come continuazione di testo, e la cache KV rende la generazione efficiente.
Con la maschera causale ogni posizione è contemporaneamente un esempio di addestramento: da 2048 token si ricavano 2048 previsioni in un solo passaggio. Un encoder mascherato ne sfrutta circa il 15%, quelle effettivamente nascoste. È un fattore sei o sette di efficienza sui dati — una delle ragioni concrete per cui i decoder hanno scalato meglio.
Per trasformare un documento in un vettore, un encoder da 100-300 M resta la scelta economica: un passaggio in avanti, contesto bidirezionale, costo cento volte inferiore. I modelli di embedding più accurati oggi sono però spesso decoder da qualche miliardo di parametri, riaddestrati a produrre un vettore: la distinzione conta meno della taglia.
Dove stanno gli 8 miliardi
Il conto completo, in ordine di peso. Vale la pena guardarlo una volta: rende concreto cosa si compra quando si «scala un modello», e cosa cambia fra una configurazione e l'altra.
Trascurando GQA e le estremità, un transformer ha circa 12·d² per strato — 4d² di attenzione e 8d² di MLP. Con d = 4096 e 32 strati: 12 · 4096² · 32 ≈ 6,4 G. Un'approssimazione a mente che sbaglia di poco.
A parità di parametri si può scegliere più strati o più canali. Più largo parallelizza meglio ed è più veloce; più profondo tende a ragionare in più passaggi. I rapporti usati in pratica — d/strati attorno a 128 — sono il frutto di misurazioni empiriche, non di una teoria.
MoE: spezzare l'MLP in esperti
Se l'MLP è il 70% dei parametri e il 70% del calcolo, l'idea è tenerne tanti ma accenderne pochi. Al posto di un MLP grande, N MLP più piccoli e un router che per ogni token ne sceglie k.
Una singola matrice [N, d] produce un punteggio per esperto; si prendono i k migliori e si combinano le loro uscite pesandole con quei punteggi. Il router si addestra insieme al resto. Nei MoE recenti gli esperti sono molti e piccoli (256 in DeepSeek-V3, 384 in Kimi K2, 8 attivi per token), più uno condiviso sempre acceso. Il bilanciamento spesso non usa più una loss aggiuntiva: a ogni esperto si somma un bias che cresce se riceve pochi token e cala se ne riceve troppi, senza toccare il gradiente del modello.
| Denso · il nostro modello | MoE tipico | |
|---|---|---|
| Parametri totali | 8 G | 400 G |
| Attivi per token | 8 G (100%) | 25 G (6%) |
| FLOP per token | 16 G | 50 G |
| Memoria richiesta | 16 GB | 800 GB — tutti i pesi devono essere caricati |
| Qualità attesa | di un 8 G | fra un 25 G e un 400 G denso |
Non c'è l'esperto di medicina e quello di codice: l'analisi delle attivazioni mostra suddivisioni molto più fini e poco interpretabili, spesso legate alla forma superficiale del token. Il nome è suggestivo e fuorviante.
Token diversi nello stesso batch vanno a esperti diversi, e su più schede questo significa spostare dati sulla rete a ogni strato. L'implementazione efficiente di un MoE è un problema di sistemi distribuiti, più che di reti neurali.
Dai logit alla scelta, con i numeri
L'ultimo vettore [4096] viene moltiplicato per la matrice di uscita [128256, 4096]: un prodotto scalare con ogni riga del vocabolario. Ne escono 128.256 punteggi non normalizzati — i logit.
Temperatura — riscala i logit. T→0 tende al massimo secco, T>1 appiattisce.
top-k — tiene solo i k candidati migliori e ridistribuisce.
top-p — tiene i candidati fino a cumulare probabilità p. Si adatta: pochi quando il modello è sicuro, molti quando è incerto. È il filtro più diffuso, ma nelle API di solito resta disattivato (p = 1) finché non lo si imposta.
2 · 4096 · 128.256 ≈ 1,05 GFLOP per token, contro i 14 GFLOP di tutti i 32 strati messi insieme: il 7% del calcolo per un solo prodotto. In addestramento, dove i logit si calcolano per tutte le posizioni contemporaneamente, questo tensore [batch, seq, 128256] è spesso il singolo consumo di memoria più grande dell'intero passaggio.
La loss: un numero, e cosa significa
Cross-entropy: L = −log p(token corretto), mediata su tutte le posizioni. Solo la probabilità assegnata al token giusto entra nel conto — tutte le altre contano indirettamente, perché la softmax le fa competere.
eL: quanti token equiprobabili il modello sta di fatto considerando. Perplexity 7,4 significa che, in media, esita fra circa sette possibilità — su un vocabolario di 128.256.
Da 2,0 a 1,9 sembra poco: sono 0,14 bit per token. Ma è anche il salto fra una generazione di modelli e la successiva, e costa un ordine di grandezza di calcolo. La loss è logaritmica: schiaccia miglioramenti enormi in differenze minuscole.
Backpropagation e il costo della memoria
Il gradiente dice, per ognuno degli 8 miliardi di parametri, di quanto cambierebbe la loss se quel parametro aumentasse di un'inezia. Si ottiene applicando la regola della catena all'indietro, riusando i risultati: un solo passaggio inverso per tutti i parametri insieme.
Calcolare il gradiente per differenze finite costerebbe un passaggio in avanti per ogni parametro: 8 miliardi di passaggi. La backpropagation costa circa due volte un passaggio in avanti, per tutti i parametri contemporaneamente. Senza questa asimmetria il deep learning non esisterebbe.
Uno è la media mobile del gradiente (la direzione recente), l'altro la media mobile del suo quadrato (quanto è instabile). Il passo effettivo è il primo diviso la radice del secondo: parametri con gradiente rumoroso si muovono meno. Costa 8 byte per parametro, e vale ogni byte.
Le attivazioni intermedie servono al passaggio inverso. Conservarle tutte è impossibile; si tengono solo quelle ai confini dei blocchi e si ricalcola il resto al volo — circa il 30% di calcolo in più per una riduzione di memoria molto maggiore.
Muon, usato nel pre-training di Moonlight e Kimi K2, tiene un solo stato, il momento, e ortogonalizza l'aggiornamento di ogni matrice: 12 byte per parametro invece di 16, cioè circa 96 GB invece di 128 sul nostro 8 G (qualcosa in più, perché embedding e uscita restano di solito ad AdamW), con una convergenza misurata come circa due volte più efficiente in calcolo. Nella pratica pesi, gradienti e stati non stanno interi su ogni scheda: ZeRO/FSDP li divide fra le N schede, che ne tengono 1/N ciascuna. Su 8 schede, 16 GB a testa invece di 128.
Le due formule che servono davvero
Con queste due si stima a mente quasi qualsiasi cosa: quanto costa addestrare un modello, quanto serve per servirlo, se un'idea è praticabile prima ancora di scrivere codice.
FLOP totali per addestrare N parametri su D token. Il 6 è 2 in avanti + 4 all'indietro.
Il nostro 8 G su 15.000 G di token: 6 · 8·10⁹ · 1,5·10¹³ = 7,2 · 10²³ FLOP. Su mille schede da 400 TFLOP/s efficaci al 40%: circa 52 giorni.
Byte per parametro: 4 in fp32, 2 in bf16, 1 in fp8, 0,5 a 4 bit più le scale.
Regola pratica per l'inferenza: miliardi di parametri × 2 = GB in bf16. Poi si aggiungono cache e margine — di solito il 20-30% in più.
Generare non è limitato dal calcolo
Per produrre un token bisogna leggere tutti i 16 GB di pesi dalla memoria della scheda e farci sopra appena 16 GFLOP. Il rapporto è di circa 1 operazione per byte letto — mentre una GPU moderna ne vorrebbe qualche centinaio per non restare in attesa.
La scheda passa la maggior parte del tempo ad aspettare la memoria, non a calcolare. Con 3 TB/s di banda, leggere 16 GB richiede 5,3 ms: circa 190 token al secondo, ed è un tetto imposto dalla banda, non dalla potenza di calcolo. Aggiungere FLOP non lo alza.
Tutti i token del prompt attraversano la rete insieme: si leggono i pesi una volta sola e si usano per 2048 posizioni. Migliaia di operazioni per byte: la scheda lavora a pieno regime.
Limitato dal calcolo. Un prompt lungo si elabora in una frazione del tempo che servirebbe a generarlo.
Un token per volta, e per ognuno si rileggono tutti i pesi. Un'operazione per byte: la scheda è quasi ferma ad aspettare.
Limitato dalla memoria. È la ragione economica per cui i token in uscita costano molto più di quelli in ingresso.
Se servi 64 richieste insieme, leggi i pesi una volta e li usi per 64 token: 64 volte il lavoro utile con la stessa lettura di memoria. È il motivo per cui i servizi in cloud raggruppano richieste di utenti diversi, e per cui un modello che gira solo per te è drammaticamente inefficiente.
Un modello piccolo propone cinque token, quello grande li verifica tutti insieme in un solo passaggio — perché verificare è come il prefill. Quelli giusti si tengono. Due o tre volte più veloce, con un risultato identico a quello che si sarebbe ottenuto comunque.
I pesi si leggono una volta per tutto il batch, ma la cache KV è diversa per ogni richiesta e va riletta ogni volta. Con 100.000 token di contesto sono ≈ 13 GB per utente, quasi quanto i 16 GB dei pesi: con 64 utenti a contesto lungo, a ogni passo si leggono 16 GB di pesi e 840 GB di cache, e il 98% della banda se ne va in cache. Per questo la cache per token è il numero su cui si progettano le architetture recenti.
Le formule, in una tabella
| Grandezza | Formula | Sul modello 8 G |
|---|---|---|
| Parametri per strato | 2 · d² + 2 · d · testeKV · dhead + 3 · d · dff (+ 2d di norme) | 218,1 M |
| Parametri totali | strati × (per strato) + 2 · V · d. A mente: 12 · d² · strati, 6,4 G | 8,03 G |
| FLOP in avanti, per token | 2 · N + 4 · strati · n · d | 16 GFLOP a contesto corto, ≈ 68 GFLOP con 100.000 token di contesto |
| FLOP di addestramento | 6 · N · D | 7,2 · 10²³ su 15 T token |
| Memoria dei pesi | N × byte per parametro | 16 GB in bf16 |
| Memoria di addestramento | ≈ 16 × N byte | 128 GB, più attivazioni |
| Cache KV per token | 2 · strati · testeKV · dhead · byte | 128 KiB |
| Costo dell'attenzione (QKᵀ + pesi·V) | 4 · n² · d · strati | 2,2 TFLOP a 2048 token, circa 1,1 con la maschera causale |
| Tetto di velocità in generazione | banda / memoria dei pesi | ≈ 190 token/s a 3 TB/s |
Moltiplicazioni fra matrici, ripetute molte volte.
Nessuna operazione di questa pagina è più complicata di un prodotto scalare, di un esponenziale o di una divisione. Quello che colpisce non è la sofisticazione dei pezzi: è che ripetendoli 8 miliardi di volte, su 15.000 miliardi di token, emerga qualcosa che sa scrivere.