Dopo la tokenizzazione, ogni ID viene trasformato in un vettore di numeri: una posizione in uno spazio ad altissima dimensionalità, dove il modello comincia a rappresentare somiglianze e relazioni.
Nell’articolo sulla tokenizzazione, abbiamo visto che il testo si trasforma in una sequenza di interi: i token ID. Questi numeri entrano nel modello, e da quel momento in poi, il modello li elabora come numeri. Non sono più parole fino alla fine di tutto il percorso, quando si genera la risposta.
Qui arriviamo a una domanda cruciale: un intero è proprio un numero, non un significato. Il token ID 1239 non è diverso dal token ID 1240 dal punto di vista puramente numerico — la distanza tra loro è esattamente 1. Semanticamente, però, potrebbero essere parole completamente non correlate. Un token potrebbe essere "gatto", l'altro "algebra". La distanza numerica tra i loro ID non dice nulla sulla loro relazione linguistica.
Eppure il modello funziona. Quando legge una sequenza di token, non produce numeri casuali, produce testo coerente, semanticamente sensato. Questo significa che il modello ha imparato a creare struttura da questi interi che apparentemente non hanno una grande relazione tra loro.
[1239, 567, 234, 8901, ...]In questo articolo scopriamo come.
Un numero non è un significato
Nell’articolo iniziale della serie, sulla struttura dei modelli (pesi, tensori e quantizzazione), abbiamo visto che il modello salva i suoi pesi in matrici. Ogni peso è un numero reale, e ogni blocco transformer elabora vettori di 4.096 numeri reali. Ma l'ingresso è una sequenza di interi (ID token), non vettori di numeri reali.
È facile notare che c'è qualcosa che non torna. Come fa il modello a trasformare un solo numero intero in una rappresentazione che possa essere elaborata da tutte queste moltiplicazioni matriciali?
La risposta è semplice ma fondamentale: una semplice ricerca su una matrice pre-addestrata, quello che i programmatori chiamano lookup.
Immagina una stanza dove ogni oggetto simile è messo nello stesso angolo. Non fisicamente simile ma semanticamente simile. Tutti i nomi di animali sono in un'area, tutti i verbi di movimento in un'altra, tutti i numeri in un'altra ancora. Se conosci dove sono messi gli oggetti, puoi navigare la stanza: cose simili sono vicine, cose diverse sono lontane.
Questa stanza ha 4.096 dimensioni ed è chiamata spazio degli embedding.
L'anticipazione: da ID a vettore
Ecco quello che accadrà in questo articolo:
- Prima parte - embedding: Vedremo come il modello usa una matrice gigante (528 milioni di parametri) per trasformare ogni token ID in un vettore di 4.096 numeri. È un'operazione banalmente semplice: prendi l'ID, usalo come indice di riga, estrai la riga.
- Seconda parte - prossimità: Ma una volta che hai questi vettori, scopri che le loro posizioni relative nello spazio codificano un significato. Due token con vettori che puntano in direzioni simili sono semanticamente simili. Non per magia ma perché sono state calcolate così.
- Terza parte - quantificazione: Imparerai la formula che misura questa prossimità: la cosine similarity. Questo trasformerà la confusione sulla distanza tra i vettori in un numero semplice tra −1 e 1, ovvero da per niente simile a totalmente simile.
- Quarta parte: Verificherai tutto questo con un esperimento pratico. Scaricherai un modello di embedding, che calcolerà le similarità tra parole reali, e vedrai che i numeri confermano l'intuizione: "gatto" e "felino" sono vicini, "gatto" e "algebra" sono lontani.
- Quinta e sesta parte: la co-occorrenza: Scoprirai perché i vettori hanno questa proprietà e perché, durante il pre-training, il modello è stato ottimizzato per imparare una lezione semplice ma fondamentale: parole che appaiono nello stesso contesto hanno significati correlati. E infine — ed è importante — scoprirai i limiti di questa lezione. Gli embedding catturano correlazione, non causalità. "Ape" e "regina" sono vicini nello spazio, ma il modello non sa perché sono vicini. Non conosce l'entomologia.
Il layer di embedding: da ID a vettore
Nel momento esatto in cui il testo viene tokenizzato, il modello riceve una sequenza di interi:
Questi numeri, ovvero gli ID dei token, sono indici. Rappresentano posizioni in un elenco.
Il modello non può elaborare questi interi così come sono. Il modello è fatto da matrici e da moltiplicazioni matriciali. Elabora vettori di numeri reali, in questo caso sequenze di 4.096 numeri e non singoli numeri interi.
C'è un divario tra l'input (che è una sequenza di interi) e quello che il modello sa fare (moltiplicare matrici di vettori).
Questo divario viene colmato dal layer di embedding.
La matrice di embedding
Il layer di embedding è estremamente semplice. È solo una matrice gigante.
Immagina una tabella con 128.256 righe e 4.096 colonne. 128.256 abbiamo detto è il numero di token nel vocabolario. Ogni riga quindi corrisponde a un token. Ogni colonna rappresenta una "dimensione" dello spazio semantico del modello. Ecco un'illustrazione schematica (ovviamente ridotta — la vera matrice è molto più grande):
Dim1 Dim2 Dim3 ... Dim4096
┌─────────────────────────────────────┐
Token 0 │ 0.234 -0.567 0.891 ... -0.123 │
Token 1 │ 0.456 0.789 -0.234 ... 0.456 │
Token 2 │-0.123 0.012 0.567 ... 0.789 │
... │ ... ... ... ... ... │
Token 128255 │ 0.890 -0.345 0.123 ... 0.234 │
└─────────────────────────────────────┘
Ogni cella è un numero reale (positivo o negativo, tipicamente tra −1 e 1). Questa matrice si chiama token_embd.weight nel codice del modello.
Contiene: 128.256 × 4.096 = 525.336.576 parametri. Circa 525 milioni di numeri. Occupa il 6,5% di tutto lo spazio dei parametri del modello.
L'operazione di lookup
L'operazione che usa questa matrice è banalmente semplice. Si chiama lookup e funziona così:
- Ricevi un token ID (ad esempio, 1239)
- Usalo come indice di riga
- Estrai l'intera riga dalla matrice
- Quella riga è il tuo embedding
In pseudocodice:
embedding = embedding_matrix[token_id]
Avevamo già smesso di usare il testo con la tokenizzazione per ottenere un indice del token, ora smettiamo di usare l'indice del token e lavoriamo con un vettore di 4.096 numeri reali. Questo vettore può essere moltiplicato per le matrici del modello e può essere sommato ad altri vettori. Può essere trasformato. Insomma, è finalmente un'informazione nel vero linguaggio parlato dal modello che è un linguaggio numerico matriciale.
Come questi pesi imparano il significato
Una domanda cruciale: da dove vengono questi numeri nella matrice di embedding? Non appaiono dal nulla. Vengono appresi durante la fase di pre-training.
Nell’articolo iniziale, sulla struttura dei modelli (pesi, tensori e quantizzazione), abbiamo visto che il modello viene addestrato per predire il token successivo data una sequenza di token. Durante questo addestramento, tutti i pesi del modello — inclusa la matrice di embedding — vengono aggiornati iterativamente.
All'inizio del processo di pretraining, la matrice di embedding viene inizializzata con numeri casuali prima dell'esecuzione dell'algoritmo di addestramento. Tutte le righe sono essenzialmente rumore. È come se tutti i concetti fossero buttati alla rinfusa nel paniere della matrice dell'embedding (tranne che non è vero che ad una linea corrisponde necessariamente un concetto - potrebbe essere vero, ma come abbiamo detto un token potrebbe anche essere una sezione parziale di una parola, un segno d'interpunzione o elementi non particolarmente specifici del testo).
Ma man mano che il modello elabora miliardi di testi e viene corretto dai suoi errori di predizione, la matrice si trasforma.
Il modello impara gradualmente a posizionare i token nel suo spazio di manovra in un modo che riflette il loro significato.
Se il token "gatto" e il token "felino" appaiono spesso nello stesso contesto, il modello scopre che i loro embedding — le loro righe nella matrice — dovrebbero essere simili. Perché così, quando il modello elabora una frase che contiene "gatto", l'embedding di "gatto" è simile a quello di "felino", e il modello può usare quella similarità per predire meglio il prossimo token.
Immagina che l'algoritmo scopra due frasi:
Mi piace accarezzare il mio gatto.
Mi piace accarezzare il mio felino.
Si rende conto che "gatto" e "felino" sono intercambiabili rispetto al "piace accarezzare" e "mio", e li avvicinerà. Ci saranno altri contesti in cui gatto e felino sono vicini, e così progressivamente i numeri dei vettori si muoveranno in modo da avvicinarsi tra loro. Non saranno perfettamente congruenti, perché gatto e felino saranno certamente usati in altri contesti, ma non saranno neppure troppo distanti. Il gioco sta nel fatto che nella predizione del prossimo token laddove potrebbe esserci gatto allora potrebbe anche esserci felino.
Invece, il token "gatto" e il token "algebra" appaiono raramente insieme, quindi possiamo esser sicuri che i loro embedding divergano. Diventano diversi perché non c'è vantaggio nel predire il prossimo token se li tratta come simili.
Questo processo — aggiustare i valori della matrice di embedding affinché riflettano il significato del token — accade per tutte le 525 milioni di righe contemporaneamente, con miliardi e miliardi di esempi.
Alla fine del pre-training, la matrice di embedding non è più rumore. È una rappresentazione appresa della struttura semantica della lingua.
Se però uno potesse parlare con la matrice stessa e chiederle di esporre quale riga è quella di "gatto" o quella di "topo", la matrice non saprebbe nulla di "gatto" e di "topo" in quanto tali, ma solo che i vettori che li rappresentano (che potrebbero essere uno solo per "topo" e ad esempio due per "g-atto") si allineano tra di loro.
La scalabilità: quanto è grande un embedding?
Una curiosità naturale: perché un token viene rappresentato in 4.096 dimensioni? Perché non 100, o 10.000?
La risposta è un compromesso tra espressività e efficienza computazionale.
- Con 100 dimensioni: Un embedding sarebbe troppo compresso. Il modello avrebbe meno "spazio" per codificare sfumature semantiche. Due token che dovrebbero essere leggermente diversi potrebbero essere forzati a essere simili. È come comprimere un'immagine in bassa risoluzione dove si perdono dettagli significativi.
- Con 4.096 dimensioni: Abbastanza spazio per codificare sfumature complesse. Il modello può rappresentare "rosso" e "cremisi" come diversi, anche se entrambi descrivono colori. Ma non è eccessivo — ogni dimensione aggiuntiva aumenta il costo computazionale di tutte le moltiplicazioni matriciali successive.
- Con 10.000 dimensioni: Troppo. Il costo computazionale diventerebbe proibitivo. 4.096 è un adeguato compromesso — abbastanza grande per espressività, abbastanza piccolo per efficienza.Queste dimensioni non catturano caratteristiche semantiche del "concetto", ad esempio per "gatto" non c'è una dimensione che rappresenta il fatto di avere i lunghi baffi, o la coda, o fare le fusa. Non c'è "significato" nelle dimensioni, almeno non nel senso in cui lo intenderebbe un linguista o un filosofo. C'è significato solo nella misura in cui "gatto" sarà diverso da "getto", e sarà legato comunque a "topo" e eventualmente avrà qualcosa in comune con "Tom" e "Jerry" (qualunque sia il modo con cui questi nomi saranno rappresentati come token).
Un dettaglio importante: non è l'unica trasformazione
Un'osservazione finale: il layer di embedding è la trasformazione da token ID a vettore. Ma non è l'unica trasformazione che il token subisce prima di entrare nel primo blocco transformer.
Alcuni modelli aggiungono altri segnali al vettore di embedding:
- Positional encoding: Un segnale che dice "tu sei il token numero 0 della sequenza, tu sei il token numero 1", ecc. Serve al modello per sapere in quale ordine i token appaiono.
- Token type embeddings (esiste solo in alcuni modelli): È un segnale aggiuntivo che dice "tu sei parte di una domanda, tu sei parte di una risposta", per esempio.
Ma per questo articolo, il focus è solo sull'embedding: come si trasforma un intero in un vettore che il modello può elaborare.
Nel prossimo articolo, scoprirai come questi vettori di embedding vengono elaborati dai blocchi transformer per produrre la prossima generazione di vettori, e così via fino all'output finale.
Lo spazio vettoriale: vicinanza geometrica = vicinanza semantica
Ora che abbiamo il vettore di embedding per ogni token, arriviamo al punto cruciale: come la geometria dello spazio codifica il significato.
Abbiamo detto che un embedding non è una lista di etichette.
Questo è un punto importante da sottolineare.
Da bambini ci facevano fare un gioco che aveva qualcosa in comune con il modo in cui Aristotele immaginò il mondo.
Un bambino pensava a qualcosa e poi in venti domande gli altri avrebbero dovuto scoprire cosa fosse, iniziando dalla classica prima domanda: Animale, vegetale o minerale? Dopodiché si iniziava a seguire una specifica tassonomia (esempio, se era un animale quante zampe avesse ecc.). Incasellate queste 20 dimensioni si provava a dare una risposta.
Ecco, nel caso del vettore di embedding non funziona per niente in questo modo. Un numero all'interno del vettore non è una specifica proprietà di quel token. Il vettore nel suo complesso non è un insieme di proprietà tipo "questo token è un animale, è a 4 zampe, è domestico".
Un vettore rappresenta semplicemente una posizione nello spazio quindi una sequenza di 4.096 numeri. Niente di più. Niente di meno.
Immagina una stanza con dei bambini disposti a caso. Possiamo identificarli con un vettore a due posizioni x e y, la distanza da un spigolo della stanza. Due bambini vicini avranno x e y vicini. Il che non ci dirà nulla sul fatto che possano avere capelli biondi o occhi azzurri, essere alti o maschi.
Come nel gioco dei bambini, nel caso del vettore di embedding dopo il pretraining, la sequenza ha una proprietà sorprendente: la posizione relativa di due vettori nello spazio corrisponde alla loro similarità semantica.
Visualizzazione in 2D: comprendere l'angolo
Come abbiamo visto in due dimensioni è tutto più facile da "vedere". I bambini/concetti si avvicinano, materialmente, in uno spazio che ci è facile capire.
Immagina ora di ridurre il problema da 4.096 dimensioni a sole 2 dimensioni.
Traccia un grafico con due assi: orizzontale e verticale. Ogni punto del grafico è un vettore — una sequenza di 2 numeri che rappresentano le coordinate x e y.
Ecco alcuni token come punti nello spazio 2D:
Guarda il diagramma: "re" e "regina" hanno vettori che puntano in direzioni molto simili. L'angolo tra loro è piccolo.
Invece "regina" e "algebra" hanno vettori che puntano in direzioni molto diverse. L'angolo tra loro è grande.
Qui è un po' diverso rispetto alla semplice vicinanza spaziale dei bambini, esiste -per così dire- una dimensione in più. C'è la posizione della punta del vettore ma anche l'orientamento. Quindi non conta solo se re e regina siano spazialmente vicini, ma se "puntino nella stessa direzione" e questo è calcolato dall'angolo tra i vettori. Questo angolo è la metrica. Un angolo piccolo significa "simili semanticamente". Un angolo grande significa "diversi semanticamente".
Questi angoli non sono scelti dei progettisti, non sono arbitrari. Emergono dalle strutture geometriche dello spazio che abbiamo scelto e dagli input dell'addestramento.
La proiezione mentale a 4.096 dimensioni
Ora il salto difficile: generalizzare da 2 dimensioni a 4.096 dimensioni.
Non puoi visualizzarlo. Il tuo cervello non può immaginare uno spazio a 4.096 dimensioni. È una limitazione fisica del cervello umano. Siamo costruiti per vedere il mondo in 3 dimensioni.
Ma matematicamente, funziona esattamente allo stesso modo.
Immagina che ogni token sia rappresentato da 4.096 numeri — ogni numero è una "coordinata" in una dimensione diversa. Due token sono semanticamente simili se i loro vettori puntano approssimativamente nella stessa direzione nello spazio 4.096-dimensionale.
L'angolo tra i vettori è la misura della loro somiglianza. Piccolo angolo = simili. Grande angolo = diversi.
Tutto il resto è lo stesso della visualizzazione 2D. La geometria non cambia.
Esistono tecniche matematiche, come t-SNE e UMAP, che riducono lo spazio da tante dimensioni a 2D preservando le distanze relative e così è possibile facilmente trovare cluster di token simili quando si visualizzano gli embedding.
Esempi concreti
Ecco alcuni esempi di cosa significa essere "vicini" nello spazio semantico di un modello:
- "gatto" ↔ "felino": Molto vicini. Appaiono spesso nelle stesse frasi. I loro embedding puntano quasi nella stessa direzione.
- "ape" ↔ "miele": Vicini, ma non quanto "gatto" e "felino". Co-occorrono frequentemente, ma non sono sinonimi. L'angolo è moderato.
- "ape" ↔ "regina": Ancora vicini, ma meno di "ape" e "miele". Appaiono insieme nel contesto degli alveari, ma la correlazione è più debole.
- "rosso" ↔ "cremisi": Molto vicini. Entrambi descrivono colori simili. Appaiono in contesti simili.
- "caldo" ↔ "freddo": Sorprendentemente vicini, ma con una nota importante. Non perché siano sinonimi, ma perché spesso appaiono insieme — "caldo vs freddo", "il caldo e il freddo". Sono opposti correlati.
- "ape" ↔ "algebra": Lontani. Appaiono raramente nella stessa frase. I loro embedding puntano in direzioni quasi ortogonali.
Una proprietà importante: non è solo "similarità"
Un dettaglio sottile: l'angolo tra due vettori non misura una nozione generica di "similarità". Misura co-occorrenza statistica nel corpus di addestramento.
"Caldo" e "freddo" hanno un angolo piccolo, ma non sono sinonimi anzi sono quasi opposti in significato. Però appaiono frequentemente nello stesso contesto ("il tempo è caldo o freddo", "contrasto tra caldo e freddo"), quindi il modello li ha avvicinati.
Questo è importante per capire i limiti del modello, che affronteremo più avanti. Ma per ora, la conclusione è semplice:
In uno spazio geometrico a 4.096 dimensioni, l'angolo tra due embedding rappresenta quanto frequentemente i corrispondenti token appaiono nello stesso contesto durante il pre-training.
E poiché le parole che appaiono nello stesso contesto spesso hanno significati correlati, questa metrica geometrica funziona sorprendentemente bene come misura di somiglianza semantica.
Nel prossimo paragrafo, scoprirai come quantificare precisamente quest'angolo con una formula semplice e potente: la cosine similarity.
Cosine similarity: la formula spiegata
Fino a ora, abbiamo parlato di "angolo tra vettori" in modo abbastanza vago. Ma come si misura davvero un angolo tra due vettori di 4.096 numeri?
La risposta è la cosine similarity: una formula che trasforma l'idea astratta di "angolo" in un numero concreto e interpretabile.
La formula
Ecco la formula completa:
$$ \cos(a, b) = \frac{a \cdot b}{|a| \cdot |b|} $$
dove:
- $a \cdot b = a_1b_1 + a_2b_2 + a_3b_3 + \cdots + a_{4096}b_{4096}$ (prodotto scalare)
- $|a| = \sqrt{a_1^2 + a_2^2 + a_3^2 + \cdots + a_{4096}^2}$ (norma di a)
- $|b| = \sqrt{b_1^2 + b_2^2 + b_3^2 + \cdots + b_{4096}^2}$ (norma di b)
Non lasciarti intimidire dai simboli. La formula è composta da due parti concettuali semplici. Vediamo pezzo per pezzo.
Numeratore: il prodotto scalare
Il numeratore è a · b, il prodotto scalare (dot product) di due vettori.
Cosa significa? Moltiplica ogni numero di $a$ per il numero corrispondente di $b$, poi somma tutti i risultati.
Esempio concreto con vettori piccoli:
$a = [0.5, 0.2, -0.3]$
$b = [0.8, 0.1, 0.4]$
$$a \cdot b = (0.5 \times 0.8) + (0.2 \times 0.1) + (-0.3 \times 0.4) = 0.4 + 0.02 + (-0.12) = 0.3$$
Cosa rappresenta il prodotto scalare? La correlazione coordinata per coordinata tra i due vettori.
Se due vettori puntano nella stessa direzione (angolo piccolo), il loro dot product è grande e positivo. Se puntano in direzioni opposte (angolo grande), è negativo. Se sono ortogonali (angolo 90 gradi), è zero.
Il problema: il dot product dipende dalla norma dei vettori. Due vettori che puntano nella stessa direzione ma uno è il doppio dell'altro hanno dot product diverso. Vogliamo una misura che sia indipendente dalla norma. Ecco perché normalizzare.
Denominatore: la normalizzazione
Il denominatore è $|a| \cdot |b|$, il prodotto delle norme di $a$ e $b$.
La norma di un vettore è:
$ |a| = \sqrt{ a_1^2 + a_2^2 + a_3^2 + \cdots + a_{4096}^2 } $
Cosa rappresenta? La misura della "grandezza" del vettore nello spazio (in matematica si chiama norma euclidea).
Dividendo il prodotto scalare per il prodotto delle norme, normalizziamo il risultato. Ora la formula è indipendente dalla norma dei vettori. Due vettori che puntano nella stessa direzione avranno lo stesso cosine similarity, anche se uno è il doppio dell'altro.
Mettere tutto insieme
La formula completa è:
$$\cos(a, b) = \frac{\text{correlazione coordinata per coordinata}}{\text{norma euclidea}}$$
Cosa significa intuitivamente?
"Quanto correlati sono questi vettori (numeratore), normalizzato per la loro norma (denominatore)?"
Se sono perfettamente correlati (puntano esattamente nella stessa direzione), il numeratore e il denominatore sono uguali, e il risultato è 1.
Se sono perfettamente anticorrelati (puntano in direzioni opposte), il risultato è -1.
Se sono ortogonali (nessuna correlazione), il risultato è 0.
L'intervallo: da −1 a 1
La cosine similarity produce sempre un numero tra −1 e 1. Ecco cosa significa:
- cos(a, b) = 1: Vettori identici. Puntano esattamente nella stessa direzione. Angolo = 0 gradi.
- cos(a, b) ≈ 0.7-1.0: Molto simili. Appaiono frequentemente nello stesso contesto. Angolo ≈ 0-45 gradi.
- cos(a, b) ≈ 0.4-0.7: Moderatamente simili. Co-occorrono, ma non sono sinonimi. Angolo ≈ 45-65 gradi.
- cos(a, b) ≈ 0.0-0.4: Leggermente simili o indipendenti. Poca correlazione. Angolo ≈ 65-90 gradi.
- cos(a, b) ≈ -0.3-0.0: Leggermente opposti. Appaiono in contesti un po' diversi. Angolo ≈ 90-110 gradi.
- cos(a, b) ≈ -0.7 a -0.3: Opposti. Appaiono in contesti molto diversi. Angolo ≈ 110-135 gradi.
- cos(a, b) = -1: Completamente opposti. Puntano esattamente in direzioni opposte. Angolo = 180 gradi. (Raro negli embedding linguistici.)
Negli embedding linguistici, valori negativi sono rari. Le parole tendono a non avere vettori completamente opposti. Il modello non apprende relazioni di opposizione forte — apprende relazioni di co-occorrenza. Quindi vedrai principalmente valori tra 0 e 1.
Perché cosine similarity, non distanza euclidea?
Una domanda naturale: perché usare cosine similarity al posto della semplice distanza euclidea (la distanza linea retta tra due punti)?
La risposta è l'invarianza alla scala. Negli embedding linguistici, la norma dei vettori non si mantiene sotto controllo. Alcuni token hanno vettori con norma grande, altri con norma piccola. Se usi distanza euclidea, due token con vettori che puntano nella stessa direzione ma norme diverse risulterebbero distanti, il che non è quello che vuoi.
La cosine similarity normalizza. Due vettori che puntano nella stessa direzione hanno cosine similarity 1, indipendentemente dalla loro norma.
Questo rende la metrica più stabile e interpretabile negli embedding linguistici.
Calcoliamo un esempio
Proviamo con un esempio reale (semplificato).
Immagina due token con embedding di 3 numeri (invece di 4.096):
$\text{"gatto"} = [0.8, 0.5, 0.2]$
$\text{"felino"} = [0.7, 0.6, 0.1]$
Calcolo del cosine similarity:
Numeratore (dot product):
$$(0.8 \times 0.7) + (0.5 \times 0.6) + (0.2 \times 0.1) = 0.56 + 0.30 + 0.02 = 0.88$$
Denominatore (norme):
$|\text{"gatto"}| = \sqrt{0.8^2 + 0.5^2 + 0.2^2} = \sqrt{0.64 + 0.25 + 0.04} = \sqrt{0.93} \approx 0.964$
$|\text{"felino"}| = \sqrt{0.7^2 + 0.6^2 + 0.1^2} = \sqrt{0.49 + 0.36 + 0.01} = \sqrt{0.86} \approx 0.927$
Cosine Similarity:
$$\cos(\text{"gatto"}, \text{"felino"}) = \frac{0.88}{0.964 \times 0.927} = \frac{0.88}{0.893} \approx 0.985$$
Risultato: 0.985. Molto alto. Cosa significa? I due vettori puntano quasi nella stessa direzione — "gatto" e "felino" sono molto simili secondo il modello.
Questo ha senso. Nel corpus di addestramento, "gatto" e "felino" appaiono frequentemente in contesti simili.
Nel prossimo paragrafo, verificherai questa formula con dati reali calcolando cosine similarity tra parole vere usando un modello di embedding.
Esperimento pratico: misurare le similarità
Fino a ora tutto è stato teoria e formule. Ma la cosine similarity non è un'astrazione accademica: è un numero che puoi calcolare veramente, con dati reali, con il tuo computer.
In questa sezione verifichiamo tutto quello che abbiamo imparato calcolando effettivamente le similarità tra parole reali.
Cosa ti serve: nomic-embed-text
Per questo esperimento usiamo un modello di embedding specializzato chiamato nomic-embed-text. Non è proprio il modello all'interno di Llama, è diverso: più piccolo, ottimizzato specificamente per calcolare embedding di testi.
Perché nomic-embed-text?
- Riproducibile: Gira localmente sul tuo computer, senza dipendenze cloud. Puoi eseguire il codice oggi, domani, tra un anno, e ottenere sempre gli stessi risultati.
- Accessibile: È disponibile via Ollama, uno strumento semplice che scarica il modello e lo esegue localmente.
- Trasparente: Non è una scatola nera — puoi vedere esattamente cosa produce.
L'unico prerequisito: Ollama installato e il modello nomic-embed-text scaricato. Se non ce l'hai, le istruzioni sono su https://ollama.ai.
Il codice: una versione semplificata
Ecco il codice Python per calcolare gli embedding e la cosine similarity:
import requests
import numpy as np
# Connessione a Ollama in locale
def get_embedding(text: str) -> np.ndarray:
"""Scarica l'embedding di un testo da Ollama."""
response = requests.post(
"http://localhost:11434/api/embeddings",
json={"model": "nomic-embed-text", "prompt": text}
)
return np.array(response.json()["embedding"])
def cosine_similarity(a: np.ndarray, b: np.ndarray) -> float:
"""Calcola la cosine similarity tra due vettori."""
dot_product = np.dot(a, b)
norm_a = np.linalg.norm(a)
norm_b = np.linalg.norm(b)
return dot_product / (norm_a * norm_b)
# Coppie di parole da confrontare
pairs = [
("neurone", "rete neurale"),
("ape", "regina"),
("ape", "miele"),
("ape", "algebra"),
("gatto", "felino"),
("caldo", "freddo"),
]
# Calcola e stampa le similarità
for word1, word2 in pairs:
embedding1 = get_embedding(word1)
embedding2 = get_embedding(word2)
similarity = cosine_similarity(embedding1, embedding2)
print(f"{word1:20s} ↔ {word2:20s} cos = {similarity:.3f}")
Questo codice è volutamente semplice. Cosa fa?
get_embedding(): Chiama l'API locale di Ollama, invia il testo, riceve un array di 768 numeri (l'embedding per quel testo).cosine_similarity(): Implementa la formula che abbiamo imparato nella sezione precedente. Calcola il dot product, le norme, e il rapporto.- Ciclo: Per ogni coppia di parole, calcola l'embedding di entrambe, poi la loro cosine similarity.
Tutto qui. Niente di magico.
I risultati reali
Ecco i dati ottenuti eseguendo il codice sul portatile con Ollama 0.5 e nomic-embed-text v1.5:
| Coppia | Cosine similarity | Interpretazione |
|---|---|---|
| neurone ↔ rete neurale | 0.625 | Alta correlazione: stesso dominio |
| caldo ↔ freddo | 0.553 | Correlazione moderata: antonimi |
| gatto ↔ felino | 0.487 | Correlazione moderata: iperonimo |
| ape ↔ regina | 0.468 | Correlazione moderata: contesto condiviso |
| ape ↔ algebra | 0.456 | Correlazione bassa: domini distanti |
| ape ↔ miele | 0.409 | Correlazione bassa: co-occorrenza |
Cosa notiamo?
neurone ↔ rete neurale (0.625): il valore più alto della serie. Entrambi i termini appartengono allo stesso dominio tecnico e co-occorrono frequentemente nei testi scientifici. L'embedding li colloca nella stessa regione dello spazio vettoriale.
caldo ↔ freddo (0.553): risultato controintuitivo. Due termini quasi opposti in significato ottengono il secondo punteggio più alto. La spiegazione è che la similarità coseno misura
co-occorrenza contestuale, non opposizione semantica. "Caldo" e "freddo" appaiono spesso nella stessa frase ("il tempo è caldo o freddo", "contrasto tra caldo e freddo"), e il modello cattura questa prossimità testuale, non la relazione antonimica.
gatto ↔ felino (0.487): correlazione moderata che riflette la relazione gerarchica tra termine specifico e iperonimo. Il valore inferiore rispetto a "caldo ↔ freddo" suggerisce che, nel corpus di addestramento di nomic-embed-text, la co-occorrenza degli antonimi termici è più frequente di quella tra un animale e la sua categoria tassonomica.
ape ↔ regina (0.468): i due termini condividono un contesto settoriale (apicoltura), ma "regina" è polisemico: designa anche la figura monarchica umana e il pezzo degli scacchi. La polisemia diluisce la similarità rispetto a coppie monosemantiche.
ape ↔ algebra (0.456): nonostante i domini appaiano distanti, il valore non è trascurabile. Questo riflette probabilmente la presenza di entrambi i termini in contesti didattici generici (manuali scolastici, enciclopedie), più che una relazione semantica diretta.
ape ↔ miele (0.409): il valore più basso, e il meno atteso. "Ape" e "miele" hanno una relazione causale diretta, ma evidentemente appaiono meno frequentemente nella stessa frase rispetto, ad esempio, agli antonimi "caldo" e "freddo". La similarità coseno non cattura relazioni causali o funzionali in quanto tali: cattura distribuzione testuale.
Interpretazione: cosa significano questi numeri?
I valori confermano che il modello non codifica relazioni semantiche astratte (sinonimia, antonimia, causalità), ma strutture di co-occorrenza. L'ordine della classifica è determinato da quanto spesso le parole appaiono negli stessi contesti testuali nel corpus di addestramento, non da quanto siano concettualmente vicine per un parlante umano.
Un punto da segnalare: il range dei valori è compresso tra 0.409 e 0.625. nomic-embed-text v1.5 normalizza gli embedding in modo che le similarità tra termini comuni raramente scendano sotto 0.3-0.4, anche per coppie semanticamente distanti. Confrontare valori assoluti tra modelli diversi è quindi fuorviante: conta l'ordinamento relativo all'interno dello stesso modello.
Robustezza: quando i numeri cambiano
Quanto sono stabili questi valori tra esecuzioni successive? In generale, la variabilità è nell'ordine di ±0.01-0.02, dovuta a piccoli artefatti numerici nel calcolo (precisione floating-point, ordine delle operazioni). Un esempio concreto:
- Prima esecuzione: gatto ↔ felino = 0.487
- Seconda esecuzione: gatto ↔ felino = 0.485 (Δ = −0.002)
La differenza è priva di significato interpretativo. Quando si leggono articoli che riportano valori di cosine similarity con tre cifre decimali, è opportuno trattarli come stime con una precisione effettiva di ±0.01-0.02, non come misure esatte.
Confronto interlinguistico: italiano vs. inglese
Eseguendo lo stesso codice con le traduzioni inglesi delle stesse coppie si ottengono risultati sensibilmente diversi:
| Coppia (IT) | cos IT | Coppia (EN) | cos EN | Δ |
|---|---|---|---|---|
| neurone ↔ rete neurale | 0.625 | neuron ↔ neural network | 0.693 | +0.068 |
| caldo ↔ freddo | 0.553 | hot ↔ cold | 0.641 | +0.088 |
| gatto ↔ felino | 0.487 | cat ↔ feline | 0.892 | +0.405 |
| ape ↔ regina | 0.468 | bee ↔ queen | 0.540 | +0.072 |
| ape ↔ algebra | 0.456 | bee ↔ algebra | 0.491 | +0.035 |
| ape ↔ miele | 0.409 | bee ↔ honey | 0.717 | +0.308 |
Tutti i valori inglesi sono superiori a quelli italiani, il che è coerente con una proprietà strutturale di nomic-embed-text v1.5: il corpus di addestramento è prevalentemente in inglese. Un modello addestrato su più testo inglese costruisce uno spazio vettoriale più denso e differenziato per quella lingua; le similarità risultanti sono in media più alte e più informative.
Due differenze meritano però attenzione specifica.
cat ↔ feline (Δ = +0.405): il salto più ampio della serie, da 0.487 a 0.892. In italiano, "felino" è termine tecnico-zoologico di uso limitato; in inglese, "feline" è più presente nel linguaggio comune e ricorre spesso accanto a "cat" in articoli divulgativi, testi veterinari e narrativa. Il corpus inglese ha esposto il modello a questa coppia molto più frequentemente, producendo embedding molto più vicini.
bee ↔ honey (Δ = +0.308): in italiano "ape ↔ miele" era il valore più basso dell'intera serie (0.409); in inglese "bee ↔ honey" è il secondo più alto (0.717), superando anche
"neuron ↔ neural network". La co-occorrenza di "bee" e "honey" nel corpus inglese è evidentemente molto più alta che in quello italiano, probabilmente per la maggiore diffusione di testi anglofoni su apicoltura, gastronomia e prodotti naturali.
Questi risultati illustrano un punto metodologico rilevante: le misure di similarità semantica prodotte da un modello di embedding non sono proprietà universali delle parole, ma proprietà della distribuzione testuale nel corpus specifico su cui il modello è stato addestrato. Confrontare similarità tra lingue diverse usando lo stesso modello monolingue restituisce, in parte, una misura della densità del corpus per quella lingua, non solo della vicinanza semantica interlinguistica.
Scalabilità: cosa succederebbe con 1.000 parole?
Una nota pratica: il codice sopra è lento. Se volessi calcolare la similarità di 1.000 parole (500.000 coppie), il codice farebbe 500.000 chiamate a Ollama, cosa che richiederebbe ore.
In pratica, se ti servisse fare questo:
- Calcola tutti gli embedding una sola volta: Scarica tutte le 1.000 parole, ottieni i loro embedding, salva i vettori in una matrice.
- Calcola tutte le similarità una sola volta: Usa una moltiplicazione matriciale per calcolare tutte le coppie contemporaneamente (operazione velocissima rispetto alle chiamate HTTP).
- Salva i risultati: Scrivi il risultato in un file o in un database.
Questo è quello che fanno i motori di ricerca semantici in produzione. Non calcolano le similarità al volo — le calcolano una volta per tutte, le salvano, poi le interrogano.
Conclusione: la teoria diventa pratica
Questo esperimento mostra che tutto quello che abbiamo imparato non è pura teoria. Puoi eseguire il codice, vedere i numeri, verificare l'intuizione.
E i numeri confermano: gli embedding catturano relazioni semantiche reali, misurabili, quantificabili.
Nel prossimo paragrafo, scoprirai perché il modello impara a creare queste relazioni — il principio (distributional hypothesis) che sta dietro a tutto.
Il principio della co-occorrenza: il meccanismo nascosto
Abbiamo visto che gli embedding hanno una proprietà straordinaria: parole semanticamente simili hanno vettori simili. Ma da dove viene questa proprietà?
Non è stata progettata esplicitamente. Non è una scelta di ingegneria. Emerge dal processo di addestramento come effetto collaterale di un principio più profondo: il principio della co-occorrenza.
Il principio della co-occorrenza: una frase famosa
Nel 1957, il linguista John Rupert Firth formulò il principio così:
"You shall know a word by the company it keeps"
(Conoscerai una parola dalla compagnia che frequenta)
In altre parole: il significato di una parola emerge dal contesto in cui appare.
Non è una formula matematica. È solo un'osservazione linguistica. Ma è un'osservazione profonda.
Se vedi una parola che appare sempre accanto a "caldo", "sole", "fuoco", "estate", probabilmente è una parola che significa qualcosa di caldo. Anche se non conosci la parola, il suo "contesto di apparizione", cioè la compagnia che frequenta, ti dice qualcosa sul suo significato.
Come il modello impara questa lezione
Durante il pre-training, il modello riceve miliardi di testi. Il suo compito è semplice: predire il token successivo, dato il contesto precedente.
Ecco un esempio:
Testo: "Guardo le api nel giardino. Le api producono ______"
Compito: predire il token mancante (la risposta corretta è "miele")
Il modello elabora i primi 9 token, "Guardo le api nel giardino. Le api producono", e fornisce un'ipotesi per il decimo token. Se indovina "miele" (o un token simile), il parametro di perdita (loss) diminuisce. Se indovina "algebra" o "tavolo", la loss aumenta.
Cosa succede durante l'addestramento?
Il modello aggiusta i suoi pesi (inclusa la matrice di embedding) affinché la predizione sia corretta. In questo caso specifico:
- L'embedding di "api" è rappresentativo di tutto ciò che le api sono nel corpus
- L'embedding di "miele" deve essere "prossimo" all'embedding di "api" nello spazio, perché è una predizione frequente quando il contesto contiene "api"
Questo accade non perché il modello capisca qualcosa di entomologia, per lui "miele", "pistoni" e "giardinaggio" sono esattamente alla stessa stregua. Accade solo perché nel corpus di addestramento, "api" e "miele" appaiono frequentemente insieme in contesti di predizione.
Il feedback loop: convergenza attraverso co-occorrenza
Immagina un altro esempio:
Testo 1: "Il felino era affamato. Il _______ si mosse velocemente"
(Risposta: gatto)
Testo 2: "Un felino selvaggio in Africa. Il _______ cacciava di notte"
(Risposta: leopardo)
Testo 3: "Ho portato il mio gatto dal veterinario. Il _______ era stressato"
(Risposta: felino)
Tutti questi testi creano un collegamento statistico: quando il contesto contiene "felino", il modello predice spesso "gatto" o "leopardo". Quando il contesto contiene "gatto", il modello predice spesso "felino".
Dopo miliardi di esempi simili, gli embedding di "gatto" e "felino" convergono verso lo stesso punto nello spazio. Non perché il modello capisce la tassonomia biologica. Ma perché appaiono frequentemente negli stessi contesti di predizione.
Questo è il feedback loop del principio della co-occorrenza:
- Un testo contiene "gatto" seguito da "felino"
- Il modello viene corretto: "predici 'felino' dato 'gatto'"
- Aggiusta i pesi: l'embedding di "gatto" si avvicina all'embedding di "felino"
- Un altro testo contiene "felino" seguito da "gatto"
- Il modello viene corretto: "predici 'gatto' dato 'felino'"
- Aggiusta i pesi: l'embedding di "felino" si avvicina all'embedding di "gatto"
- Ripeti miliardi di volte
Alla fine: gli embedding di "gatto" e "felino" puntano quasi nella stessa direzione.
Perché funziona (globalmente)
Pur senza esserlo veramente, questo meccanismo viene considerato globalmente intelligente. Il principio della co-occorrenza cattura una verità linguistica profonda: le parole che significano cose simili tendono ad apparire negli stessi contesti.
Se parli di "caldo", è probabile che tu parli anche di "sole", "temperatura", "estate", "fuoco". Se parli di "freddo", parli di "neve", "ghiaccio", "inverno", "congelato".
Il modello non conosce la fisica della temperatura. Ma cattura il fatto che queste parole co-occorrono frequentemente e usa questa informazione per organizzare gli embedding.
Per molte relazioni semantiche, questo è sorprendentemente efficace:
- Sinonimi: "automobile" e "macchina" appaiono negli stessi contesti → embedding simili
- Iponimi/Iperonimi: "gatto" e "animale" appaiono insieme → embedding correlati
- Aggettivi correlati: "rosso" e "cremisi" appaiono in contesti simili → embedding vicini
- Associazioni semantiche: "ape" e "miele" co-occorrono → embedding correlati
Il principio della co-occorrenza, messo in pratica attraverso il pre-training predittivo, produce embedding che riflettono relazioni semantiche reali.
Ma ci sono limiti…
Qui arriviamo al punto critico: il principio della co-occorrenza è potente, ma è solo una conseguenza statistica della co-occorrenza.
Non cattura:
- Relazioni causali: "ape" e "miele" hanno cosine similarity 0.409/0.717 perché co-occorrono. Ma il modello non sa che "le api producono miele". Sa solo che appaiono insieme.
- Relazioni procedurali: "acqua" e "vapore" sono correlati negli embedding, ma il modello non sa il processo per cui l'acqua diventa vapore (ebollizione, trasferimento di calore). Sa solo che i termini appaiono in contesti simili.
- Relazioni definitorie: "ape" è correlato a "lavoratrice" negli embedding, ma il modello non sa la definizione biologica di "operaia" in un alveare. Sa solo che le parole appaiono insieme.
La prossima sezione affronterà questi limiti in dettaglio. Ma per ora, ricorda: il principio della co-occorrenza è la ragione per cui gli embedding funzionano. È anche il motivo per cui falliscono in determinati modi.
Conclusione: il principio segreto
La struttura geometrica dello spazio degli embedding — la proprietà che token simili sono vicini — non è magia. Emerge da un principio semplice: parole che appaiono nello stesso contesto sono trattate dal modello come simili.
Questo principio è codificato nella matrice di embedding attraverso miliardi di aggiustamenti incrementali durante il pre-training.
Comprendere il principio della co-occorrenza è comprendere come il modello trasforma il testo grezzo in una rappresentazione semanticamente coerente. È il cuore del funzionamento degli LLM.
I limiti critici: co-occorrenza vs conoscenza reale
Il principio della co-occorrenza è potente. Ma è anche profondamente limitato.
Il problema è questo: il modello cattura co-occorrenza statistica, non comprensione causale.
Questo sembra una distinzione astratta. Ma ha conseguenze concrete e gravi per l'affidabilità del modello.
La co-occorrenza non è causalità
Ricorda: "ape" e "regina" hanno cosine similarity 0.468 (0.540 in inglese). Sono considerati moderatamente simili dal modello.
Perché? Nel corpus di addestramento, i termini appaiono insieme frequentemente: "una regina ape", "il ruolo della regina nell'alveare", "la regina depone uova", ecc.
Ma il modello non sa perché "regina" e "ape" sono correlati. Non sa che:
- Una regina è un tipo specifico di ape
- La regina ha il ruolo biologico di riproduzione
- La regina secerne feromoni che controllano le altre api
- Un alveare non funziona senza regina
Sa solo: queste parole appaiono insieme nel testo. E ha ottimizzato i pesi affinché l'embedding di "regina" sia vicino all'embedding di "ape" perché questo lo aiuta a predire il prossimo token.
Se prendi un apicoltore esperto — che conosce l'etologia delle api attraverso l'osservazione diretta e lo studio — la sua conoscenza di "regina" è di un ordine di grandezza diverso. Sa cose che non possono emergere dalla co-occorrenza statistica.
Il modello simula questa conoscenza. Ma non la possiede.
Tre categorie di relazioni
Possiamo categorizzare le relazioni semantiche in tre livelli di difficoltà:
1. Relazioni associative (co-occorrenza diretta)
Esempio: "ape" ↔ "miele", "fuoco" ↔ "fumo", "acqua" ↔ "pesce"
Queste relazioni sono facili per il modello. Le parole appaiono frequentemente insieme nel corpus. Gli embedding convergono verso la stessa regione dello spazio.
Il modello le cattura bene.
2. Relazioni analogiche (struttura parallela)
Esempio: "re" sta a "regina" come "principe" sta a "principessa"
Sorprendentemente, il modello cattura anche questi: vettore("regina") - vettore("re") ≈ vettore("principessa") - vettore("principe")
Perché? Perché nel testo, queste relazioni si ripetono in strutture parallele. "Il re e la regina…", "Il principe e la principessa…". Il modello impara i vettori in modo che la differenza tra "re" e "regina" sia simile alla differenza tra "principe" e "principessa".
Non è causalità. Ma è una struttura geometrica che emerge dai dati.
3. Relazioni causali/procedurali (processo sottostante)
Esempio: "acqua" → "vapore" (tramite ebollizione), "seme" → "pianta" (tramite crescita), "studente" → "competenza" (tramite apprendimento)
Qui il modello fallisce sistematicamente.
"Acqua" e "vapore" hanno una cosine similarity ragionevole perché appaiono spesso in testi di fisica. Ma il modello non conosce il processo termodinamico che trasforma l'acqua in vapore.
Se chiedi al modello: "Come si trasforma l'acqua in vapore?" probabilmente produrrà una risposta corretta, perché ha letto testi che lo spiegano. Ma la risposta emerge dal pattern matching testuale, non da una comprensione causale.
Ecco il problema: il modello sembra competente anche in categoria 3. Produce frasi grammaticalmente corrette che sembrano spiegare processi causali. Ma la spiegazione è ricombinazione di pattern dal testo di addestramento, non derivazione da principi primi.
Esempi concreti di fallimento
Immagina di fare queste domande:
Domanda: "Se mi trovo in una stanza con una finestra chiusa e una candela accesa, e la stanza è sigillata, per quanto tempo la candela continuerà a bruciare?"
Un esperto di chimica sa: la candela consumerà l'ossigeno finché non rimane più ossigeno. Circa 15-20 minuti in una stanza di medie dimensioni. Conosce il processo: combustione richiede ossigeno, l'ossigeno è limitato, la concentrazione di CO2 aumenta e inibisce la combustione.
Il modello probabilmente produrrà una risposta che sembra corretta. Dirà qualcosa come "la candela brucerà fino a quando l'ossigeno non si esaurisce, circa 20 minuti". Corretto.
Ma se fai una piccola variazione:
Domanda: "Se mi trovo in una stanza sigillata con una candela accesa e una pianta in vaso, per quanto tempo la candela continuerà a bruciare?"
Un esperto sa: la pianta produrrà ossigeno attraverso la fotosintesi durante il giorno, estendendo il tempo di combustione. Di notte, la pianta consumerà ossigeno, riducendo il tempo.
Il modello probabilmente darà la stessa risposta: "circa 20 minuti". Perché nei testi di addestramento, le "stanze sigillate con candele" non vengono discusse insieme alle "piante in vaso" abbastanza frequentemente per creare un collegamento statistico.
Il modello ha fallito su una piccola variazione perché non capisce il principio causale sottostante. Capisce i pattern.
Un altro esempio: il bug dell'astrologia
Qui arriviamo a qualcosa di veramente preoccupante.
Nel corpus di addestramento, il testo sull'astrologia è mescolato con il testo sull'astronomia. Entrambi parlano di stelle, pianeti, costellazioni.
Se chiedi al modello: "Qual è la distanza di Giove dalla Terra?" probabilmente risponde correttamente "circa 550 milioni di km".
Ma se chiedi: "Secondo l'astrologia, quali sono le caratteristiche di una persona nata sotto il segno della Bilancia?" il modello probabilmente risponde con stereotipi astrologici — "equilibrio", "indecisione", "amore per l'armonia" — come se fossero fatti.
Il modello non sa che l'astrologia è pseudoscienza. Sa solo che nel testo, queste parole appaiono insieme.
La co-occorrenza statistica non è sufficiente per distinguere verità da falsità, scienza da pseudoscienza, causalità da correlazione spuria.
Perché il modello sembra più competente di quanto sia
Questo è il paradosso centrale. Il modello può:
- Scrivere codice funzionante (pattern matching su milioni di repositories GitHub)
- Rispondere a domande di storia e geografia (pattern matching su enciclopedie e articoli di notizie)
- Spiegare concetti matematici (pattern matching su libri di testo e forum)
E quindi sembra intelligente, competente, affidabile.
Ma per ogni compito dove fallisce — e ce ne sono molti — fallisce perché si basa su correlazione statistica, non su comprensione causale.
Implicazioni per l'affidabilità
Cosa significa questo per il modo in cui usi i modelli?
- Non fidarti ciecamente: Se il modello produce una risposta coerente, non significa che sia vera. Potrebbe essere un pattern statistico plausibile che non corrisponde a realtà.
- Variazioni e casi limite sono pericolosi: Il modello fallisce prevedibilmente quando affronti una piccola variazione di un problema che ha visto nel training. Non ha principi sottostanti per generalizzare.
- Domande su causalità sono pericolose: Chiedi al modello "perché?" solo se sei disposto a verificare la risposta con fonti affidabili. Il modello non sa davvero perché.
- La competenza è locale: Il modello è competente in regioni dello spazio testuale dove il training data è denso. È incompetente in regioni dove il training data è raro o dove la causalità è sottodeterminata dai dati.
Conclusione: la geometria ha limiti
Gli embedding sono una geometria. Una geometria bella, interpretabile, utile.
Ma la geometria cattura solo ciò che è scritto nei testi. Non cattura ciò che è vero secondo il mondo fisico, biologico, o logico.
Il modello è straordinariamente bravo a produrre testo che sembra vero. È sorprendentemente scarso a produrre testo che è vero quando la verità richiede comprensione causale.
Questa distinzione — tra plausibilità statistica e verità causale — è il confine più importante per comprendere quando fidarti e quando dubitare dei modelli linguistici.
Nel prossimo articolo, vedrai come questi embedding vengono usati dai blocchi transformer per produrre sequenze di token. In particolare scoprirai come questi embedding vengono elaborati dal meccanismo di self-attention e così scoprirai che i limiti della geometria degli embedding si propagano attraverso tutta la pipeline.
L'attention è il meccanismo per cui il modello fa interagire tra loro gli embedding. Due token con embedding simili si "prestano attenzione" a vicenda. Due token con embedding dissimili si ignorano.
Attraverso il meccanismo di attention che il modello impara a comprendere le relazioni tra token in una sequenza, e produce il prossimo token in modo coerente con il contesto.
Quando senti dire che "gli LLM sono solo matematica lineare" o "sono solo pattern matching", ricorda quello che abbiamo visto in questo articolo.
È vero: i modelli sono matematica e si basano su pattern dal testo di addestramento.
Ma la geometria che emerge da questa matematica è tutt'altro che ordinaria. È una rappresentazione dell'intera struttura semantica del linguaggio, catturata in una matrice di 525 milioni di numeri.
Però non è intelligenza. Non ha assolutamente nessuna delle caratteristiche dell'intelligenza. E non è comprensione nel senso in cui le intendi tu.
È qualcosa di potente e reale ma è qualcosa di diverso. Quindi parlare di intelligenza artificiale è senza dubbio un abuso del linguaggio, utile ad imporre una narrativa che non ha nulla a che vedere con la realtà dei fatti di questi sistemi.
Riferimenti
Jurafsky, Daniel, e James H. Martin, Speech and Language Processing, capitolo “Vector Semantics and Embeddings”. Disponibile online nella bozza della terza edizione:
https://web.stanford.edu/~jurafsky/slp3/
È il riferimento didattico migliore per spiegare embedding, spazi vettoriali, similarità semantica e cosine similarity.
Bengio, Yoshua, Réjean Ducharme, Pascal Vincent e Christian Jauvin (2003), “A Neural Probabilistic Language Model”, in Journal of Machine Learning Research, 3, pp. 1137–1155. Disponibile qui:
https://www.jmlr.org/papers/v3/bengio03a.html
È uno dei testi fondamentali per capire il passaggio da parole come simboli discreti a rappresentazioni distribuite apprese da una rete neurale.
Mikolov, Tomas, Kai Chen, Greg Corrado e Jeffrey Dean (2013), “Efficient Estimation of Word Representations in Vector Space”. Disponibile su arXiv:
https://arxiv.org/abs/1301.3781
È il riferimento classico su Word2Vec e sulla formazione di regolarità semantiche e sintattiche negli spazi vettoriali.
Vaswani, Ashish et al. (2017), “Attention Is All You Need”, in Advances in Neural Information Processing Systems 30. Disponibile su arXiv:
https://arxiv.org/abs/1706.03762
È il paper fondativo dei Transformer e serve per collegare gli embedding all’architettura moderna degli LLM.
Nussbaum, Zach, John X. Morris, Brandon Duderstadt e Andriy Mulyar (2024), “Nomic Embed: Training a Reproducible Long Context Text Embedder”. Disponibile su arXiv:
https://arxiv.org/abs/2402.01613
È il riferimento tecnico principale per il modello nomic-embed-text, usato nell’esempio pratico dell’articolo.
Bender, Emily M., e Alexander Koller (2020), “Climbing towards NLU: On Meaning, Form, and Understanding in the Age of Data”, in Proceedings of the 58th Annual Meeting of the Association for Computational Linguistics, pp. 5185–5198. Disponibile negli atti ACL:
https://aclanthology.org/2020.acl-main.463/
È il riferimento più importante per discutere il limite tra manipolazione della forma linguistica, significato e comprensione.
Pearl, Judea, e Dana Mackenzie (2018), The Book of Why: The New Science of Cause and Effect, New York, Basic Books. Pagina dell’autore:
https://bayes.cs.ucla.edu/WHY/
È utile per distinguere correlazione, co-occorrenza, intervento causale e ragionamento controfattuale: precisamente il punto critico della parte finale dell’articolo.

Potrebbe interessarti
Servizi online di Intelligenza Artificiale: scegliere tra offerte, rischi e opportunità
MemPalace: Da Resident Evil alla rivoluzione della Memoria AI passando per Cicerone
Il tokenizzatore (Autopsia di un LLM - parte seconda)