SLM · Small Language Model

Un transformer italiano costruito dall'interno

I grandi modelli linguistici sembrano capire l'italiano. Scrivono, rispondono, ragionano. SLM è un esperimento per capire cosa c'è davvero dietro — costruendone uno da zero, piccolo, con un laptop da 8 GB e qualche mese di lavoro. Non è un'alternativa ai grandi modelli. È uno strumento per capire come funzionano dall'interno, perché costruire qualcosa è il modo più onesto per capirlo davvero.

Una nota prima di cominciare. Le dimensioni che troverete in questa documentazione — 46 milioni di parametri, un vocabolario di 16.000 unità, 512 dimensioni per ogni rappresentazione, 12 livelli di elaborazione — non sono la forma obbligata di un transformer. Sono scelte deliberate, fatte per un progetto con due obiettivi espliciti: essere comprensibile e girare su hardware piccolo, un Mac con 8 GB di memoria. Un transformer può avere 7 miliardi di parametri, come LLaMA 3, oppure 70 miliardi, come i modelli di fascia alta, oppure 46 milioni, come questo. Il meccanismo è lo stesso in tutti i casi: stessa architettura, stesse operazioni, stessa logica. Cambiano le dimensioni, e con le dimensioni cambiano le capacità. I numeri che seguono sono un'istanza di un continuum, non valori assoluti.

Dal testo ai numeri

Prima di entrare nell'architettura, c'è una cosa da tenere a mente per tutto il resto della lettura: il modello non vede mai il testo. Da quando il corpus viene processato, il testo scompare. Quello che rimane sono numeri, e da numeri si parte fino alla fine.

Step 1 — Tokenizzazione (avviene una volta sola, offline)

Un tokenizer — nel nostro caso BPE-SPM con un vocabolario di 16.000 unità — divide il testo in pezzi chiamati token e assegna a ciascuno un numero identificativo. "il" potrebbe essere il token 312. "gatto" potrebbe essere due token: "gat" e "to". "addestramento" potrebbe diventare "addestr" e "amento". La suddivisione è statistica, non grammaticale. Il risultato è una sequenza di interi salvata in un file .pt su disco. Quel file è l'unica forma in cui il corpus esiste per il modello. Il testo originale non viene mai più letto.

"il gatto dorme sul tetto"
        ↓
[312, 8741, 2093, 451, 9834]

Step 2 — Embedding (primo strato del modello)

Una sequenza di interi non è ancora qualcosa su cui si possa fare algebra utile. Il primo strato del modello — l'embedding — è una tabella di lookup: per ogni intero da 0 a 15.999, associa un vettore di 512 numeri float. Questa tabella è parte del modello e viene appresa durante il training. All'inizio i vettori sono casuali. Dopo milioni di step, token con significati simili si troveranno vicini nello stesso spazio geometrico. L'embedding è l'unico punto in cui un intero diventa un vettore. Da qui in avanti, il modello non sa più che c'era del testo.

312  →  [0.12, −0.45, 0.87, …, 0.33]   ← 512 dimensioni
8741 →  [−0.91, 0.22, 0.04, …, 0.67]

Step 3 — Transformer (pura algebra lineare)

I layer elaborano la sequenza di vettori attraverso meccanismi di attenzione e reti feed-forward. Nessun riferimento al testo originale: solo operazioni su tensori float.

Step 4 — Prediction e loss

L'output finale è una distribuzione di probabilità su tutti i 16.000 token del vocabolario. Il modello risponde: "dato questo contesto, quale token viene dopo?" Il target è il token successivo reale, letto direttamente dal .pt. La loss misura l'errore; la backpropagation aggiusta i pesi.


Embedding, attenzione, parametri

L'embedding — lo spazio delle rappresentazioni. La tabella di embedding non codifica relazioni tra token: rappresenta ogni token individualmente come un punto in uno spazio di 512 dimensioni. Dopo il training, token simili per significato tendono a stare vicini in quello spazio, ma questa vicinanza emerge come effetto del training, non è programmata. L'embedding costruisce il terreno su cui l'attenzione può lavorare.

L'attenzione — il calcolo delle relazioni. Le relazioni tra token non vengono memorizzate: vengono calcolate ogni volta che una sequenza passa attraverso il modello. Per ogni token, il meccanismo di attenzione calcola tre proiezioni usando tre matrici di pesi (W_Q, W_K, W_V) che il modello impara durante il training:

query  → "cosa sto cercando?"
key    → "cosa offro agli altri?"
value  → "cosa trasmetto se vengo scelto?"

Il prodotto scalare tra la query di un token e le key di tutti gli altri produce punteggi di rilevanza. Dopo una normalizzazione (softmax), questi punteggi diventano pesi per una media dei value. Le matrici W_Q, W_K, W_V non contengono relazioni memorizzate: contengono funzioni per rilevarle.

Un singolo meccanismo di questo tipo si chiama head. Il nostro modello usa 8 head in parallelo, ognuna con le proprie matrici. Ciascuna impara a rilevare un tipo diverso di relazione. Questa specializzazione emerge dal training, non è progettata.

I parametri — la conoscenza distribuita. I parametri sono tutti i numeri che compongono le matrici del modello. Prima del training, sono valori casuali. Dopo milioni di step, ogni numero è stato aggiustato finché il modello non ha imparato a predire l'italiano. La conoscenza non è localizzata in nessun posto specifico: è distribuita su tutti i 45.997.056 numeri insieme.

MatriceDimensioniParametri
Embedding16.000 × 5128.192.000
W_Q, W_K, W_V, W_O (per layer)512 × 512 × 41.048.576
FFN W1 + W2 (per layer)512×2048 + 2048×5122.097.152
Layer norm ×2 (per layer)512 × 2 × 22.048
× 12 layer~37,8M
Embedding8,2M
Totale45.997.056

Il file best.pt è esattamente questo: un dizionario di matrici con i valori appresi. Caricarlo significa copiare quei numeri in memoria. Il continued pre-training riparte da quei numeri invece che dal caos iniziale. I numeri che non vengono mai toccati dal training si chiamano iperparametri: d_model, n_heads, n_layers, learning_rate. Definiscono la struttura e il processo, non il contenuto.


Cosa può fare questo modello

Un decoder-only transformer impara un'unica cosa: predire il token successivo. Tutto il resto — grammatica, coerenza, fatti, stile — emerge come side effect di questa previsione ripetuta su enormi quantità di testo. La capacità di un decoder dipende da tre fattori che interagiscono:

FattoreIn questo modelloCosa determina
Numero di parametri45.997.056Quanti pattern il modello può memorizzare e distinguere
Numero di layer12La profondità dell'elaborazione — quanti livelli di astrazione sopra la sequenza grezza
Dimensione embedding512La risoluzione delle rappresentazioni — quanta sfumatura ogni token può portare

Con 46 milioni di parametri addestrati su corpus italiano di qualità, questo modello può produrre italiano grammaticalmente corretto, sviluppare coerenza a livello di frase e di paragrafo breve, e riflettere lo stile del corpus su cui è stato addestrato. Non può costruire ragionamenti complessi a più passi, non copre in modo affidabile la conoscenza enciclopedica, e la sua finestra di contesto è limitata a 512 token.

I grandi modelli — GPT-4, LLaMA 3 70B — hanno parametri nell'ordine dei miliardi. Il meccanismo è lo stesso: stessa architettura, più numeri, più layer, finestre di contesto molto più lunghe, e quantità di testo di training su scala industriale. La perplexity misura la distanza: Run #4 è a PPL ~36. I modelli di riferimento sull'italiano sono a PPL ~5–8.

I due percorsi qui sotto permettono di esplorare questo progetto da due angolature diverse: la storia e le ragioni, oppure l'architettura e il codice.
Il progetto
L'idea, il percorso, il perché
Per chi vuole capire cosa sta succedendo, senza tecnicismi. La storia del progetto e le ragioni che lo muovono.
Leggi →
La costruzione
Architettura, training, risultati
Per chi vuole scendere nell'architettura, nel codice e nelle decisioni tecniche passo dopo passo.
Esplora →
Phase 10 completata — esplorazione architetturale conclusa
best val loss 0.0293 (LoRA v2, PPL≈1.0) · 0.1322 (LoRA v1) · 2.8165 (base) parametri 45.9M base · 393k LoRA trainabili (0.85%) · adapter 1.6MB fase Tutte le fasi 01–10 completate. Riflessioni architetturali documentate: BERT encoder, GBNF, limiti del decoder a 45M. Prossimo: YOLO v3 multimodale (Phase 11) o LoRA su modello base più grande.