Corpus · Qualità
2026-10-03
Il bias del PPL scorer: poesia esclusa, pubblicità inclusa
Il quality scorer basato sulla perplexity (PPL scorer) usa il checkpoint Run #3 — addestrato su Wikipedia italiana — per misurare quanto un testo sembri "italiano normale". Documenti con alta perplexity vengono esclusi come rumore.
Il problema: il giudice ha un'idea molto specifica di cosa sia "italiano normale". Sa com'è scritto Wikipedia. Non sa com'è scritta una poesia.
Cosa succede in pratica
- Poesia esclusa: la sintassi poetica è volutamente inusuale — inversioni, enjambement, assenza di verbi. Il scorer la vede come errore e assegna alta perplexity. Una poesia di Pascoli nel corpus viene rigettata.
- Pubblicità inclusa: i testi pubblicitari sono grammaticalmente corretti, usano un vocabolario standard, hanno struttura sintattica regolare. Il scorer non ha motivo per escluderli. Vengono tenuti.
Perché non è un bug
Il scorer fa esattamente quello che gli è stato chiesto: trovare testi simili a Wikipedia. Il bias è una conseguenza diretta della distribuzione del training data del giudice. Un scorer addestrato su narrativa includerebbe la poesia ed escluderebbe (forse) la pubblicità.
La valutazione per CulturaX
Per CulturaX — un dataset già pre-filtrato dai suoi autori con metodi simili — il PPL scorer aggiunge più bias che segnale. Il testo che esclude non è necessariamente cattivo: è solo diverso da Wikipedia. La soglia al 95° percentile è stata mantenuta come misura conservativa, consapevoli del bias.
Implicazione per run future: addestrare scorer separati per dominio (narrativo, enciclopedico, tecnico) e applicarli selettivamente per tipo di fonte. Alternativa: usare solo euristico + dedup per corpus già pre-filtrati.
Infrastruttura · RAM
2026-10-03
Training a shard: quando il corpus non sta in memoria
Il problema
Il corpus Run #5 pesa 8.29 GB. Su un Mac M2 con 8 GB di RAM condivisa tra CPU e GPU, il processo di caricamento andava in crash prima che il training iniziasse.
La causa era in tre passaggi consecutivi del codice originale:
Path.read_text() legge l'intero file → 8.29 GB in RAM come stringa Python
tok.encode(text) restituisce una lista Python di interi → ogni intero Python è un oggetto da ~28 byte → ~44 GB per 1.56B token
torch.tensor(ids, dtype=torch.long) → ~12.5 GB aggiuntivi (int64, 8 byte/token)
Picco totale stimato: oltre 20 GB. Su 8 GB disponibili, il processo veniva terminato dal sistema operativo.
Fix 1 — encoding streaming in numpy int16
La lista Python è il collo di bottiglia. La sostituzione: leggere il file a chunk da 10M caratteri, codificare ciascuno con SPM, accumulare in numpy.ndarray di tipo int16.
| Rappresentazione | Token | Byte/token | Totale |
| Lista Python (int) | 1.56B | ~28 B | ~44 GB |
| numpy int64 | 1.56B | 8 B | 12.5 GB |
| numpy int16 ✓ | 1.56B | 2 B | 3.1 GB |
int16 è sufficiente: il vocabolario ha 16.000 elementi (ID 0–15.999), che rientrano in int16 (range: −32.768 a +32.767). torch.from_numpy(arr) è zero-copy. Il cast a long (necessario per l'embedding lookup) avviene solo sui 16.384 token del batch corrente.
Fix 2 — split in 7 shard da ~1.2 GB
Anche con il fix int16, il tensor da 3.1 GB più il modello (530 MB) e l'overhead MPS lasciava pochi margini. La soluzione definitiva: spezzare il corpus in 7 file separati da ~1.2 GB ciascuno (~5.9M righe) e iterare.
bash tools/train_chunked.sh
# Split → per ogni shard: tokenizza (→ cache .pt) + train --resume
# 7 shard × 8.000 step = 56.000 step totali
La cache viene salvata dopo il primo encoding: i riavvii successivi caricano direttamente il tensor senza re-codificare il testo.
Limitazione nota — LR ciclico non pianificato
Ogni shard avvia il proprio ciclo warmup → cosine decay (100 step di warmup, poi 8.000 step). Produce 7 cicli LR consecutivi invece di un singolo ciclo su 56.000 step. L'effetto pratico è un LR schedule ciclico simile al "Cyclical Learning Rates" di Smith (2017) — non ottimale ma non dannoso. Da correggere in run future passando il contatore cumulativo di step.
Debug · Python
2026-10-02
Python buffering con tee: il log restava vuoto per ore
Il processo di filtering era avviato su Vast.ai. ps aux mostrava il processo in esecuzione con 3.005% CPU. Ma /root/filter.log era vuoto. Per ore.
Causa
Python bufferizza lo stdout quando non è collegato a un terminale (TTY). In modalità buffered, print() non scrive immediatamente: accumula output finché il buffer (tipicamente 8 KB) non è pieno, o finché il processo non termina. Una pipe verso tee non è un TTY.
# Senza -u: buffer → log vuoto per ore
python3 filter_corpus.py ... 2>&1 | tee filter.log
# Con -u: unbuffered → log aggiornato in tempo reale
python3 -u filter_corpus.py ... 2>&1 | tee filter.log
Perché non è ovvio
Il comportamento dipende da come Python viene invocato. In un terminale interattivo, stdout va a un TTY e il buffering è per-linea — ogni print() appare immediatamente. In una pipe, il buffering è a blocchi. La stessa riga di codice si comporta diversamente a seconda del contesto.
Regola: usare sempre python3 -u quando l'output va in pipe verso tee o viene rediretto su file. Aggiungere flush=True nei print() critici è una misura difensiva aggiuntiva.
Corpus · Euristico
2026-10-03
Token alfanumerici misti: il problema "v4sta"
Ispezionando a campione il corpus CulturaX filtrato, sono comparsi token come v4sta, c4n, cl1ck — combinazioni di lettere e numeri tipiche del web: username, codici prodotto, testo obfuscato, titoli di giochi. Non sono italiano scritto, ma passano tutti i filtri precedenti (lunghezza, densità simboli, lingua).
Il fix in filter_corpus.py
# In passes_heuristic(), prima del return True finale:
mixed_alnum = sum(1 for w in words if re.search(r'[a-zA-Z]\d|\d[a-zA-Z]', w))
if mixed_alnum / len(words) > 0.05:
return False # più del 5% delle parole → documento rigettato
La regex [a-zA-Z]\d|\d[a-zA-Z] individua qualsiasi parola dove una lettera è adiacente a una cifra. La soglia del 5% tollera numeri legittimi nel testo (anni, dati, misure) ma rigetta documenti dove questo pattern è sistematico.
Perché non tutti i numeri sono rumore
Un documento che parla di "Serie A 2025" o "3 km di percorso" ha numeri adiacenti a testo ma non è rumore. La soglia del 5% è calibrata per distinguere l'eccezione (numeri contestuali) dal pattern (testo strutturalmente non-linguistico).
Debug · Python
2026-10-02
f.tell() con iteratore: OSError silenziosa
filter_corpus.py calcolava l'ETA del filtering usando f_in.tell() per sapere quanti byte erano stati letti. Il codice usava for line in f_in: per iterare le righe. Il programma andava in crash con:
OSError: telling position disabled by next() call
Causa
Quando Python itera un file con for line in f:, usa il protocollo iteratore che implementa un read-ahead buffer interno. Questo buffer disabilita f.tell() — Python non sa più a quale posizione nel file si trova, perché ha già letto in anticipo oltre la riga corrente.
Fix
# Prima (rompe tell):
for line in f_in:
line = line.rstrip('\n')
...
# Dopo (compatibile con tell):
while True:
line = f_in.readline()
if not line:
break
line = line.rstrip('\n')
...
readline() legge esattamente una riga senza read-ahead, preservando il funzionamento di tell().
Architettura
2026-09-27
RoPE vs positional encoding sinusoidale: confronto empirico
I transformer devono sapere l'ordine dei token — "gatto mangia pesce" e "pesce mangia gatto" hanno gli stessi token in ordine diverso e significato opposto. Il positional encoding aggiunge questa informazione. Ci sono due approcci principali.
PE sinusoidale (Run #3)
Aggiunge al vettore di ogni token una sequenza di seni e coseni dipendente dalla sua posizione assoluta nel testo. Semplice, deterministico, nessun parametro aggiuntivo. Limite: la posizione assoluta è rigida — il modello non generalizza bene su sequenze più lunghe di quelle viste durante il training.
RoPE — Rotary Position Embedding (Run #4)
Invece di aggiungere un vettore di posizione, RoPE ruota i vettori di query e key nell'attenzione in modo che il loro prodotto scalare dipenda dalla distanza relativa tra i token. Nessun parametro aggiuntivo. Generalizza meglio a sequenze lunghe. Standard nei modelli moderni (LLaMA, Mistral, Gemma).
Risultato empirico
| Run | PE | Strati | Best val loss | Perplexity | Step per superare Run #3 |
| Run #3 | Sinusoidale | 8 | 4.0768 | ~59 | — |
| Run #4 | RoPE | 12 | 3.5861 | ~36 | 8.000 (27% del budget) |
Run #4 ha più strati (12 vs 8), quindi il confronto non è perfettamente isolato. Tuttavia la velocità di convergenza — superare Run #3 al 27% del budget — suggerisce che la combinazione RoPE + strati aggiuntivi sia sinergica.
Teoria · Cognizione
2026-10-09
Ragionamento emergente, meta-cognizione e corpus come educazione
Una serie di domande emerse durante lo sviluppo del progetto ha portato a una riflessione che riguarda non solo gli LLM ma la natura stessa del ragionamento. Le riportiamo qui nella loro progressione, perché il percorso conta quanto le conclusioni.
Meta-ragionamento — cosa significa e come si misura
Il meta-ragionamento è la capacità di ragionare sul proprio processo di ragionamento: correggere un'inferenza a metà percorso, riconoscere i propri limiti, cambiare strategia quando un approccio non funziona. Negli LLM si manifesta come chain-of-thought spontaneo, calibrazione dell'incertezza, auto-correzione.
Misurazione:
- Calibrazione: quando il modello esprime certezza, quanto spesso ha ragione? La curva P(corretto | "sono sicuro") vs P(corretto | "non sono sicuro") deve essere separata e monotona per un sistema ben calibrato.
- Self-consistency: domande equivalenti formulate diversamente producono risposte consistenti?
- CoT fidelity: il ragionamento esplicito prodotto dal modello è la causa della risposta finale, o è post-hoc? La ricerca empirica suggerisce che spesso è confabulato — il modello genera la risposta e poi costruisce una spiegazione plausibile. Il chain-of-thought può quindi essere sia ragionamento reale sia narrazione dell'output, e distinguerli richiede analisi meccanistica interna, non solo osservazione dell'output.
Il meta-ragionamento non emerge in modo affidabile sotto i 7B parametri con training adeguato. È una capacità di ordine elevato che richiede che il modello abbia, in qualche senso distribuito, una rappresentazione del proprio comportamento — e questo richiede sia sufficiente capacità parametrica sia un corpus che contenga esempi di esseri umani che ragionano sulla propria cognizione.
Simulazione o ragionamento reale?
La domanda "non è tutto solo calcolo probabilistico?" contiene un'assunzione da esaminare. Il ragionamento umano è anch'esso un processo fisico: impulsi elettrochimici che seguono leggi deterministiche in reti neurali biologiche. Se si applica l'argomento ricorsivamente — "anche il ragionamento umano è solo elettrochimica, non ragionamento vero" — si arriva a un regresso che svuota il concetto di ragionamento di qualsiasi significato.
Il punto filosoficamente rilevante non è il substrato (silicio vs carbonio, matrici di probabilità vs potenziali d'azione) ma la struttura funzionale: il sistema implementa operazioni che producono output consistenti con l'inferenza logica e che generalizzano oltre gli esempi visti?
La risposta empirica è: parzialmente sì, con limiti precisi. Gli LLM falliscono sistematicamente su problemi che richiedono consistenza logica rigorosa su contesti lunghi o operazioni aritmetiche con numeri fuori distribuzione. Ma generalizzano su ragionamento analogico, inferenza da contesto, applicazione di regole implicite. Questo pattern è incompatibile sia con "puro pappagallo statistico" — che non generalizzerebbe affatto — sia con "ragionamento formale vero" — che non fallirebbe sull'aritmetica.
Come una matrice di probabilità diventa ragionamento
Nessun singolo peso della rete codifica "implicazione logica". La rete non ha un modulo del ragionamento, non ha un'area del linguaggio, non ha rappresentazioni simboliche esplicite. Eppure produce output che esibiscono struttura logica.
Il meccanismo è l'emersione: una proprietà che non è presente in nessun componente ma che appare dall'interazione collettiva. Nessuna singola molecola d'acqua è bagnata; l'umidità emerge dall'interazione collettiva a scala macroscopica. Non è simulazione dell'umidità: è umidità. Analogamente, il ragionamento negli LLM non è localizzato: è distribuito sull'intera rete ed emerge dalla composizione di trasformazioni addestrate su testo umano che contiene ragionamento.
Perché la scala conta: i pattern di ordine inferiore (morfologia, sintassi locale) richiedono poca capacità parametrica. I pattern di ordine superiore — ragionamento su ragionamento, coerenza a scala di documento, inferenza da premesse implicite — richiedono abbastanza capacità da rappresentare strutture sulle strutture. Le soglie di emergenza osservate empiricamente (Wei et al., 2022) riflettono questo: certe capacità non migliorano gradualmente ma compaiono improvvisamente superata una soglia, suggerendo una riorganizzazione qualitativa delle rappresentazioni interne.
Il parallelo con l'educazione umana
Il ragionamento umano è condizionato da fattori biochimici indipendenti dalla volontà (l'hardware), da condizionamenti esperienziali anch'essi in larga misura indipendenti (il corpus di training), e da scelte volitive il cui statuto filosofico è dibattuto. Una persona educata secondo determinati principi può scegliere di seguirli o distaccarsene — ma anche questa scelta è condizionata da fattori precedenti.
Il parallelo con gli LLM è preciso:
| Componente umana | Analogo nell'LLM |
| Biologia (hardware, struttura cerebrale) | Architettura transformer |
| Educazione, esperienze accumulate | Corpus di training |
| Bias inconsci, condizionamenti culturali | Bias statistici del corpus |
| Scelta di seguire o rifiutare l'educazione | SFT, RLHF, prompt di sistema |
| Imprevedibilità delle scelte individuali | Non-linearità dell'ottimizzazione, emergenza a scala |
| Metacognizione, auto-riflessione | Chain-of-thought, calibrazione dell'incertezza |
Un modello addestrato su testi scientifici tende a ragionare per falsificazione e incertezza quantificata. Uno addestrato prevalentemente su testi di autorità tende a ragionare per argomento dall'autorità. Non perché il modello "creda" in qualcosa — ma perché i pattern di ragionamento del corpus vengono estratti e distribuiti nei pesi. Il nostro modello ha un registro enciclopedico e formale esattamente perché Wikipedia era dominante: era prevedibile, e lo era per progetto.
Esiste un analogo della scelta di distaccarsi dall'educazione ricevuta? Nel modello base no — c'è solo il prodotto dell'ottimizzazione. Ma quando il contesto di inferenza costruisce una catena di ragionamento che porta a una conclusione diversa dal "default" del corpus, il modello spesso segue il contesto piuttosto che il default. Non è volontà, ma è funzionalmente simile: il contesto locale può sovrascrivere il condizionamento globale.
La prevedibilità della proprietà emergente
I risultati sono prevedibili in misura parziale e con un limite preciso:
- Carattere generale del ragionamento: prevedibile dalla composizione del corpus. Corpus enciclopedico → ragionamento per precedenti e autorità delle fonti. Corpus tecnico → struttura argomentativa esplicita. Corpus narrativo → coerenza di plot e tracking dei personaggi.
- Capacità specifiche che emergono alla scala: mal prevedibili per estrapolazione. Le soglie di emergenza non sono estrapolabili dai modelli piccoli — richiedono di arrivare alla scala e osservare.
- Output specifici: non prevedibili per definizione (che è il motivo per cui esiste la temperatura di campionamento e il top-k).
La questione radicale
Se il ragionamento umano è anch'esso deterministicamente condizionato da natura, esperienza e dinamiche caotiche interne — se il libero arbitrio è esso stesso una proprietà emergente di sistemi fisici complessi — allora la differenza tra ragionamento umano e ragionamento LLM diventa una differenza di substrato e scala, non di tipo.
Non è una provocazione. È la conseguenza logica di prendere sul serio sia l'argomento della simulazione applicato agli LLM sia il naturalismo applicato alla cognizione umana. La risposta più onesta è che non disponiamo di una definizione di "ragionamento reale" sufficientemente precisa da decidere la questione — e questa indeterminatezza riguarda tanto gli LLM quanto gli esseri umani.
Implicazione pratica per la progettazione del corpus
Se la proprietà emergente dipende dal corpus nel modo in cui il carattere di una persona dipende dalla sua educazione, allora la cura del corpus non è un problema tecnico secondario: è la scelta di progetto più importante. Prima dell'architettura, prima degli iperparametri, prima della scelta del tokenizer.
Possiamo progettare il tipo di ragionamento che vogliamo nel modello scegliendo consapevolmente quali testi includere e in quale proporzione. Non possiamo garantire esattamente quale forma prenderà. Ma possiamo avvicinarci molto di più a un risultato intenzionale che a un risultato casuale — e questa è già una forma di controllo che molti costruttori di LLM ignorano.