Nel capitolo 7 abbiamo introdotto la metafora del "tavolo di Wittgenstein" per comprendere come funzionano i Transformer: un tavolo rotondo dove le parole si siedono e dialogano tra loro, dove ogni parola sviluppa una comprensione del suo ruolo attraverso l'interazione con tutte le altre. Ora, con questo simulatore, potete vedere concretamente come avviene questa magia computazionale.
Il simulatore vi guida attraverso le quattro fasi fondamentali che trasformano una semplice frase in quelle rappresentazioni numeriche che permettono ai Large Language Models di "comprendere" e generare testo.
1. Tokenizzazione: Il primo passo dove il testo viene spezzettato in unità base (token) e ad ogni token viene assegnato un ID numerico. È come assegnare un numero di posto a ogni ospite che si siede al tavolo.
2. Word Embedding: Ogni token viene trasformato in un vettore multidimensionale. Questi vettori sono come le "personalità numeriche" delle parole - catturano aspetti del loro significato in forma matematica.
3. Positional Encoding: Viene aggiunta l'informazione sulla posizione di ogni parola nella frase attraverso funzioni sinusoidali. Senza questo passaggio, il modello non saprebbe distinguere "il gatto mangia il topo" da "il topo mangia il gatto"!
4. Self-Attention: Il cuore del Transformer. Ogni parola "dialoga" con tutte le altre, pesando quanto deve prestare attenzione a ciascuna per comprendere il proprio significato nel contesto.
Esperimento 1: Il Potere del Contesto
Iniziate confrontando le due frasi del capitolo:
"Una vecchia legge la regola in silenzio"
"Una vecchia legge la regola il silenzio"
Cliccate su "Inizia Analisi" per la prima frase e navigate fino alla fase 4 (Self-Attention). Osservate le connessioni tra le parole. Poi tornate all'inizio, inserite la seconda frase e ripetete il processo.
Cosa notare: Come il cambio da "in" a "il" modifica completamente le relazioni di attenzione. Nella prima frase, "silenzio" si collega fortemente a "in" formando un complemento di modo. Nella seconda, "silenzio" diventa soggetto e si collega diversamente a "regola". Questo è esattamente ciò che permette ai Transformer di disambiguare frasi complesse!
Esperimento 2: L'Ambiguità Risolta
Provate con la frase ambigua: "Dice che mangia le mele vermi"
Navigate attraverso tutte le fasi osservando come:
Nella tokenizzazione, "vermi" viene riconosciuto come token separato
Nell'embedding, vedrete che "vermi" e "mele" hanno rappresentazioni molto diverse
Nel self-attention, notate come il modello cerca di dare senso alla frase creando connessioni che potrebbero suggerire l'interpretazione "mele verdi" (se il modello fosse completamente addestrato)
Esperimento 3: Il Ruolo della Posizione
Usate una frase semplice come "Il gatto dorme sul divano" e:
Osservate la fase 3 con il positional encoding attivato
Disattivate il checkbox "Mostra encoding posizionale"
Notate come senza le informazioni posizionali, le parole perdono il loro ordine
Perché è importante: Senza positional encoding, "Il gatto dorme sul divano" sarebbe indistinguibile da "Sul divano dorme il gatto" per il modello!
Esperimento 4: Dimensionalità e Complessità
Provate a variare le "Dimensioni embedding" da 2 a 8 usando il cursore:
Con 2 dimensioni: rappresentazione minimale, poche sfumature
Con 8 dimensioni: più ricchezza rappresentazionale
Osservate nella fase 2 (Embedding) come la matrice diventa più complessa. Nei Transformer reali, queste dimensioni sono nell'ordine delle migliaia!
Esperimento 5: Creare le Vostre Frasi
Inserite frasi con strutture interessanti:
Frasi con pronomi: "Marco disse che lui avrebbe vinto" - osservate come "lui" cerca di collegarsi a "Marco"
Frasi con dipendenze a lungo raggio: "Il libro che ho letto ieri era interessante" - notate come "libro" e "interessante" si collegano nonostante la distanza
Nella visualizzazione Self-Attention:
Le linee più spesse indicano maggiore attenzione
Ogni parola ha sempre la massima attenzione su se stessa (anche se non visualizzata per chiarezza)
Parole funzionali come "il", "la" tendono a distribuire la loro attenzione
Parole contenuto come nomi e verbi creano connessioni più forti e specifiche
Nella matrice di Embedding:
I colori blu indicano valori positivi, rossi negativi
L'intensità del colore corrisponde alla magnitudine
Parole simili tendono ad avere pattern di colori simili
Mentre esplorate il simulatore, ricordate che state osservando una versione estremamente semplificata di ciò che accade in modelli come GPT-4 o Claude. Qui lavoriamo con:
Vocabolario di ~20 parole (vs. 50.000+ nei modelli reali)
Embedding di 2-8 dimensioni (vs. 12.288 in GPT-4!)
Frasi di 5-10 parole (vs. contesti di migliaia di token)
Eppure, anche in questa versione giocattolo, potete cogliere i principi fondamentali che rendono i Transformer così potenti: la capacità di trasformare simboli discreti in rappresentazioni continue, di catturare relazioni contestuali complesse, e di far "dialogare" le parole tra loro in quello spazio matematico che abbiamo poeticamente chiamato il "tavolo di Wittgenstein".
Buona esplorazione! E ricordate: ogni volta che interagite con ChatGPT, Claude o altri LLM, sotto il cofano sta avvenendo esattamente questo processo, solo su una scala enormemente più grande.
Prima di tutto, dobbiamo capire perché i Transformer hanno bisogno del positional encoding. A differenza delle reti ricorrenti (RNN) che processano le parole una alla volta in sequenza, i Transformer guardano tutte le parole simultaneamente. È come se tutte le parole si sedessero contemporaneamente al "tavolo di Wittgenstein" - ma senza un modo per distinguere chi si è seduto per primo!
Senza positional encoding, le frasi "Il gatto mangia il pesce" e "Il pesce mangia il gatto" sarebbero identiche per il modello - un problema piuttosto serio!
Le funzioni sinusoidali (sin e cos) sono state scelte per creare un "codice a barre" unico per ogni posizione. Ecco la formula dal paper originale:
PE(pos, 2i) = sin(pos / 10000^(2i/d_model))
PE(pos, 2i+1) = cos(pos / 10000^(2i/d_model))
Dove:
pos = posizione della parola (0, 1, 2, ...)
i = dimensione dell'embedding
d_model = dimensione totale dell'embedding
Immagina di avere diverse onde sinusoidali con frequenze diverse:
Dimensione 0: sin(pos/10000^0) = sin(pos) - oscilla rapidamente
Dimensione 2: sin(pos/10000^(2/512)) = sin(pos/10000^0.0039) - oscilla più lentamente
Dimensione 4: sin(pos/10000^(4/512)) - ancora più lenta
E così via...
Ogni posizione riceve una combinazione unica di questi valori sinusoidali. È come avere un codice Morse multidimensionale:
Posizione 0: [0.00, 1.00, 0.00, 1.00, ...]
Posizione 1: [0.84, 0.54, 0.01, 0.99, ...]
Posizione 2: [0.91, -0.41, 0.02, 0.99, ...]
E così via...
Le funzioni sinusoidali possono generare encoding per posizioni mai viste durante l'addestramento. Se il modello è stato addestrato su frasi di 100 parole, può comunque gestire frasi di 200 parole.
Una proprietà matematica elegante: la distanza tra PE(pos) e PE(pos+k) dipende solo da k, non dalla posizione assoluta. Questo aiuta il modello a imparare relazioni relative ("la parola 3 posizioni avanti").
Non servono parametri addizionali da apprendere - le funzioni sono deterministiche e calcolabili direttamente.
Nel tuo simulatore, quando attivi il positional encoding, dovresti vedere:
Onde di diverse frequenze sovrapposte nel grafico
Pattern unici per ogni posizione verticale (dove si trovano le parole)
Linee tratteggiate che mostrano dove ogni parola "raccoglie" il suo encoding posizionale
È come se ogni sedia al tavolo di Wittgenstein avesse un'etichetta numerica invisibile ma matematicamente perfetta, permettendo alle parole di sapere non solo con chi stanno dialogando, ma anche dove si trovano nell'ordine della conversazione.
Questa eleganza matematica è uno dei motivi per cui i Transformer hanno rivoluzionato il processamento del linguaggio naturale!
Per chi vuole capirne di più... e farsi venire il mal di testa se pensa a questo quando scrive i Prompt.... (lo stesso, anzi parecchio peggio, potrebbe accadere se uno pensa alle pompe di sodio e potassio dei neuroni per qualunque istante della nostra vita...)
Facciamo un esempio numerico concreto con la parola "regola" dalla frase "Una vecchia legge la regola in silenzio".
Frase: "Una vecchia legge la regola in silenzio"
Parola: "regola"
Posizione: 4 (contando da 0: Una=0, vecchia=1, legge=2, la=3, regola=4)
Dimensioni embedding: 4 (come default del simulatore)
Dal simulatore, la parola "regola" (ID: 4) genera questo embedding basato sulla funzione:
embedding[i] = sin(token_id * (i+1) * 0.5) * 0.5 + cos(token_id * (i+1) * 0.3) * 0.3
Per "regola" (ID=4):
Dim 0: sin(4×1×0.5)×0.5 + cos(4×1×0.3)×0.3 = sin(2.0)×0.5 + cos(1.2)×0.3 ≈ 0.454 + 0.109 = 0.563
Dim 1: sin(4×2×0.5)×0.5 + cos(4×2×0.3)×0.3 = sin(4.0)×0.5 + cos(2.4)×0.3 ≈ -0.378 - 0.223 = -0.601
Dim 2: sin(4×3×0.5)×0.5 + cos(4×3×0.3)×0.3 = sin(6.0)×0.5 + cos(3.6)×0.3 ≈ -0.140 - 0.258 = -0.398
Dim 3: sin(4×4×0.5)×0.5 + cos(4×4×0.3)×0.3 = sin(8.0)×0.5 + cos(4.8)×0.3 ≈ 0.494 + 0.030 = 0.524
Word Embedding di "regola": [0.563, -0.601, -0.398, 0.524]
Ora calcoliamo il positional encoding per la posizione 4:
PE(pos, 2i) = sin(pos / 10000^(2i/d_model))
PE(pos, 2i+1) = cos(pos / 10000^(2i/d_model))
Per pos=4 e d_model=4:
Dim 0 (i=0): PE(4,0) = sin(4 / 10000^(0/4)) = sin(4 / 1) = sin(4) ≈ -0.757
Dim 1 (i=0): PE(4,1) = cos(4 / 10000^(0/4)) = cos(4 / 1) = cos(4) ≈ -0.654
Dim 2 (i=1): PE(4,2) = sin(4 / 10000^(2/4)) = sin(4 / 100) = sin(0.04) ≈ 0.040
Dim 3 (i=1): PE(4,3) = cos(4 / 10000^(2/4)) = cos(4 / 100) = cos(0.04) ≈ 0.999
Positional Encoding posizione 4: [-0.757, -0.654, 0.040, 0.999]
Il simulatore combina i due vettori sommando l'encoding posizionale scalato per 0.3:
Final = Word_Embedding + 0.3 × Positional_Encoding
Calcolo finale per "regola":
Dim 0: 0.563 + (0.3 × -0.757) = 0.563 - 0.227 = 0.336
Dim 1: -0.601 + (0.3 × -0.654) = -0.601 - 0.196 = -0.797
Dim 2: -0.398 + (0.3 × 0.040) = -0.398 + 0.012 = -0.386
Dim 3: 0.524 + (0.3 × 0.999) = 0.524 + 0.300 = 0.824
Embedding finale di "regola" alla posizione 4: [0.336, -0.797, -0.386, 0.824]
Confrontiamo con la stessa parola in una posizione diversa. Se "regola" fosse in posizione 2:
Il word embedding resterebbe identico: [0.563, -0.601, -0.398, 0.524]
Il positional encoding cambierebbe: [-0.909, -0.416, 0.020, 1.000]
Il risultato finale sarebbe: [0.290, -0.726, -0.392, 0.824]
Anche se è la stessa parola, l'embedding finale è diverso perché incorpora l'informazione posizionale! Questo permette al Transformer di distinguere "La regola è chiara" da "È chiara la regola" - stesso significato ma diversa struttura sintattica.
Ecco perché ogni posizione ha davvero un "pattern unico" che si riflette nell'embedding finale di ogni parola!