GPT-2 Italiano 514M — modello didattico addestrato da zero
Un GPT-2 da 514 milioni di parametri addestrato interamente da zero sulla lingua italiana, su una singola GPU consumer (RTX 5070 Ti, 16 GB), come materiale didattico per l'insegnamento dell'intelligenza artificiale nella scuola secondaria di secondo grado.
Non è un modello utile in produzione. Serve a mostrare come funziona un LLM: cosa impara, cosa non impara, e perché. La sezione Limiti è la parte più importante di questa scheda.
Fratello minore: gpt2-it-185m-instruct — insieme formano un esperimento controllato sulle scaling law.
In breve
| Parametri | 514.5M |
| Architettura | GPT-2 (decoder-only), 24 layer, n_embd 1280, 20 teste |
| Context | 1024 token |
| Vocabolario | 32.001 (BPE ByteLevel addestrato da zero sull'italiano) |
| Token di pre-training | 10.000.000.423 (~19.5 token/parametro — Chinchilla-optimal) |
| Corpus | 25% Wikipedia IT + 75% FineWeb-2 ita_Latn |
| Validation loss (pre-training) | 2.686 |
| Validation loss (instruct) | 1.463 |
| Hardware | 1× RTX 5070 Ti 16GB (Blackwell sm_120) |
| Tempo di training | ~15 giorni, in sessioni interrompibili (scheduler WSD) |
| Costo elettrico stimato | ~45 € |
Come si usa
Il modello è addestrato esclusivamente sul formato Alpaca a turno singolo. Con un prompt diverso le risposte peggiorano sensibilmente.
from transformers import pipeline, PreTrainedTokenizerFast
MODEL = "emanuelealbertosi/gpt2-it-514m-instruct"
tok = PreTrainedTokenizerFast.from_pretrained(MODEL, clean_up_tokenization_spaces=False)
gen = pipeline("text-generation", model=MODEL, tokenizer=tok, device=0)
def rispondi(istruzione):
prompt = f"### Istruzione:\n{istruzione}\n\n### Risposta:\n"
out = gen(prompt, max_new_tokens=250, temperature=0.4, top_k=50,
repetition_penalty=1.2, do_sample=True,
pad_token_id=tok.eos_token_id, eos_token_id=tok.eos_token_id,
clean_up_tokenization_spaces=False)
return out[0]["generated_text"][len(prompt):].strip()
print(rispondi("Qual è la capitale della Francia?"))
Formato esatto del prompt (con ### Input: opzionale fra i due, se serve un contesto):
### Istruzione:
{domanda}
### Risposta:
Usare temperature bassa (0.3–0.5). A 0.7 il modello è più creativo ma sbaglia più spesso i fatti: nei test, "capitale della Francia" dà Parigi 3 volte su 4 a T=0.3, molto meno a T=0.7.
GGUF / LM Studio
Versione GGUF: gpt2-it-514m-gguf.
Template Jinja per LM Studio — solo modelli Instruct
Per i file Instruct usare una chat nuova con una sola domanda, senza system prompt.
Incollare nel campo Jinja solo il contenuto del blocco seguente, senza i delimitatori Markdown.
Questo template raccoglie i messaggi utente: una conversazione con più domande non è un uso
supportato dal modello. Non aggiungere prefissi Human: o AI:.
Scarica il template come file .jinja.
{% for message in messages %}{% if message['role'] == 'user' and message['content'] is defined %}### Istruzione:
{{ message['content'] }}
### Risposta:
{% endif %}{% endfor %}
Il blocco è stato verificato con Jinja2, anche con un messaggio assistant privo di content.
Questo non sostituisce una verifica nella specifica versione di LM Studio installata.
Se una chat conserva un vecchio preset, ricaricare il modello e aprire una chat nuova.
Per aggirare del tutto la formattazione chat, usare il server locale di LM Studio e
/v1/completions con il prompt testuale ### Istruzione:\nDOMANDA\n\n### Risposta:\n.
Documentazione dei template.
Parametri consigliati: temperature 0.4, top_k 50, repeat_penalty 1.2,
repeat_last_n 512. L'EOS è il token 32000 e viene riconosciuto correttamente da
llama.cpp. La generazione termina quando il modello emette EOS: non è garantito che lo
emetta dopo ogni risposta, quindi impostare anche un limite di nuovi token.
Cosa sa fare
Su domande brevi e concetti frequenti nel corpus è affidabile:
Qual è la montagna più alta del mondo? La montagna più alta del mondo è l'Everest, che raggiunge un'altezza di 8.848 metri (29.029 piedi). Si trova nella catena montuosa Mahalangur, nell'Himalaya, e si trova al confine tra Nepal e Tibet. ✅ (interamente corretto)
Qual è la capitale della Francia? La capitale della Francia è Parigi.
Elenca tre colori primari. I tre colori primari sono rosso, blu e giallo.
Chi è Mario Draghi? Mario Draghi è il presidente della Banca centrale europea (BCE). (ruolo corretto; data e luogo di nascita che seguono sono inventati)
Sulla scrittura creativa — dove non ci sono fatti da sbagliare — se la cava bene:
Descrivi una giornata al mare. Una giornata di sole è un'occasione meravigliosa per godersi il caldo bagliore del sole e fare delle lunghe nuotate. Il cielo azzurro si estende fino all'orizzonte, con nuvole bianche che brillano intensamente nel mezzo dell'azzurro intenso. L'aria è fresca e frizzante mentre ci si gode la scena serena della natura [...]
Limiti ed esempi di fallimento
Questa è la sezione per cui il modello esiste.
1. La finestra di competenza è di circa 30–50 token
Il pattern più costante: la prima frase è quasi sempre corretta, poi la risposta deriva.
Spiega cos'è la fotosintesi. La fotosintesi è il processo mediante il quale le piante, le alghe e alcuni batteri convertono l'energia della luce solare in energia chimica sotto forma di glucosio o zucchero. ✅ [...] La fotosintesi utilizza una varietà di biomassa composta da cloroplasti (carboidrati) ❌ [...] Le piante utilizzano anche l'emoglobina prodotta durante la fotosintesi per trasportare l'ossigeno ❌
L'apertura è da manuale di biologia. Duecento token dopo, non significa più niente.
2. Non sa contare le lettere (limite del tokenizer)
Quante r ci sono in giallo? In giallo ci sono 16 numeri interi positivi, quindi ci sono 16 numeri interi positivi in giallo. Quindi ci sono 16 numeri interi positivi in giallo. ❌
Il modello non vede i caratteri: vede token. "giallo" per il BPE è una manciata di simboli opachi, e contare le "r" richiede un accesso alle lettere che l'architettura non ha. È lo stesso fallimento del modello Emma-5 di Egomnia, per la stessa ragione strutturale.
3. Non sa fare aritmetica — e la simula
Il fallimento più istruttivo dell'intero progetto:
Quanto fa 47 × 23? La formula per calcolare la somma di tutti i multipli di un numero è (somma + divisore) / 2 [...]
47 × 23 = 49 / 3 49 × 23 = 50 / 3 50 × 23 = 53 / 3 795 / 3 = 895 / 3 ...(la lista prosegue per decine di righe)
Il modello ha imparato come appare un calcolo svolto — numeri, operatori, progressione, allineamento — senza nessuna relazione con la matematica. È la confabulazione trasferita dal dominio linguistico a quello simbolico.
4. Nessun ragionamento multi-step
Se ho 5 mele e ne mangio 2, poi ne compro il triplo di quelle rimaste, quante ne ho? Se ho 5 mele e ne mangio 2, poi ne compro il doppio di quelle rimaste, quante ne ho? ❌
Ripete la domanda, cambia una parola, non risponde.
5. Si contraddice all'interno della stessa risposta
Il muflone è un gatto? No, il muflone non lo è. ✅ Il termine "muflone" si riferisce a una persona o animale simile al maiale, che di solito depone le uova ❌ [...] Pertanto, sebbene il muflone sia considerato un tipo di gatto domestico ❌ (ha dimenticato il "no" iniziale)
La negazione iniziale è però un risultato notevole: il modello da 185M della stessa famiglia rispondeva "Sì, il muflone è un tipo di felino comune", assecondando la premessa falsa.
6. Non verifica quando gli si chiede di farlo
Cos'è un muflone? → "una piccola creatura mitica che viveva nella foresta pluviale amazzonica [...] ali che potevano raggiungere l'altezza di 20 piedi" ❌ Mi sei sicuro? → "Sì, sono sicuro! [...] La caratteristica distintiva dei mufloni è la loro lunga testa che può raggiungere oltre 20 piedi [...] cacciano con il bastoncino da caccia prede come leoni, tigri o coccodrilli" ❌
Alla richiesta di verifica non controlla nulla: rigenera, con dettagli nuovi e incoerenti con la risposta precedente. Non ha memoria di ciò che ha appena affermato — solo un contesto testuale su cui continuare.
7. Le parole rare vengono spezzate e agganciate male
A temperature diverse, la stessa domanda su un termine raro produce agganci lessicali completamente diversi:
| T | "Cos'è un muflone?" |
|---|---|
| 0.3 | "una piccola creatura pelosa con la testa e il corpo di colore marrone" |
| 0.7 | "una piccola palla di pelo che si trova in alcune razze di cani" |
| 1.0 | "una palla di bobbo, una sfera rotante ricoperta da particelle chiamate urati" |
In un'altra esecuzione il modello ha agganciato "muffin" e ha risposto con una ricetta britannica attribuita a Sir Thomas Mallory. "Muflone" non è un token singolo: il BPE lo spezza, e il frammento aggancia il vicino sbagliato.
8. Inventa fonti e istituzioni con totale naturalezza
Dammi un consiglio per studiare meglio. Usa siti web ben documentati come Coursera ✅, Udacity ✅ e Moodle ✅
In un'altra esecuzione, nella stessa lista compariva ABAB (American Bee-Based Learning Board) ❌ — inesistente, con lo stesso tono delle piattaforme vere.
Altri limiti
- Nessuna conversazione multi-turno (addestrato solo su istruzioni singole).
- Nessun allineamento: nessun RLHF, nessun filtro sui contenuti. Il modello riflette il web italiano su cui è addestrato, bias inclusi.
- Prolissità: tende a espandere anche quando la domanda richiede poco.
⚠️ Non usare questo modello come fonte di informazioni. Non è adatto a contesti educativi in cui gli studenti potrebbero prendere per buone le sue affermazioni, se non come esempio dichiarato di ciò che un LLM piccolo sbaglia.
Come è stato addestrato
Corpus
Streaming da Wikipedia italiana (fino a esaurimento del 25% del target, ~1B token) e
FineWeb-2 ita_Latn per il resto, tokenizzato al volo e scritto in un binario uint16
(20 GB) letto con np.memmap — approccio in stile nanoGPT, per non tenere mai il testo
grezzo su disco.
Tokenizer
BPE ByteLevel addestrato da zero su un campione di ~420 MB del corpus. Vocabolario 32.000
più <|endoftext|> aggiunto dopo il training, così da avere un id alto (32000).
Non è un dettaglio cosmetico: nel modello da 185M l'EOS era all'id 0, e llama.cpp non
fermava mai la generazione in GGUF pur avendo il metadato corretto
(tokenizer.ggml.eos_token_id = 0 presente, llm.token_eos() = 0, ma il token 0 non veniva
mai generato). Spostare l'EOS in fondo al vocabolario ha risolto alla radice.
Pre-training — scheduler WSD
Eseguito in sessioni interrompibili su ~15 giorni di calendario, con scheduler WSD (Warmup-Stable-Decay):
- Warmup: 2.000 step
- Stable: LR costante a 3e-4 per 152.587 step — la fase che si può fermare e riprendere liberamente, perché il modello resta sempre nello stesso "stato termico"
- Decay: 15.258 step (10%) con coseno fino a ~0
Configurazione: batch_size=2, gradient_accumulation_steps=32 (batch effettivo 64),
gradient_checkpointing=True, bf16, AdamW, weight decay 0.1. Una sola epoca sui 10B token.
L'effetto del decay
| fase | validation loss |
|---|---|
| step 100.000 (stable) | 2.878 |
| step 152.587 (fine stable) | 2.847 |
| dopo il decay | 2.686 |
Il decay ha prodotto in 15.000 step un calo (−0.16) maggiore di quello ottenuto nei 100.000 step precedenti a LR costante. È la dimostrazione pratica del perché WSD funziona: la fase stable accumula apprendimento che la loss non mostra finché il learning rate non viene raffreddato.
Instruct tuning
2 epoche su DanielSc4/alpaca-cleaned-italian (50.724 esempi), lr 2e-4, coseno,
validation loss finale 1.463.
Una prima versione a 3 epoche raggiungeva 1.505 con la val loss in risalita nell'ultima epoca: 3 epoche sono troppe per questa taglia. La versione a 2 epoche è migliore su tutti gli indicatori misurati (sovrapposizione lessicale con il training set al 25%, quindi generalizzazione e non memorizzazione).
Due accorgimenti necessari, entrambi scoperti a caro prezzo sul modello precedente:
- Ogni risposta termina con
\n+ EOS, non con il solo EOS. Nel pre-training ogni documento finiva contesto\n<|endoftext|>, quindi il modello aveva imparatoP('\n') ≈ 0.87prima dell'EOS. Chiedere l'EOS senza il\nsignificava chiederlo in una posizione dove il prior del pre-training vinceva sempre, e il modello non imparava mai a fermarsi. default_data_collatorinvece diDataCollatorForLanguageModeling: quest'ultimo maschera tutti i token uguali alpad_token, e siccome pad ed eos coincidono, mascherava anche l'EOS legittimo a fine risposta.
Scaling: la lezione centrale del progetto
Tre modelli della stessa famiglia, stesso tokenizer, stessa pipeline, corpus dello stesso tipo. Cambiano solo scala e nutrizione:
| modello | token | token/param | val loss | "Il muflone è un gatto?" |
|---|---|---|---|---|
| 185M | 1.4B | 7.6 | 3.373 | (non testato) |
| 185M | 4B | 21.5 | 3.108 | "Sì, è un tipo di felino comune" |
| 514M | 10B | 19.5 | 2.686 | "No, il muflone non lo è" |
Due conclusioni distinte:
Più dati a parità di parametri (7.6 → 21.5 token/param sul 185M) migliora nettamente la forma — sintassi, registro, coerenza locale — e non tocca la confabulazione sui fatti. Anzi la rende più insidiosa, perché il modello sbaglia con più autorevolezza.
Più parametri attacca il problema dal lato giusto: fatti comuni corretti, resistenza parziale alle premesse false, finestra di coerenza più ampia. Ma 514M restano 514M: la soglia oltre la quale un LLM diventa genuinamente affidabile è di un ordine di grandezza più in alto.
Uso didattico
Il modello è pensato per mostrare in classe, con un modello proprio e non con una scatola nera commerciale:
- cos'è la confabulazione e perché non è "un bug" ma il funzionamento normale di un predittore di token;
- perché la fluidità inganna — più il modello scrive bene, più le allucinazioni diventano difficili da riconoscere;
- come il tokenizer determina cosa il modello può e non può fare (le "r" in giallo; "muflone" → "muffin");
- la differenza tra imitare la forma e possedere il contenuto — il 47 × 23 è l'esempio più limpido;
- cosa significano le scaling law in pratica, con modelli confrontabili;
- quanto costa davvero addestrare un LLM: ~15 giorni di GPU consumer e ~45 € di corrente per un modello che comunque non è utilizzabile in produzione.
Licenza e attribuzione dei dati
Pesi rilasciati sotto Apache 2.0.
Corpus di pre-training:
- Wikipedia italiana (CC BY-SA)
- FineWeb-2, config
ita_Latn(ODC-By)
Instruct tuning:
Citazione
@misc{albertosi2026gpt2it514m,
title = {GPT-2 Italiano 514M: un LLM didattico addestrato da zero su GPU consumer},
author = {Emanuele Albertosi},
year = {2026},
url = {https://huggingface.co/emanuelealbertosi/gpt2-it-514m-instruct}
}
Modello base per completamento
La versione solo pretraining è disponibile come GGUF F16 nello stesso progetto: istruzioni per la demo. Non applicare al modello base il template Alpaca di questa scheda.