Query, Key e Value: come un modello decide cosa guardare

Tre ruoli, un prodotto scalare e una softmax: come funziona davvero il meccanismo di attenzione, spiegato passo per passo con numeri ed esempi.
Immagine realizzata con IA
Le immagini presenti nel post possono essere realizzate o elaborate con strumenti di Intelligenza Artificiale. Questa scelta nasce anche dalla necessità di prevenire le continue contestazioni e richieste economiche improprie che ricevo relative a fotografie utilizzate che sono sempre state acquistate con regolare licenza.
Contenuti dell'articolo

Ogni parola guarda tutte le altre: quando ho spiegato i Transformer ho usato questa immagine perché è immediata, ma da sola non dice cosa significa guardare. Dietro c’è un meccanismo fatto di tre ruoli, una moltiplicazione, una divisione e una normalizzazione. In questo articolo lo smonto pezzo per pezzo, con esempi e numeri, senza formule lasciate lì da sole.

Prima il quadro generale. Se ti manca il contesto, parti da Cosa sono i Transformer e come funzionano. Lì trovi la storia, l’architettura e perché questa famiglia di modelli ha cambiato tutto. Qui entriamo dentro un solo ingranaggio: l’attenzione.

Il problema: una parola da sola non significa niente

Prendi la parola pesca. Può essere il frutto, l’attività in mare, il verbo alla terza persona, perfino un cognome. Nella frase “la pesca era dolcissima” capisci subito che si parla di frutta. Nella frase “la pesca è stata lunga” capisci che si parla di mare. La parola è identica, il significato no: lo costruisce il contesto.

Un modello linguistico deve fare esattamente questo, per ogni parola della frase. Deve capire quali altre parole le danno senso e in che misura. Prendi “il gatto beve il latte”: la parola “beve” ha bisogno di sapere chi beve e cosa, altrimenti resta un’azione senza attori. Un essere umano lo fa senza pensarci, un modello deve calcolarlo.

Le vecchie reti ricorrenti lo facevano leggendo in fila, una parola alla volta, trascinandosi dietro un riassunto di quello che avevano già visto. Funzionava, ma piano e con la memoria corta: dopo venti parole, le prime erano già sbiadite. E soprattutto non si poteva parallelizzare, perché ogni passo dipendeva dal precedente.

I Transformer hanno cambiato idea: ogni parola può confrontarsi con tutte le altre, in parallelo, senza fare la fila. Per farlo servono tre cose: qualcuno che fa una domanda, qualcuno che risponde con un’etichetta e qualcosa che viene portato a casa. In gergo: query, key e value.

Tre ruoli, la metafora della biblioteca

Immagina di entrare in biblioteca e cercare un libro sui vulcani. Nella tua testa hai una richiesta: “voglio qualcosa che parli di vulcani”. Quella richiesta è la query. Cammini tra gli scaffali e leggi i titoli sul dorso: ogni titolo è una key. Quando un titolo corrisponde alla tua richiesta, tiri giù quel libro. Il contenuto del libro è il value.

Il punto che sfugge quasi sempre è questo: la stessa parola produce tutti e tre i ruoli insieme. “Gatto” è una query quando cerca informazioni utili a capire se stessa, è una key quando viene cercata dalle altre parole, è un value quando le altre parole si portano a casa un pezzo del suo significato. Non sono tre parole diverse, sono tre versioni della stessa parola.

RuoloDomanda a cui rispondeNel modello
QueryCosa sto cercando?Vettore della parola che osserva
KeyDi cosa parlo, come mi faccio trovare?Vettore della parola osservata
ValueCosa porto con me, se mi scegli?Vettore della parola osservata

Come nascono, in pratica

Ogni token entra nel modello come un vettore di numeri, cioè un embedding. Da lì si ricavano i tre ruoli moltiplicandolo per tre matrici di pesi diverse: Wq per la query, Wk per la key, Wv per il value.

Quelle matrici non le scrive nessuno a mano: si imparano durante l’addestramento, come tutto il resto. Il modello scopre da solo quali trasformazioni rendono la ricerca più utile, esattamente come impara a riconoscere quali parole contano in una frase.

Perché non usare direttamente l’embedding? Perché l’embedding dice cosa significa una parola in generale, mentre qui serve sapere che tipo di informazione sta cercando o offrendo in quel momento. Le tre matrici specializzano la stessa rappresentazione in tre compiti diversi. È come una persona che parla tre lingue: il contenuto è uno, il modo di esprimerlo cambia.

Le dimensioni contano. Se il modello lavora a 512 dimensioni, le matrici possono proiettare su spazi più piccoli, per esempio 64. Non è un dettaglio da nerd: da quella scelta dipende il costo del calcolo e tra poco vedrai perché.

Schema dei tre ruoli: dall'embedding alle matrici Wq, Wk e Wv che producono query, key e value
Da un embedding a tre ruoli: le matrici Wq, Wk e Wv producono query, key e value

Il prodotto scalare: quanto due vettori si somigliano

Ora bisogna confrontare ogni query con ogni key. Il confronto si fa con il prodotto scalare, un’operazione che restituisce un solo numero e che ha un’interpretazione geometrica molto comoda.

Se due vettori puntano nella stessa direzione, il prodotto è alto e positivo. Se sono perpendicolari, il prodotto è zero. Se puntano in direzioni opposte, è negativo. In pratica: numero alto, i due vettori si assomigliano; zero, non c’entrano niente; negativo, dicono cose contrarie.

Un esempio minuscolo. Query [1, 0] contro key [1, 0]: il prodotto fa 1. La stessa query contro la key [0, 1]: fa 0. Contro la key [-1, 0]: fa -1. Il segno racconta già quasi tutto, senza bisogno di altre spiegazioni.

Qui c’è una differenza rispetto alla similarità coseno: nell’attenzione non si normalizza per la lunghezza dei vettori. Si usa il prodotto grezzo, che premia anche i vettori “forti”, quelli con valori grandi. È una scelta voluta e il problema dell’intensità si risolve subito dopo, con la divisione.

Tutte le coppie in un colpo solo

Metti le query di tutte le parole in una matrice Q e le key in una matrice K. Il confronto simultaneo tra ogni query e ogni key si ottiene con un solo prodotto matriciale: Q moltiplicato per K trasposta. Esce una matrice quadrata, dove la riga è la parola che guarda e la colonna è la parola guardata.

Per una frase di venti token sono quattrocento punteggi in un colpo. Ecco perché i Transformer sfruttano bene le GPU: non c’è un ciclo che aspetta la parola successiva, c’è algebra lineare su blocchi interi. Ed è anche il motivo per cui il costo cresce in modo quadratico: raddoppi la lunghezza, quadruplichi i confronti.

Perché si divide per la radice di d_k

I punteggi grezzi hanno un problema: crescono con la dimensione dei vettori. Con 64 dimensioni e valori intorno all’unità, i prodotti scalari possono arrivare a decine o centinaia. Numeri così grandi mandano in crisi il passaggio successivo.

La softmax è molto sensibile alle differenze. Se i punteggi sono enormi, la funzione schiaccia quasi tutto su un valore vicino a 1 e lascia gli altri a zero. Il modello diventa sicurissimo di una sola parola e cieco su tutte le altre. Peggio: i gradienti diventano minuscoli e l’apprendimento rallenta fino a fermarsi.

La soluzione è semplice: dividere tutti i punteggi per la radice quadrata della dimensione delle key. Con 64 dimensioni si divide per 8 e i numeri rientrano in una scala gestibile. Il rapporto tra i punteggi resta lo stesso, cambia solo l’unità di misura. Non è un trucco estetico: è ciò che tiene in piedi l’addestramento.

Softmax: da punteggi a percentuali

Dopo la divisione, ogni riga della matrice contiene punteggi arbitrari, anche negativi. Servono pesi che sommino a 1, così da poter dire “questa parola conta il 66%, quest’altra il 24%, quell’altra il 10%”. Di questo si occupa la softmax, che ho spiegato in dettaglio nell’articolo sulle funzioni di attivazione.

Un esempio con tre punteggi: 2, 1 e 0. La softmax calcola l’esponenziale di ciascuno, ottenendo circa 7,39, 2,72 e 1,00. La somma è 11,11. Dividendo ogni valore per la somma si ottiene 66,5%, 24,5% e 9%. Tradotto: la prima parola pesa due terzi dell’attenzione, la seconda un quarto, la terza poco più di niente.

Nota una cosa importante: i pesi non sono mai esattamente zero. Anche una parola poco pertinente conserva una piccola quota. È una delle ragioni per cui i modelli linguistici mescolano informazioni invece di scegliere in modo netto e per cui a volte trascinano dentro un dettaglio che non c’entra.

Moltiplicare per i value

Ultimo passaggio. Ogni parola ha un value, cioè il contenuto che porta con sé. L’output per una data parola è la somma dei value di tutte le parole, ognuno moltiplicato per il proprio peso di attenzione.

Nell’esempio di prima: 66,5% del value della prima parola, più 24,5% del secondo, più 9% del terzo. Il risultato è un vettore nuovo, che non coincide con nessuno dei tre: è una sintesi pesata costruita su misura per quella posizione.

Questo vettore passa allo strato successivo, dove trova altre trasformazioni, fino all’ultimo strato, quello che produce la previsione sul token seguente. Il giro si ripete per ogni posizione della frase e ogni posizione ottiene una sintesi diversa.

La formula completa, tradotta in italiano

I quattro passaggi del meccanismo di attenzione: confronto Q per K trasposta, divisione per radice di d_k, softmax e media pesata dei value
I quattro passaggi dell’attenzione, dalla query alla media pesata dei value

Mettendo insieme i pezzi si ottiene la formula che compare in ogni articolo tecnico: attenzione uguale softmax di Q per K trasposta, diviso radice di d_k, il tutto moltiplicato per V. Vista così sembra ostica, ma è solo la scrittura compatta dei quattro passaggi appena fatti.

Letta a pezzi dice una cosa banale: confronta ogni domanda con tutte le etichette, trasforma i punteggi in percentuali, prendi quella percentuale di contenuto da ciascuna parola. Il nome tecnico è scaled dot-product attention: “scaled” per la divisione, “dot-product” per il prodotto scalare. Non c’è altro.

Confronto, scala, normalizzo, mescolo. Se devi ricordare una sola cosa di questo articolo, ricorda questa sequenza: quattro passaggi nell’ordine giusto e hai il meccanismo completo.

Un esempio passo passo con quattro parole

Prendi la frase “la banca chiude il conto”. Il modello deve capire che “banca” qui è un istituto, non la riva di un fiume. La parola produce una query che cerca indizi di tipo finanziario e le key delle altre parole sono le etichette che rispondono.

Le key di “chiude” e “conto” rispondono bene, quella di “la” risponde male. I punteggi grezzi, in forma molto stilizzata, potrebbero essere 9 per “conto”, 7 per “chiude”, 1 per “la”. Dopo divisione e softmax diventano percentuali.

L’output di “banca” è quindi una miscela in cui il significato di “conto” pesa molto, quello di “chiude” abbastanza, quello dell’articolo quasi nulla. Ecco perché in “la banca del fiume” esce un vettore completamente diverso: cambiano le key intorno, cambiano i pesi, cambia il risultato. Il significato non è nel token, è nella combinazione.

Cosa resta fuori da qui

Questo è il funzionamento di una sola testa di attenzione. Un Transformer reale ne usa diverse in parallelo, ognuna con le proprie matrici e dimensioni ridotte e poi concatena i risultati. È il multi-head, che merita un articolo a parte.

Restano fuori anche il positional encoding, che dice al modello in che ordine stanno le parole e le connessioni residuali che tengono insieme i blocchi. Senza quei pezzi l’attenzione da sola non funziona, ed è il motivo per cui scrivo un articolo dedicato all’anatomia di un blocco Transformer.

Quando leggi che un modello presta attenzione a una parola, non immaginare un evidenziatore: immagina una media pesata, dove ogni parola contribuisce in proporzione a quanto è pertinente e nessuna sparisce del tutto.

Domande frequenti

Query, key e value sono parole diverse?

No. Sono tre rappresentazioni della stessa parola, ottenute moltiplicandola per tre matrici di pesi diverse. Ogni token è contemporaneamente query, key e value e i tre ruoli servono a compiti differenti.

Perché servono tre matrici invece di una?

Perché i ruoli sono diversi. Cercare informazioni, farsi trovare e portare contenuto richiedono rappresentazioni diverse: una sola matrice costringerebbe il modello a usare la stessa prospettiva per tutti e tre, perdendo precisione proprio dove serve.

Il prodotto scalare è l’unico modo per confrontare query e key?

No. Esistono varianti che sommano i vettori invece di moltiplicarli, come l’attenzione additiva dei primi lavori di traduzione automatica. Il prodotto scalare ha vinto perché si calcola molto più efficientemente sulle GPU.

Perché si divide proprio per la radice di d_k?

Perché il prodotto scalare cresce in proporzione alla dimensione dei vettori e la radice riporta la scala a un valore confrontabile con 1. Senza quella divisione la softmax diventa troppo estrema e l’apprendimento peggiora.

Se una parola non è pertinente, viene ignorata del tutto?

Quasi. Il suo peso diventa molto piccolo ma raramente nullo, perché la softmax non produce zeri esatti. Un residuo di informazione resta sempre, ed è parte del motivo per cui i modelli non ragionano in modo perfettamente pulito.

Dove si vedono questi pesi in un modello reale?

Non direttamente: i pesi di attenzione non sono esposti dalle API. Si osservano solo con strumenti di interpretabilità che aprono il modello e ne registrano le attivazioni interne.

Newsletter settimanale con le ultime novità sull’intelligenza artificiale

Corso gratuito sull’Intelligenza Artificiale, dalle nozioni base ai concetti più complessi

Condividi:

Potrebbero interessarti