6 ottobre 2026

Addestrare un mini-LLM da 25M parametri su CPU: anatomia di un GLM “flash” e cosa imparare davvero

Dalla tokenizzazione “a byte” alle scelte architetturali (attenzione ibrida, MoE, weight tying): come costruire un modello piccolo ma utile per sperimentare con pre-training e post-training.

Un modello da 25 milioni di parametri è abbastanza piccolo da girare su CPU, ma abbastanza complesso da insegnare le stesse idee che contano nei LLM moderni: come rappresentano i token, perché il vocabolario può divorare i parametri, cosa cambia con attenzione lineare+sparse, a cosa servono le residual “hyperconnections” e come impostare esperimenti sensati tra pre-training e reinforcement learning. In questo articolo mettiamo ordine nei concetti e nelle scelte pratiche per chi vuole sperimentare in locale senza farsi bloccare dal compute.

Costruire e addestrare un LLM “da zero” su una macchina comune non è un esercizio nostalgico: è il modo più rapido per capire cosa conta davvero in un sistema moderno, senza dover inseguire miliardi di parametri e infrastrutture costose. Un modello nell’ordine dei 25 milioni di parametri è abbastanza piccolo da iterare velocemente, ma abbastanza grande da rendere evidenti i compromessi architetturali che oggi guidano quasi tutte le scelte industriali.

L’obiettivo non è replicare un modello frontier: è imparare a progettare esperimenti, leggere segnali di training e capire come si combinano pre-training e post-training (in particolare via reinforcement learning) per ottenere capacità osservabili.

1) L’unità fondamentale: next-token prediction

Un LLM, in forma essenziale, fa una cosa: dato un contesto, stima una distribuzione di probabilità sul prossimo token.

Pipeline concettuale:

  1. Tokenizzazione: testo → sequenza di token ID.
  2. Embedding: token ID → vettori (uno per token).
  3. Transformer layers: i vettori vengono trasformati iterativamente.
  4. LM head: l’ultimo stato (o gli stati) viene proiettato in una distribuzione su tutto il vocabolario.

Questa semplicità è ingannevole: molte decisioni critiche (compute, parametri, memoria, stabilità numerica) derivano proprio dai punti 1 e 4.

2) Perché nei modelli piccoli conviene tokenizzare “a byte” (o a caratteri)

Nei modelli reali si usano tokenizer subword (BPE e simili) con vocabolari enormi (centinaia di migliaia di token). Funziona benissimo… finché non lavori con un modello molto piccolo.

Il motivo è pratico: embedding matrix e output projection scalano con la dimensione del vocabolario.

  • Embedding: vocab_size × hidden_dim
  • Output head: hidden_dim × vocab_size

Con un vocabolario gigantesco, un modello “mini” finisce per sprecare la maggior parte dei parametri solo per convertire token↔vettori, lasciando pochissima capacità ai layer “intelligenti”.

Una scelta tipica per sperimentare è quindi una tokenizzazione a byte/caratteri con ~256–260 token. Risultato:

  • niente training del tokenizer
  • embedding e head diventano leggeri
  • più parametri disponibili per attenzione/FFN/MoE
  • iterazioni più rapide, ottime per ricerca e debugging

In un contesto di sperimentazione locale, è spesso un trade-off vincente.

3) Weight tying: stessi pesi per input embedding e output head

Un’ottimizzazione classica ma ancora attuale è il weight tying: usare la stessa matrice per

  • trasformare token ID → embedding
  • proiettare hidden state → logits sul vocabolario

In pratica riduci parametri e spesso ottieni anche un comportamento più coerente tra “spazio degli embedding” e “spazio delle predizioni”. Su un modello piccolo, il risparmio è particolarmente significativo.

4) Stabilità numerica: RMSNorm e perché ti salva l’allenamento

Nei transformer, le attivazioni possono crescere o collassare mentre attraversano molti layer; questo rende l’ottimizzazione instabile (gradienti che esplodono o svaniscono, dominance di poche dimensioni, ecc.).

La RMSNorm normalizza le attivazioni mantenendole su scale gestibili, facilitando backprop e training prolungato. È uno di quei dettagli “non sexy” che però determinano se un run converge o no.

5) Positional encoding: come il modello distingue l’ordine

L’attenzione, da sola, non conosce l’ordine dei token: vede un set di vettori e calcola affinità. Per introdurre l’informazione posizionale, si usano encoding come RoPE (rotary positional embeddings), che ruotano coppie di dimensioni degli embedding in base alla posizione.

Nelle varianti più recenti capita di separare:

  • attenzione “principale” con poca o nessuna positional encoding
  • un meccanismo ausiliario (ad esempio un indicizzatore) che usa encoding posizionali per decidere dove guardare

Questo tipo di decomposizione è utile quando vuoi ottimizzare prestazioni e scalabilità su contesti lunghi.

6) Attenzione ibrida: lineare + sparsa (e perché è “flash”)

L’attenzione standard ha un costo quadratico sulla lunghezza del contesto: se raddoppi i token, il compute cresce molto più che linearmente.

Un’idea sempre più comune è usare un ibrido:

  • attenzione lineare (o meccanismi affini agli state-space): mantiene uno “stato” di memoria a dimensione fissa che si aggiorna al passare dei token. È veloce e scala bene, ma comprime la storia (perdi dettaglio sui token lontani).
  • attenzione sparsa: invece di attendere su tutti i token, seleziona solo un sottoinsieme “rilevante” (o una finestra) e calcola attenzione completa solo lì. Recupera dettagli distanti senza pagare sempre il costo pieno.

Un pattern pratico è alternare i layer: ad esempio impostare ogni N-esimo layer come sparse attention e usare linear attention negli altri. Il risultato è un modello con:

  • memoria “a basso costo” per il recente e il contesto generale
  • recupero periodico di dettagli precisi quando serve

L’indicizzatore (sparse indexer)

Per fare attenzione sparsa bene serve decidere quali token sono rilevanti. Un approccio efficace è un indicizzatore leggero: una forma semplificata di attenzione che costa poco e serve solo a selezionare candidati. Poi, sui candidati selezionati, applichi l’attenzione completa.

Questa divisione del lavoro (selezione economica → attenzione costosa su subset) è una delle chiavi per contesti lunghi e inferenza rapida.

7) Residual “hyperconnections”: più autostrade per l’informazione

Nei transformer classici, la residual connection è un singolo “canale” che porta informazione tra i layer. Alcune architetture recenti introducono più percorsi residuali (hyperconnections), che vengono combinati con pesi di mixing.

L’intuizione: non tutta l’informazione dovrebbe essere forzata a passare attraverso le stesse trasformazioni (attenzione/FFN). Avere più “corsie” permette di preservare e trasportare segnali diversi, che verranno poi fusi.

Su modelli piccoli, questo può aiutare la dinamica di training, soprattutto quando si cerca efficienza senza aumentare troppo profondità o dimensioni.

8) Mixture of Experts (MoE): capacità condizionata senza costo pieno

MoE significa avere molte “reti esperte” e attivarne solo alcune per token. Invece di far passare ogni token attraverso un enorme FFN, scegli un sottoinsieme di esperti.

Vantaggio: aumenti la capacità effettiva del modello (più parametri totali) mantenendo il compute per token relativamente controllato.

Nel mondo reale si parla di centinaia di esperti e pochi scelti per token (top-k). In scala ridotta, l’idea resta la stessa: routing + subset di esperti.

9) Pre-training vs post-training: cosa cambia davvero

Con un modello piccolo e un contesto breve, è comune osservare che il solo pre-training (next-token prediction generico) non basta per risolvere task “operativi” anche semplici.

Il salto qualitativo spesso arriva nel post-training, dove l’obiettivo non è più soltanto predire il token successivo, ma ottimizzare un comportamento rispetto a ricompense o criteri.

Esempi tipici di domande sperimentali in RL/post-training:

  • conviene dare ricompensa parziale per soluzioni “quasi giuste” oppure premiare solo la risposta perfetta?
  • quanto penalizzare output non valido (es. codice che non compila)?
  • che ruolo ha la temperatura nella fase di esplorazione (trade-off esplorazione/sfruttamento)?

Queste domande contano più del “fare girare il training”: sono ciò che distingue un set-up che produce un miglioramento reale da uno che semplicemente consuma tempo.

10) La lezione operativa: iterazione rapida > compute

Allenare un mini-LLM su CPU non è una rinuncia: è un metodo.

  • vocabolario piccolo (byte/char) per evitare che embedding/head divorino parametri
  • weight tying per risparmiare memoria e migliorare coerenza
  • normalizzazione (RMSNorm) per stabilità
  • attenzione ibrida (lineare + sparsa) per efficienza
  • MoE e hyperconnections come strumenti per aumentare capacità e qualità senza scalare brutalmente il modello
  • post-training come fase in cui definisci che cosa deve imparare davvero

Sintesi e implicazione pratica

Un modello da ~25M parametri è un laboratorio perfetto: abbastanza economico da permettere molte prove, abbastanza realistico da farti vedere i compromessi che guidano i sistemi moderni. Se vuoi imparare (o insegnare nel team) come si progettano esperimenti su LLM, la strategia migliore è ridurre la scala, fissare metriche chiare e usare l’iterazione rapida per rispondere a domande mirate. È lì che si costruisce competenza: non nell’inseguire il modello più grande, ma nel saper scegliere quale esperimento vale la pena fare domani.