Tema

Machine learningdi

Il Transformer, aperto col cacciavite

Il pezzo sull’attenzione ha lasciato il meccanismo sul tavolo, smontato: domanda, chiave e valore, la softmax che pesa, la maschera causale, le teste multiple. Manca il montaggio - come questi pezzi si combinano in una macchina che genera testo. Questo pezzo lo fa seguendo le trecento righe di nanoGPT, il GPT minimo di Andrej Karpathy che la serie usa come cacciavite: un modello completo, licenza MIT, pensato per essere letto. Ogni componente che incontreremo ha un nome, un posto e poche righe di codice.

La promessa della serie si chiude qui per metà: il montaggio. Sappiamo che cosa entra (token e posizione), sappiamo come i token si mettono in relazione (l’attenzione). Resta da vedere come si alternano i pezzi, che cosa tiene in piedi una pila di dodici blocchi, e come dalla cima della pila esca la probabilità del prossimo token.

L’inventario: i numeri del config e il percorso

I numeri di nanoGPT stanno in cima al file: dodici blocchi, dodici teste, 768 dimensioni, un vocabolario di 50.304 voci; e il percorso sta in poche righe.

Il config di nanoGPT dichiara la taglia del modello prima di costruirlo: una finestra di contesto (nel codice block_size) di 1024 token, un vocabolario di 50.304 voci - la taglia di GPT-2 (50.257) arrotondata a un multiplo di 64 per efficienza -, dodici blocchi in pila, dodici teste di attenzione, e una dimensione interna di 768 numeri per vettore. Sono i numeri del GPT-2 di base, che nanoGPT riproduce: il modello da 124 milioni di parametri che il pezzo sull’embedding ha già incontrato parlando del trapianto del vocabolario.

Il percorso del testo dentro il modello sta in poche righe della funzione forward: la somma dei due embedding - quello del token e quello della posizione, già visti nel pezzo sull’embedding - passa per i dodici blocchi uno dopo l’altro, poi per un’ultima normalizzazione e infine per la testa d’uscita, che produce i punteggi che la softmax trasforma in probabilità. Ogni passo del percorso corrisponde a un pezzo che la serie ha già aperto: la somma sta nel pezzo sull’embedding, l’attenzione nel proprio, la softmax nel pezzo sulle reti multistrato. Mancava l’ordine in cui si montano, ed è l’ordine del codice.

Un blocco: attenzione e MLP, con la scorciatoia

Il blocco alterna due operazioni: l’attenzione mette in relazione le posizioni, l’MLP elabora ogni posizione da sola; intorno a ognuna una connessione residua, la somma dell’ingresso con l’uscita che lascia scorrere il segnale.

La classe Block di nanoGPT è la scatola che si ripete dodici volte, ed è il blocco del Transformer; il suo corpo è di quattro righe, di cui queste sono le due che contano:

x = x + attn(ln_1(x))
x = x + mlp(ln_2(x))
Il percorso del modello e l'interno di un blocco A sinistra il percorso: la somma dell'embedding del token e di quello della posizione entra nella pila di dodici blocchi, la testa d'uscita riporta l'ultimo vettore alla taglia del vocabolario, la softmax produce le probabilità del prossimo token. A destra l'interno di un blocco: l'ingresso passa per una normalizzazione, poi per l'attenzione; la connessione residua, in tratteggio, somma l'ingresso all'uscita dell'attenzione; il risultato passa per una seconda normalizzazione, poi per l'MLP; una seconda connessione residua somma l'ingresso del sotto-blocco alla sua uscita; il risultato è l'uscita del blocco. IL PERCORSO wte + wpe TOKEN E POSIZIONE BLOCCHI × 12, IN PILA TESTA D'USCITA 768 → 50.304 PROBABILITÀ SOFTMAX IL BLOCCO, APERTO INGRESSO NORM ATTENZIONE + NORM MLP + USCITA RESIDUO RESIDUO
A sinistra il percorso del modello: la somma dei due embedding entra nella pila di blocchi, la testa d'uscita riporta l'ultimo vettore alla taglia del vocabolario e la softmax produce le probabilità del prossimo token. A destra l'interno di un blocco: attenzione e MLP si alternano, ognuno preceduto da una normalizzazione; le due connessioni residue, in tratteggio, sommano l'ingresso all'uscita - la scorciatoia del gradiente.

Il blocco fa due operazioni, in ordine, e le circonda con due gesti. La prima operazione è l’attenzione multi-testa del pezzo precedente: mette in relazione le posizioni fra loro - è il punto dove la frase si lega. La seconda è l’MLP del Transformer, di cui parleremo fra un momento: elabora ogni posizione da sola. L’alternanza è il ritmo del Transformer: prima le relazioni, poi il pensiero locale, poi di nuovo le relazioni.

I due gesti sono la normalizzazione e la scorciatoia. La normalizzazione di livello (ln_1, ln_2) riporta i vettori a una scala comune prima di ogni operazione: i prodotti scalari dell’attenzione tendono a crescere con la dimensione (è il motivo della divisione per la radice quadrata visto nel pezzo sull’attenzione), e senza una messa in scala i numeri finirebbero per esplodere lungo la pila. La connessione residua è il x + che somma l’ingresso del sotto-blocco alla sua uscita. La somma ha due effetti. Il primo è pratico: il blocco non deve ricostruire la rappresentazione da zero, le basta correggerla - e un’aggiunta è più facile da imparare di una riscrittura. Il secondo è decisivo per l’addestramento: quando la retropropagazione risale la pila, la derivata della somma fa passare il segnale d’errore per intero, senza attraversare il sotto-blocco. Il gradiente ha una scorciatoia, ed è questo che regge una pila profonda.

L’MLP: ogni posizione da sola

L’MLP allarga il vettore di quattro volte, lo passa per una curva e lo riporta alla dimensione di partenza: elabora ogni posizione indipendentemente, senza guardare le altre.

La classe MLP fa tre cose, e si capiscono dal codice: una proiezione allarga il vettore da 768 a 3.072 numeri - quattro volte la dimensione interna -, una curva morbida li trasforma, e una seconda proiezione li riporta a 768. La curva è la GELU, una variante della ReLU del pezzo sulle reti multistrato: zero per i valori molto negativi, quasi lineare sopra, e una transizione dolce in mezzo che tiene viva la pendenza.

Il punto è quello che l’MLP non fa: non guarda le altre posizioni. Ogni vettore della sequenza attraversa l’MLP da solo, con gli stessi pesi. Le relazioni fra posizioni le fa soltanto l’attenzione; l’MLP è il momento in cui ogni posizione “pensa” per conto proprio ciò che ha raccolto. L’alternanza dei due è il meccanismo completo: la frase si lega, ogni parola elabora quello che ha imparato, e il legame si rinnova.

La testa d’uscita: dall’ultimo vettore alla probabilità

L’ultimo strato riporta il vettore alla taglia del vocabolario, e la softmax ne fa una distribuzione di probabilità su ogni possibile prossimo token.

Quando la sequenza ha attraversato i dodici blocchi, l’ultimo vettore (quello dell’ultima posizione) deve diventare la scelta del prossimo token. Lo fa la testa d’uscita, una proiezione da 768 a 50.304 numeri: uno per ogni voce del vocabolario. Quei numeri sono i logits, le somme grezze che la softmax delle reti multistrato trasforma in probabilità - 50.304 valori positivi che sommano a 1. Il prossimo token non è ancora scelto: è pesato.

Il codice di nanoGPT ripete qui un dettaglio che la serie ha già incontrato. La testa d’uscita condivide la matrice con l’embedding dei token

La generazione: un token alla volta

Il modello non scrive una risposta: a ogni passo pesa tutte le voci del vocabolario e ne sceglie una, poi ripete con la sequenza allungata - finché non ha prodotto i token richiesti.

La funzione generate di nanoGPT è il ciclo completo. A ogni passo il modello riceve la sequenza finora prodotta, calcola le probabilità dell’ultima posizione, e ne sceglie una. Il codice mostra tre dettagli che contano. Il primo: se la sequenza è più lunga del blocco di contesto, si tiene solo la coda - la finestra si taglia, e il costo è quello quadratico del pezzo sull’attenzione. Il secondo: prima della softmax i logits si dividono per la temperatura, il cursore che il piccolo GPT di questa serie mostra dal vivo: una temperatura bassa schiaccia le probabilità verso il candidato più alto e il testo diventa prevedibile; alta le appiattisce e il testo diventa creativo, fino all’insensato. Il terzo dettaglio è il più facile da trascurare: la scelta non prende il candidato più probabile: è un campionamento, cioè una scelta fatta secondo le probabilità - una moneta truccata, non una risposta secca. Per questo lo stesso modello, con lo stesso ingresso, può produrre testi diversi: la probabilità decide quanto spesso ogni candidato vince, non quale deve vincere.

Il token scelto si aggiunge alla sequenza, e il ciclo ricomincia: il modello legge ciò che ha appena scritto, pesa e sceglie di nuovo. È esattamente ciò che il pezzo sul tokenizzatore aveva descritto a parole - una rete che legge una sequenza di token e la allunga di un token - ora con il meccanismo per intero.

Potete smontarlo: il widget qui sotto usa il piccolo GPT della serie, gli stessi pesi e un seme fisso, e vi lascia togliere pezzi - la connessione residua, l’attenzione, l’MLP, i blocchi. A parità di seme e di pesi, se il testo cambia è colpa di quello che avete smontato.

Il piccolo GPT della serie, aperto col cacciavite: premete «Genera» e poi togliete un pezzo alla volta. Seme fisso, confronti onesti.

Cosa togliere
Quanti blocchi

blocchi: 4 su 4

Temperatura

temperatura: 0,80

Le tre difese del montaggio

Le connessioni residue, la normalizzazione e l’inizializzazione calibrata fanno sì che dodici blocchi in fila non si rompano: il segnale scorre, il gradiente risale, e i numeri restano in scala.

Dodici blocchi in pila non crollano sotto il loro stesso peso perché il montaggio ha tre difese, e tutte e tre stanno nel codice. La prima è la connessione residua: il gradiente risale per la scorciatoia senza attraversare i sotto-blocchi, e il lavoro di ogni blocco resta una correzione. La seconda è la normalizzazione: le scale restano sotto controllo a ogni passaggio, e i numeri non esplodono né si spengono. La terza è l’inizializzazione calibrata: l’ultima proiezione di ogni blocco - la c_proj che riporta alla dimensione di partenza - parte con una deviazione standard tanto più piccola quanto più la pila è profonda: 0,02 diviso per la radice del doppio del numero di blocchi, circa 0,004 con dodici blocchi. Così, all’inizio, ogni blocco aggiunge un ritocco minuscolo, e l’addestramento può decidere quanto farli crescere.

Il GPT-2 di base ne ha dodici; il GPT-2 XL arriva a quarantotto blocchi e 1.558 milioni di parametri. La profondità non è gratis: ogni blocco aggiunge calcolo e memoria, e il costo sale con la finestra di contesto. Il pezzo sulle evoluzioni racconta come i modelli riducono quel costo senza rinunciare al montaggio.

Perché un modello piccolo

La serie apre un modello minimo perché è completo: ha gli stessi componenti dei grandi, in poche righe leggibili. E mentre GPT e Claude non dichiarano come sono fatti dentro, il modello piccolo si può aprire per intero.

Il cacciavite funziona perché il modello che apre è piccolo ma completo: nanoGPT contiene l’intero percorso - la somma dei due embedding, i blocchi di attenzione e MLP, la testa d’uscita, il campionamento - e ogni componente sta in poche righe. Un modello grande non è una macchina diversa: è la stessa, con più blocchi, più parametri e qualche trucco in più. I trucchi sono la materia dei prossimi due pezzi: la miscela di esperti e la cache di chiavi e valori, che riducono il costo che questo pezzo ha appena misurato. Una ragione in più chiude il cerchio: di DeepSeek e Qwen i rapporti tecnici dicono come sono fatti dentro, di GPT e Claude no. Il modello piccolo è l’unico di cui questa serie può dire per intero com’è fatto.

I concetti che questo articolo introduce

Sei voci coprono il montaggio: il blocco, i due gesti che lo sorreggono, il pensiero locale, la testa e la scelta del prossimo token.

Concetto Ambito Che cos’è Si lega a
Blocco del Transformer machine-learning L’unità che si ripete lungo la pila: una normalizzazione, l’attenzione multi-testa, una connessione residua, poi una seconda normalizzazione, l’MLP e una seconda connessione residua compone il meccanismo di attenzione e l’MLP del Transformer; la sua pila è il corpo del modello di linguaggio
Connessione residua machine-learning La somma dell’ingresso di un sotto-blocco con la sua uscita: il segnale e il gradiente hanno una scorciatoia, e ogni blocco corregge invece di riscrivere è nel blocco del Transformer; rende addestrabile la profondità (la profondità della rete)
Normalizzazione di livello machine-learning La messa in scala che riporta i vettori a una scala comune prima di ogni operazione, tenendo i numeri sotto controllo lungo la pila precede il meccanismo di attenzione e l’MLP del Transformer nel blocco; stabilizza l’addestramento
MLP del Transformer machine-learning Lo strato che allarga ogni vettore di quattro volte, lo passa per una curva morbida e lo riporta alla dimensione di partenza, elaborando ogni posizione da sola è nel blocco del Transformer; usa la GELU, variante della ReLU delle reti multistrato; non guarda le altre posizioni
Testa d’uscita machine-learning L’ultimo strato che riporta il vettore alla taglia del vocabolario, da cui la softmax produce le probabilità del prossimo token condivide la matrice con l’embedding del token (weight tying); è seguita dalla softmax e dal campionamento
Campionamento machine-learning La scelta del prossimo token secondo le probabilità - con la temperatura che le schiaccia o le appiattisce - invece di prendere il candidato più probabile segue la softmax; alimenta la generazione un token alla volta

Fonti

Le affermazioni portanti vengono dal codice nanoGPT fissato al commit esaminato, letto per intero per questo pezzo.