# Trasformatore (informatica)

In informatica e più precisamente nell'apprendimento automatico, un trasformatore (in inglese transformer) è un modello di apprendimento profondo che adotta il meccanismo della auto-attenzione, pesando differentemente la significatività di ogni parte dei dati in ingresso. È usato primariamente nelle branche dell'elaborazione del linguaggio naturale e della visione artificiale.
Come le reti neurali ricorrenti (RNN), i trasformatori sono progettati per processare dati sequenziali, come il linguaggio naturale, con l'applicazione alla traduzione e la sintetizzazione di testi. Tuttavia, a differenza delle RNN, i trasformatori elaborano l'intero insieme di dati d'ingresso contemporaneamente. Il cosiddetto meccanismo dell'attenzione fornisce il contesto per ogni posizione nella sequenza di ingresso. Per esempio, se i dati rappresentano una frase, il trasformatore non deve elaborare una parola alla volta: questo permette più parallelizzazione rispetto alle RNN e perciò di ridurre i tempi dell'addestramento.
I trasformatori sono stati presentati nel 2017 da un gruppo di Google Brain e sono sempre più il modello preferito per affrontare problemi di elaborazione del linguaggio naturale, sostituendo i modelli a RNN come la memoria a lungo breve termine (LSTM, da long short-term memory). L'aggiuntiva parallelizzazione durante l'addestramento del modello permette l'utilizzo di insiemi di dati più grandi. Questo portò allo sviluppo di sistemi preaddestrati come BERT (Bidirectional Encoder Representations from Transformers) e GPT (Generative Pre-trained Transformer), allenati con grandi insiemi di dati di linguaggi, come il corpo letterario di Wikipedia e Common Crawl, e può essere regolato per essere ottimizzato a svolgere compiti specifici.

## Storia
### Predecessori

Per molti anni, la modellazione e la generazione di sequenze sono state effettuate utilizzando semplici reti neurali ricorrenti (RNN). Un esempio precoce e ben citato è stata la rete di Elman (1990). In teoria, le informazioni contenute in un singolo token possono propagarsi arbitrariamente fino in fondo alla sequenza, ma in pratica il problema del gradiente nullo fa sì che lo stato del modello alla fine di una lunga frase, non mantenga informazioni precise ed estraibili sui token precedenti.
Una svolta fondamentale è stata LSTM (1995), Template:NoteTag una RNN innovativa capace di superare il problema del gradiente, consentendo un apprendimento efficiente atta alla modellazione di sequenze lunghe. Una delle innovazioni chiave risiedeva nell'uso di un meccanismo di attenzione basato sulla capacità di alcuni neuroni di moltiplicare gli output di altri neuroni, (le cosiddette unità moltiplicative). Le reti neurali che utilizzano unità moltiplicative furono successivamente chiamate reti sigma-pi o reti di ordine superiore. I modelli LSTM sono stati l'architettura standard per la modellazione di sequenze lunghe fino alla pubblicazione dei Transformers nel 2017.Tuttavia, l'architettura LSTM utilizzava ancora l'elaborazione sequenziale, come la maggior parte delle altre RNN. Nello specifico, le RNN operano un token alla volta dall'inizio alla fine della sequenza. Non sono perciò in grado di operare più token in parallelo.
I trasformatori moderni superano questo problema, ma a differenza delle reti neurali reattive (RNN), richiedono tempi di calcolo superiori (di ordine quadratico rispetto alle dimensioni della finestra di contesto). Il controllore di peso veloce con scala lineare (1992) viene allenato per calcolare una matrice di peso utile per un'ulteriore elaborazione, a seconda dell'input. Una delle sue due reti ha "pesi veloci" o "collegamenti dinamici" (1981). Una rete neurale lenta impara tramite la discesa del gradiente a generare chiavi e valori utili per calcolare i cambiamenti di peso della rete neurale veloce (weights) che viene infine impiegata per generare le risposte alle query. Successivamente si è dimostrato che ciò è equivalente a un trasformatore lineare non normalizzato.

### Attenzione con seq2seq

L'idea dietro la transduzione di sequenza operata tramite codificatore-decodificatore si è sviluppata nei primi anni del 2010 (vedi articoli precedenti). Gli articoli più comunemente citati come autori dell'architettura seq2seq sono due pubblicati contemporaneamente nel 2014.
Un modello da 380M parametri per la traduzione automatica utilizzava due memorie a lungo e breve termine (LSTM). La sua architettura è composta da due parti. Il codificatore è un LSTM che accetta una sequenza di token e la trasforma in un vettore. Il decodificatore è un altro LSTM che converte il vettore in una sequenza di token. Al contrario, un altro modello da 130M parametri impiegava le unità ricorrenti gated (GRU) invece degli LSTM. Ricerche successive hanno dimostrato che i GRU offrono le stesse prestazioni degli LSTM all'interno dell'architettura seq2seq.
Questi primi modelli seq2seq non avevano alcun meccanismo di attenzione e il vettore di stato era accessibile solo dopo l'elaborazione dell'ultima parola del testo sorgente. Sebbene in teoria un tale vettore conservi le informazioni sull'intera frase originale, in pratica tali informazioni si conservavano soltanto in minima parte. Ciò accadeva perché l'input veniva elaborato in sequenza da una rete ricorrente sotto forma di un vettore di output di dimensione fissa, che poi era rielaborato da un'altra rete ricorrente per restituire l'output. Se l'input è lungo, il vettore di output non sarà in grado di contenere tutte le informazioni rilevanti contenute nella sequenza. A dimostrazione, invertire la frase di input migliorava le traduzioni da sequenza a sequenza.
Il modello RNNsearch ha introdotto un meccanismo di attenzione a seq2seq per la traduzione automatica al fine di risolvere il problema del collo di bottiglia (del vettore di output di dimensione fissa), consentendo al modello di elaborare più facilmente le dipendenze a lunga distanza. Il nome è dovuto al fatto che "emula la ricerca in una frase sorgente durante la decodifica di una traduzione".
Si è proceduto in seguito al confronto delle prestazioni relative tra le architetture del modello di attenzione globale (quello di RNNsearch) e locale (a finestra scorrevole) per la traduzione automatica, rivelando che l'attenzione mista aveva una qualità superiore rispetto all'attenzione globale, mentre l'attenzione locale aveva un effetto positivo sui tempi di traduzione.
Nel 2016, Google Translate è stato rinnovato e ha preso il nome di Google Neural Machine Translation, sostituendo il modello precedente basato sulla traduzione automatica statistica . Il nuovo modello era un modello seq2seq in cui il codificatore e il decodificatore erano entrambi costituiti da 8 strati di LSTM bidirezionale. Ci sono voluti nove mesi per svilupparlo e ha superato le prestazioni dell’approccio statistico, che aveva richiesto dieci anni per essere realizzato.

### Parallelizzare l'attenzione

I modelli Seq2seq con attenzione (inclusa l'auto-attenzione) soffrivano ancora dello stesso problema delle reti ricorrenti, ovvero erano difficili da parallelizzare, il che impediva la loro accelerazione sulle GPU. Nel 2016, l'attenzione scomponibile che applicava un meccanismo di auto-attenzione alle reti feedforward - che sono facili da parallelizzare - e ha ottenuto il risultato SOTA nell'implicazione testuale con un numero di parametri inferiori rispetto alle LSTM. Uno dei suoi autori, Jakob Uszkoreit, era convinto che l'attenzione senza ricorrenza fosse sufficiente per la traduzione linguistica, da qui il titolo "l'attenzione è tutto ciò di cui hai bisogno". Questa ipotesi andava in contrasto alle credenze convenzionali dell'epoca, e perfino suo padre, un noto linguista computazionale, di dichiarò scettico. Nello stesso anno, l'auto-attenzione (chiamata intra-attenzione o attenzione intra-frase) è stata proposta per i LSTM.
Nel 2017, il modello originale del trasformer codificatore-decodificatore (di dimensioni pari a 100 M) è stato proposto nel documento " Attention is all you need ". All'epoca, l'attenzione della ricerca era rivolta al miglioramento di seq2seq per la traduzione automatica, al fine di elaborare tutti i token in parallelo, pur cercando di mantenere il meccanismo di attenzione del prodotto scalare per non limitarne le prestazioni nell'elaborazione testuale. Ciò ha portato all'introduzione di un modello di attenzione multi-testa più facile da parallelizzare grazie all'uso di teste indipendenti e alla mancanza di ricorrenza. La parallelizzabilità è stata un fattore importante per la diffusione di questa architettura nelle grandi reti neurali.

### Il grande boom dell'intelligenza artificiale

Già nella primavera del 2017, prima ancora che fosse pubblicato in prestampa "Attention is all you need", uno dei coautori aveva applicato la variante "decoder-only" dell'architettura per generare articoli fittizi di Wikipedia. L'architettura del transformer è oggi utilizzata in molti modelli generativi che contribuiscono all'attuale boom dell'intelligenza artificiale .
Nella modellazione del linguaggio, ELMo (2018) è un LSTM bidirezionale che produce incorporamenti di parole contestualizzati, migliorando la linea di ricerca di bag of words e word2vec. È stato seguito da BERT (2018), un modello di trasformatore dotato unicamente di encoder. Nell'ottobre 2019, Google ha iniziato a utilizzare BERT per elaborare le query di ricerca. Nel 2020, Google Translate ha sostituito il precedente modello RNN-encoder–RNN-decoder con un modello Transformer-encoder–RNN-decoder.
A partire dal 2018, la serie OpenAI GPT di Transformers esclusivamente decodificatori è diventata lo stato dell'arte nella generazione del linguaggio naturale . Nel 2022, un chatbot basato su GPT-3, ChatGPT, è diventato inaspettatamente popolare, innescando un boom attorno ai grandi modelli linguistici.
Dal 2020, i Transformers sono stati applicati in modalità che vanno oltre il testo, tra cui il trasformatore visivo, il riconoscimento vocale, la robotica, e il multimodale. Il trasformatore applicato alla visione, a sua volta, ha stimolato nuovi sviluppi nelle reti neurali convoluzionali. I generatori di immagini e video come DALL-E (2021), Stable Diffusion 3 (2024), e Sora (2024), si basano sull'architettura Transformer.

## Addestramento
### Metodi per stabilizzare l'allenamento

Nel documento originale gli autori raccomandavano di utilizzare il "riscaldamento del tasso di apprendimento" (o "learning rate warmup"). Ciò significa che il tasso di apprendimento dovrebbe aumentare linearmente da 0 al valore massimo per la prima parte dell'addestramento (di solito si consiglia che sia pari al 2% del numero totale di passaggi - steps - impiegati per l'addestramento), prima di diminuire nuovamente.
Uno studio del 2020 ha scoperto che l'utilizzo della normalizzazione degli strati prima (invece che dopo) dell'attenzione multitesta e degli strati di feedforward stabilizza l'addestramento, senza richiedere l'aumento lineare del tasso di apprendimento.

### Pre-addestramento e finetuning

In genere, i trasformatori vengono prima preaddestrati tramite apprendimento autosupervisionato su un ampio set di dati generico, seguito da una messa a punto supervisionata su un piccolo set di dati specifico per un'attività. Il set di dati pre-addestramento è in genere un corpus di grandi dimensioni non etichettato, come ad esempio The Pile . Le attività di pre-addestramento e messa a punto includono comunemente:
* modellazione del linguaggio
* previsione della frase successiva
* domanda risposta
* comprensione della lettura
* analisi del sentimento
* parafrasi
Il rapporto sul trasformatore T5 documenta un gran numero di attività di pre-addestramento del linguaggio naturale. Alcuni esempi sono:
* ripristinare o riparare testi incompleti o danneggiati. Ad esempio, l'input "GrazieTemplate:Nnbspio alla tua festaTemplate:Nnbspsettimana", potrebbe generare l'output "Grazie per avermi invitato alla tua festa la settimana scorsa ".
* traduzione tra lingue naturali ( traduzione automatica)
* giudicare la validità pragmatica del linguaggio naturale. Ad esempio, la seguente frase potrebbe essere giudicata "non accettabile", perché, nonostante sia sintatticamente ben formata, è improbabile nell'uso umano ordinario: Il percorso sta saltando bene.
Si noti che, sebbene ciascuno di questi compiti sia banale o ovvio per i madrelingua umani della lingua (o delle lingue), in genere la loro simulazione si è rivelata impegnativa per le precedenti generazioni di architetture di apprendimento automatico.

### Compiti (Tasks)

In generale, ci sono 3 tipologie di attività che si applicano alla modellazione del linguaggio: "mascheramento", "autoregressione", e "prefixLM". Queste classi non dipendono da un'architettura di modellazione specifica come Transformer, ma vengono spesso discusse nel contesto dei Transformer.
In un compito di mascheramento, uno o più token vengono mascherati e il modello produce una distribuzione di probabilità che prevede quali sono i dati mancanti in base al contesto. La funzione di perdita per questo compito è in genere la somma delle perplessità logaritmiche dei token mascherati: ${\text{Perdita}}=-\sum _{t\in {\text{token mascherati}}}\ln({\text{probabilita' di }}t{\text{ in base al contesto}})$ e il modello viene addestrato per minimizzare questa funzione di perdita. La serie di modelli BERT è addestrata per la previsione di token mascherati e per un secondo compito.
In un compito autoregressivo, l'intera sequenza viene inizialmente mascherata e il modello produce una distribuzione di probabilità per il primo token. Quindi viene rivelato il primo token e il modello prevede il secondo token, e così via. La funzione di perdita rimane la stessa durante l'intera attività. La serie di modelli GPT viene addestrata tramite attività autoregressive.
In un compito prefixLM, la sequenza è divisa in due parti. La prima parte viene presentata come contesto e il modello prevede il primo token della seconda parte. Appena quest'ultimo viene rivelato il modello predice il secondo token e così via. Anche in questo caso la funzione di perdita rimane invariata. La serie di modelli T5 viene addestrata tramite attività di prefixLM.
Si noti che "masked" come in "masked language modelling" non è "masked" come in " masked attention ", e "prefixLM" (prefix language modeling) non è "prefixLM" (prefix language model).

## Funzionamento pratico

In quanto modello probabilistico, il trasformatore deve il suo funzionamento a numerose formule, appartenenti a diverse branche della matematica. Questa sezione affronta il funzionamento pratico in sintesi. Per una comprensione più tecnica, leggere il paragrafo seguente.
Per comprendere la matematica dei trasformatori, è necessaria una conoscenza di:
* algebra lineare (vettori, matrici, operazioni)
* probabilità
Come molti modelli di machine learning, i trasformatori funzionano grazie a una rete neurale. Una rete neurale è un modello matematico composto da unità chiamate neuroni. I neuroni sono organizzati in strati (o livelli, in inglese *layers*). In generale, un modello di intelligenza artificiale sfrutta tre tipi di strati:
* Strato di input: riceve i dati in ingresso.
* Strati nascosti: elaborano i dati.
* Strato di output: fornisce il risultato.
Ogni neurone artificiale riceve uno o più input, applica un peso a ciascuno di essi, somma i valori e passa il risultato attraverso una funzione di attivazione per produrre un output.
In generale, la formula base per un singolo neurone è:
    $y=f\left(\sum _{i=1}^{n}w_{i}x_{i}+b\right)$
Dove:
* y è l'output del neurone.
* f è la funzione di attivazione.
* w_i sono i pesi associati agli input x_i.
* b è il bias.
Non tutti i neuroni si attivano contemporaneamente: la funzione di attivazione decide se un neurone deve attivarsi o meno, trasmettendo o bloccando l'output agli strati successivi.
Il passaggio dei dati all'interno della rete prende il nome di propagazione. Esistono due tipi principali di propagazione:
* Propagazione in avanti (*forward propagation*).
* Propagazione indietro (*backpropagation*).

### Propagazione in avanti

Durante la propagazione in avanti:
* Gli input vengono moltiplicati per i pesi e sommati, includendo il bias.
* Il risultato viene passato attraverso la funzione di attivazione.
* L'output di ciascun neurone viene inviato agli altri neuroni negli strati successivi.
Per valutare l'efficienza della rete si utilizza la funzione di perdita (*loss function*), che misura quanto l'output della rete neurale si discosti dal valore desiderato. Durante l'allenamento, l'obiettivo è minimizzare la funzione di perdita, affinché la rete migliori le sue previsioni.
Esempi comuni di funzioni di perdita includono:
* Errore quadratico medio (MSE): Utilizzato per problemi di regressione, misura la differenza media al quadrato tra il valore predetto e il valore reale.
    ${\text{MSE}}={\frac {1}{n}}\sum _{i=1}^{n}\left(y_{i}-{\hat {y}}_{i}\right)^{2}$

### Propagazione indietro

La retropropagazione (*backpropagation*) è l'algoritmo principale per allenare le reti neurali. Consiste nel calcolare l'errore dell'output e nel propagare questo errore all'indietro attraverso la rete, aggiornando i pesi dei neuroni. Questo processo viene ripetuto più volte per migliorare l'accuratezza del modello.
Durante la propagazione indietro:
* Si calcola l'errore dell'output.
* L'errore viene propagato all'indietro, calcolando i gradienti dei pesi.
* I pesi vengono aggiornati utilizzando l'algoritmo di discesa del gradiente.

### Discesa del gradiente

La discesa del gradiente è un algoritmo di ottimizzazione utilizzato per aggiornare i pesi della rete neurale durante l'allenamento. L'obiettivo è trovare i valori ottimali dei pesi che minimizzano la funzione di perdita.
Si parte da un valore iniziale casuale per i pesi e si aggiornano iterativamente in direzione del gradiente negativo della funzione di perdita:
    $w=w-\eta \cdot \nabla _{w}L(w)$
Dove:
* w sono i pesi.
* \eta è il tasso di apprendimento (*learning rate*).
* \nabla_w L(w) è il gradiente della funzione di perdita rispetto ai pesi.
Attraverso l'aggiornamento iterativo dei pesi, la rete diventa progressivamente più precisa nelle sue previsioni.

## Architettura

Nonostante le numerose varianti e adattamenti dell'architettura Transformer, come GPT, BERT o T5, tutti questi modelli condividono la stessa struttura di base. Al centro di ogni Transformer c'è il meccanismo di attenzione, che permette di elaborare sequenze in parallelo, catturando le relazioni tra gli elementi indipendentemente dalla loro distanza nella sequenza. Sebbene ciascuna variante modifichi alcuni aspetti, come il modo in cui gestiscono l'input o la direzione della generazione (ad esempio, autoregressiva o bidirezionale), mantengono la stessa architettura fondamentale, che li rende estremamente potenti ed efficienti per una vasta gamma di compiti nel trattamento del linguaggio naturale e oltre.
Tutti i trasformatori hanno gli stessi componenti primari:
* Tokenizzatori, che convertono il testo in token.
* Livello di embedding, che converte i token e le posizioni dei token in rappresentazioni vettoriali.
* Livelli trasformatori, che eseguono trasformazioni ripetute sulle rappresentazioni vettoriali, estraendo sempre più informazioni linguistiche. Sono costituiti da strati alternati di attenzione e di feedforward. Esistono due tipi principali di strati di trasformatori: la tipologia di codifica e quella di decodifica, con ulteriori varianti.
* Livello di annullamento dell'incorporamento, che riconverte le rappresentazioni vettoriali finali in una distribuzione di probabilità sui token.
La seguente descrizione ricalca esattamente il Transformer descritto nel documento originale. Esistono delle varianti, descritte nella sezione seguente .
Per convenzione, scriviamo tutti i vettori come vettori riga. Ciò, ad esempio, significa che spingere un vettore attraverso uno strato lineare significa moltiplicarlo per una matrice di pesi sulla destra, come $xW$.

### Tokenizzazione

Poiché l'architettura Transformer elabora in modo nativo dati numerici, non testo, è necessaria una traduzione tra testo e token. Un token è un numero intero che rappresenta un carattere o un breve segmento di caratteri. Sul lato di input, il testo di input viene analizzato in una sequenza di token. Allo stesso modo, sul lato output, i token di output vengono riconvertiti in testo. Il modulo che esegue la conversione tra testi e sequenze di token è appunto il tokenizzatore.
L'insieme di tutti i token è il vocabolario del tokenizzatore e la sua dimensione è la dimensione del vocabolario $n_{\text{vocabulary}}$. Quando ci si trova di fronte a token esterni al vocabolario (generalmente a causa di caratteri speciali, errori di decodifica o comportamenti anomali da parte del modello), in genere si usa un token speciale, scritto come "[UNK]" per "sconosciuto" (unknown).
Alcuni tokenizzatori comunemente utilizzati sono la codifica a coppie di byte, WordPiece e SentencePiece.

### Incorporamento

Ogni token viene convertito in un vettore di incorporamento tramite una tabella di ricerca. Un incorporamento è una rappresentazione numerica densa di un elemento, come una parola o un token, in uno spazio vettoriale continuo. In altre parole, per ottenere l'incorporamento di un token, si prende la sua rappresentazione one-hot (costituita da un vettore composto da un solo "1" nella posizione corrispondente al token) e la si moltiplica per una matrice di incorporamento . Il risultato è un vettore denso che rappresenta il token nello spazio degli incorporamenti. Ad esempio, se il token di input è $3$, allora la rappresentazione one-hot è $[0,0,0,1,0,0,\dots ]$, e il suo vettore di incorporamento è $\mathrm {Embed} (3)=[0,0,0,1,0,0,\dots ]M$I vettori di incorporamento del token vengono sommati ai rispettivi vettori di codifica posizionale (vedere sotto), producendo la sequenza di vettori di input.
Il numero di dimensioni in un vettore di incorporamento è chiamato dimensione nascosta o dimensione di incorporamento e scritto come $d_{\text{emb}}$

### Disincorporamento

Uno strato di non incorporamento è quasi l'inverso di uno strato di incorporamento. Mentre uno strato di incorporamento converte un token in un vettore, uno strato di non incorporamento converte un vettore in una distribuzione di probabilità sui token.
Lo strato di scomposizione è uno strato softmax lineare: $\mathrm {UnEmbed} (x)=\mathrm {softmax} (xW+b)$ La matrice ha forma $(d_{\text{emb}},n_{\text{vocabolario}})$. La matrice di incorporamento $M$ e la matrice di disincorporamento $W$ a volte devono essere l'una la trasposizione dell'altra, questa pratica prende il nome di legatura con peso.

### Codifica posizionale

Una codifica posizionale è una rappresentazione vettoriale di dimensione fissa delle posizioni relative dei token all'interno di una sequenza: fornisce al modello del trasformatore informazioni su dove si trovano le parole nella sequenza di input. Senza la codifica posizionale, il modello non sarebbe in grado di elaborare la sequenza di input come qualcosa di più di un insieme di parole; ad esempio, sia " uomo morde cane " che "cane morde uomo" verrebbero elaborati esattamente nello stesso modo.
La codifica posizionale è definita come una funzione di tipo $f:\mathbb {R} \to \mathbb {R} ^{d};d\in \mathbb {Z} ,d>0$, Dove $d$ è un numero intero positivo pari. La codifica posizionale completa definita nel documento originale è: $(f(t)_{2k},f(t)_{2k+1})=(\sin(\theta ),\cos(\theta ))\quad \forall k\in \{0,1,\ldots ,d/2-1\}$ Dove $\theta ={\frac {t}{r^{k}}},r=N^{2/d}$.
Qui, $N$ è un parametro libero che dovrebbe essere significativamente più grande del più grande $k$ che verrebbe inserito nella funzione di codifica posizionale. Il documento originale utilizza $N=10000$.
La funzione è in una forma più semplice quando scritta come una funzione complessa di tipo $f:\mathbb {R} \to \mathbb {C} ^{d/2}$$f(t)=\left(e^{it/r^{k}}\right)_{k=0,1,\ldots ,{\frac {d}{2}}-1}$ Dove $r=N^{2/d}$.
Il motivo principale per cui si utilizza questa funzione di codifica posizionale è che, utilizzandola, gli spostamenti sono trasformazioni lineari: $f(t+\Delta t)=\mathrm {diag} (f(\Delta t))f(t)$ Dove $\Delta t\in \mathbb {R}$ è la distanza che si desidera spostare. Ciò consente al trasformatore di assumere qualsiasi posizione codificata e di trovare la codifica della posizione n passi avanti o n passi indietro, mediante una moltiplicazione di matrici.
Prendendo una somma lineare, qualsiasi convoluzione può anche essere implementata come trasformazioni lineari: $\sum _{j}c_{j}f(t+\Delta t_{j})=\left(\sum _{j}c_{j}\,\mathrm {diag} (f(\Delta t_{j}))\right)f(t)$ per qualsiasi costante $c_{j}$. Ciò consente al trasformatore di prendere qualsiasi posizione codificata e di trovare una somma lineare delle posizioni codificate dei suoi vicini. Questa somma di posizioni codificate, una volta inserita nel meccanismo di attenzione, creerebbe pesi di attenzione sui suoi vicini, in modo molto simile a quanto avviene in un modello linguistico di rete neurale convoluzionale. Come afferma l'autore, "abbiamo ipotizzato che ciò avrebbe consentito al modello di imparare facilmente a prestare attenzione in base alla posizione relativa".
Nelle implementazioni tipiche, tutte le operazioni vengono eseguite sui numeri reali, non sui numeri complessi, ma poiché la moltiplicazione complessa può essere implementata come una moltiplicazione di matrici reali 2 per 2, questa è una mera differenza di notazionalità.

### Encoder-decoder (panoramica)

Come i precedenti modelli seq2seq, il modello originale del trasformatore utilizzava un'architettura codificatore-decodificatore. Il codificatore è costituito da livelli di codifica che elaborano tutti i token di input insieme, uno strato dopo l'altro, mentre il decodificatore è costituito da livelli di decodifica che elaborano iterativamente l'output del codificatore e i token di output man mano che vengono generati.
Lo scopo di ogni livello di codifica è quello di creare rappresentazioni contestualizzate dei token, in cui ogni rappresentazione corrisponde a un token che "mescola" le informazioni provenienti da altri token di input tramite un meccanismo di auto-attenzione. Ogni livello di decodifica contiene due sottolivelli di attenzione: (1) attenzione incrociata per incorporare l'output del codificatore (rappresentazioni di token di input contestualizzate) e (2) auto-attenzione per "mescolare" le informazioni tra i token di input al decodificatore (vale a dire i token generati finora durante il tempo di inferenza).
Sia gli strati del codificatore che del decodificatore hanno una rete neurale feed-forward per l'elaborazione aggiuntiva dei loro output e contengono connessioni residue e passaggi di normalizzazione degli strati. Questi strati feed-forward contengono la maggior parte dei parametri di un modello di trasformatore.

### Rete di feedforward

Una rete di feedforward è un tipo di rete neurale artificiale in cui i dati fluiscono in una sola direzione, dai nodi di input attraverso uno o più strati nascosti fino ai nodi di output, senza cicli o retroazioni. È utilizzata per apprendere e approssimare funzioni, trasformando input in output attraverso pesi, bias e funzioni di attivazione.
I moduli di rete feedforward (FFN) in un trasformatore sono perceptron multistrato a 2 strati: $\mathrm {FFN} (x)=\phi (xW^{(1)}+b^{(1)})W^{(2)}+b^{(2)}$ Dove $\phi$ è la sua funzione di attivazione. Il Transformer originale utilizzava l'attivazione ReLU.
Il numero di neuroni nello strato intermedio è chiamato dimensione intermedia (GPT), dimensione del filtro (BERT), o dimensione feedforward (BERT). Di solito è più grande della dimensione di incorporamento. Ad esempio, sia nella serie GPT-2 che nella serie BERT, la dimensione intermedia di un modello è 4 volte la sua dimensione di incorporamento: $d_{\text{ffn}}=4d_{\text{emb}}$.

### Attenzione al prodotto scalare
#### Testa di attenzione

Il meccanismo di attenzione utilizzato nell'architettura Transformer è costituito da unità di attenzione basate su prodotti scalari. Per ogni unità, il modello del trasformatore apprende tre matrici di pesi: i pesi della query $W^{Q}$, i pesi chiave $W^{K}$ e i pesi del valore $W^{V}$.
Il modulo accetta tre sequenze: una sequenza di query, una sequenza di chiavi e una sequenza di valori. La sequenza di query è una sequenza di lunghezza $\ell _{\text{seq, query}}$, e ogni voce è un vettore di dimensione $d_{\text{emb, query}}$. Lo stesso vale per le sequenze chiave e valore.
Ogni vettore $x_{i,{\text{query}}}$ nella sequenza di query, viene moltiplicato per una matrice $W^{Q}$ per produrre un vettore di query $q_{i}=x_{i,{\text{query}}}W^{Q}$. La matrice di tutti i vettori di query è la matrice di query: $Q=X_{\text{query}}W^{Q}$ Allo stesso modo, costruiamo la matrice chiave $K=X_{\text{key}}W^{K}$ e la matrice dei valori $V=X_{\text{value}}W^{V}$.
Di solito è il caso in cui tutti $W^{Q},W^{K},W^{V}$ sono matrici quadrate, ovvero $d_{\text{emb, query}}=d_{\text{query}}$, ecc.
I pesi del meccanismo di attenzione vengono calcolati utilizzando la query e i vettori chiave: il peso $a_{ij}$ dal token $i$ per il token $j$ è il prodotto scalare tra $q_{i}$ E $k_{j}$. I pesi vengono divisi per la radice quadrata della dimensione dei vettori chiave, ${\sqrt {d_{k}}}$, che stabilizza i gradienti durante l'allenamento, e passa attraverso un softmax che normalizza i pesi. Il fatto che $W^{Q}$ E $W^{K}$ sono matrici diverse consentono all'attenzione di essere non simmetrica: se il token $i$ si occupa del token $j$ (cioè $q_{i}\cdot k_{j}$ è grande), questo non significa necessariamente che il token $j$ si occuperà del token $i$ (cioè $q_{j}\cdot k_{i}$ potrebbe essere piccolo). L'output dell'unità di attenzione per il token $i$ è la somma ponderata dei vettori di valore di tutti i token, ponderati in base a $a_{ij}$, e all'attenzione dal token $i$ per ogni token.
Il calcolo dell'attenzione per tutti i token può essere espresso come un unico grande calcolo di matrice utilizzando la funzione softmax, utile per l'addestramento grazie alle ottimizzazioni delle operazioni di matrice computazionale che calcolano rapidamente le operazioni di matrice. Le matrici $Q$, $K$ E $V$ sono definite come le matrici in cui le $i$ righe sono vettori: $q_{i}$, $k_{i}$, E $v_{i}$ rispettivamente. Quindi possiamo rappresentare l'attenzione come ${\begin{aligned}{\text{Attenzione}}(Q,K,V)={\text{softmax}}\left({\frac {QK^{\mathrm {T} }}{\sqrt {d_{k}}}}\right)V\end{aligned}}$
dove il softmax viene applicato a ciascuna riga della matrice.
Il numero di dimensioni in un vettore di query è la dimensione della query $d_{\text{query}}$ e similmente per la dimensione della chiave $d_{\text{key}}$ e dimensione del valore $d_{\text{value}}$. La dimensione di output di una testa di attenzione è la dimensione della sua testa $d_{\text{head}}$. Il meccanismo dell'attenzione richiede che siano soddisfatte le seguenti tre uguaglianze: $\ell _{\text{seq, key}}=\ell _{\text{seq, value}},\;d_{\text{query}}=d_{\text{key}},\;d_{\text{value}}=d_{\text{head}}$ ma per il resto non ha vincoli.
Se la testa dell'attenzione viene utilizzata in modo auto-attentivo, allora $X_{\text{query}}=X_{\text{key}}=X_{\text{value}}$. Se la testa dell'attenzione viene utilizzata in modo incrociato, allora di solito $X_{\text{query}}\neq X_{\text{key}}=X_{\text{value}}$. In teoria è possibile che tutti e tre siano diversi, ma nella pratica ciò raramente accade.

#### Attenzione multitesta

Un set di matrici $\left(W^{Q},W^{K},W^{V}\right)$ è detto testa di attenzione e ogni strato in un modello di trasformatore ha più teste di attenzione. Mentre ogni attenzione si concentra sui token che sono rilevanti per ogni token, più teste consentono al modello di fare lo stesso per diverse definizioni di "rilevanza". In particolare, le matrici di query e proiezione delle chiavi, $W^{Q}$ E $W^{K}$, che sono coinvolte nel calcolo del punteggio di attenzione, definiscono la "rilevanza". Nel frattempo, la matrice di proiezione del valore $W^{V}$, in combinazione con la parte della matrice di proiezione di output $W^{O}$, determina in che modo i token presidiati influenzano le informazioni che vengono passate ai livelli successivi e, in ultima analisi, i logit di output. Inoltre, l'ambito dell'attenzione, ovvero la gamma di relazioni tra token catturate da ciascuna testa attentiva, può espandersi man mano che i token attraversano strati successivi. Ciò consente al modello di catturare dipendenze più complesse e a lungo raggio negli strati più profondi. Molte teste di attenzione dei trasformatori codificano relazioni di pertinenza che sono significative per gli esseri umani. Ad esempio, alcuni centri di attenzione possono concentrarsi principalmente sulla parola successiva, mentre altri si concentrano principalmente sui verbi e sui loro oggetti diretti. I calcoli per ogni punto di attenzione possono essere eseguiti in parallelo, il che consente un'elaborazione rapida. Gli output per il livello di attenzione vengono concatenati per passare ai livelli di rete neurale feed-forward.
Concretamente, lasciamo che le teste di attenzione siano indicizzate da $i$, allora abbiamo ${\text{Testemultiple}}(Q,K,V)={\text{Concat}}_{i\in [n_{\text{heads}}]}({\text{Attenzione}}(XW_{i}^{Q},XW_{i}^{K},XW_{i}^{V}))W^{O}$ dove la matrice $X$ è la concatenazione degli incorporamenti delle parole (embeddings) e le matrici $W_{i}^{Q},W_{i}^{K},W_{i}^{V}$ sono "matrici di proiezione" possedute dalla testa di attenzione individuale $i$, E $W^{O}$ è una matrice di proiezione finale posseduta dall'intera testa attentiva multi-testa.
È teoricamente possibile che ogni testa dell'attenzione abbia una dimensione di testa diversa $d_{\text{head}}$, ma nella pratica ciò accade raramente.
Ad esempio, nel modello GPT-2 più piccolo sono presenti solo meccanismi di auto-attenzione. Ha le seguenti dimensioni: $d_{\text{emb}}=768,n_{\text{head}}=12,d_{\text{head}}=64$ Da $12\times 64=768$, la sua matrice di proiezione di output $W^{O}\in \mathbb {R} ^{(12\times 64)\times 768}$ è una matrice quadrata.

#### Attenzione mascherata

Potrebbe essere necessario tagliare i collegamenti di attenzione tra alcune coppie di parole. Ad esempio, il decoder, quando decodifica la posizione del token $t$, non dovrebbe avere accesso al token in posizione $t+1$. Ciò può essere ottenuto prima della fase softmax aggiungendo una matrice di maschera $M$ questo è $-\infty$ alle voci in cui il collegamento di attenzione deve essere tagliato, e $0$ in altri luoghi: ${\begin{aligned}{\text{AttenzioneMascherata}}(Q,K,V)={\text{softmax}}\left(M+{\frac {QK^{\mathrm {T} }}{\sqrt {d_{k}}}}\right)V\end{aligned}}$Un modulo di attenzione non mascherato può essere pensato come un modulo di attenzione mascherato in cui la maschera ha tutte le voci pari a zero.
Ad esempio, la seguente matrice è comunemente utilizzata nei moduli di auto-attenzione del decoder, chiamata "mascheramento causale": $M_{\text{causale}}={\begin{bmatrix}0&-\infty &-\infty &\dots &-\infty \\0&0&-\infty &\dots &-\infty \\0&0&0&\dots &-\infty \\\vdots &\vdots &\vdots &\ddots &\vdots \\0&0&0&\dots &0\end{bmatrix}}$ In parole povere, significa che ogni token può prestare attenzione a se stesso e a tutti i token precedenti, ma non a quelli successivi. Come esempio di un uso non comune della matrice maschera, XLNet considera tutte le maschere della forma $PM_{\text{causale}}P^{-1}$, Dove $P$ è una matrice di permutazione casuale.

### Codificatore

Un codificatore è costituito da uno strato di incorporamento, seguito da più strati di codificatore.
Ogni livello del codificatore è costituito da due componenti principali: un meccanismo di auto-attenzione e uno strato di feed-forward. Accetta un input come sequenza di vettori di input, applica il meccanismo di autoattenzione per produrre una sequenza intermedia di vettori, quindi applica lo strato feed-forward per ogni vettore individualmente. Schematicamente abbiamo: ${\begin{aligned}{\text{vettori input }}&h_{0},h_{1},\dots \\{\text{vettori combinati come matrice }}H&={\begin{bmatrix}h_{0}\\h_{1}\\\vdots \end{bmatrix}}\\{\text{EncoderLayer}}(H)&={\begin{bmatrix}{\text{FFN}}({\text{Attenzione-multitesta}}(H,H,H)_{0})\\{\text{FFN}}({\text{Attenzione-multitesta}}(H,H,H)_{1})\\\vdots \end{bmatrix}}\\\end{aligned}}$
Dove ${\text{FFN}}$ sta per "rete feed-forward". Possiamo scriverlo più sinteticamente come ${\text{EncoderLayer}}(H)={\text{FFN}}({\text{Attenzione-multitesta}}(H,H,H))$ con la convenzione implicita che il ${\text{FFN}}$ viene applicato individualmente a ciascuna riga della matrice.
Gli strati del codificatore sono impilati. Il primo strato di codifica prende la sequenza di vettori di input dallo strato di incorporamento, producendo una sequenza di vettori. Questa sequenza di vettori viene elaborata dal secondo codificatore e così via. L'output dello strato finale del codificatore viene poi utilizzato dal decodificatore.
Poiché il codificatore elabora l'intero input in una sola volta, ogni token può prestare attenzione a tutti gli altri token (attenzione universale), quindi non c'è bisogno di mascheramento causale.

### Decodificatore

Un decodificatore è costituito da uno strato di incorporamento, seguito da più strati di decodificatore, seguiti da uno strato di non incorporamento.
Ogni decodificatore è costituito da tre componenti principali: un meccanismo di auto-attenzione mascherato causalmente, un meccanismo di attenzione incrociata e una rete neurale feed-forward. Il decoder funziona in modo simile al codificatore, ma viene inserito un meccanismo di attenzione aggiuntivo che estrae le informazioni rilevanti dalle codifiche generate dai codificatori. Questo meccanismo può anche essere chiamato attenzione codificatore-decodificatore.
Come il primo codificatore, il primo decodificatore accetta come input le informazioni posizionali e gli incorporamenti della sequenza di output, anziché le codifiche. Il trasformatore non deve utilizzare l'uscita attuale o futura per prevedere un'uscita, quindi la sequenza di uscita deve essere parzialmente mascherata per impedire questo flusso di informazioni inverso. Ciò consente la generazione di testo autoregressivo. Ai fini della decodifica, l'attenzione generalizzata non è appropriata, perché un token non può prestare attenzione ai token non ancora generati. Pertanto, il modulo di auto-attenzione nel decodificatore è causalmente mascherato.
Al contrario, il meccanismo di attenzione incrociata si occupa dei vettori di output del codificatore, che vengono elaborati prima che il decodificatore inizi la decodifica. Di conseguenza, non c'è bisogno di mascheramento nel meccanismo dell'attenzione incrociata.
Schematicamente abbiamo: ${\begin{aligned}H'&={\text{MaskedMultiheadedAttention}}(H,H,H)\\{\text{DecoderLayer}}(H)&={\text{FFN}}({\text{MultiheadedAttention}}(H',H^{E},H^{E}))\end{aligned}}$ Dove $H^{E}$ è la matrice in cui le righe sono i vettori di output del codificatore.
L'ultimo decodificatore è seguito da un ultimo strato di unembedding. per produrre le probabilità di output sul vocabolario. Quindi, uno dei token viene campionato in base alla probabilità e il decodificatore può essere eseguito nuovamente per produrre il token successivo, ecc., generando in modo autoregressivo il testo di output.

#### In sintesi

L'architettura Transformer funziona elaborando sequenze di dati tramite il meccanismo di self-attention, che valuta l'importanza relativa di ogni elemento rispetto agli altri, consentendo al modello di catturare relazioni contestuali sia a breve che a lungo raggio. Questo approccio, combinato con elaborazione parallela e reti feed-forward, permette al Transformer di superare i limiti delle reti ricorrenti, rendendolo altamente efficiente per compiti complessi come traduzione, generazione di testo e analisi del linguaggio. La sua struttura modulare, composta da encoder e decoder, facilita l'apprendimento e l'applicazione a molteplici domini.
A differenza delle reti sequenziali come le RNN, il Transformer elabora l'intera sequenza contemporaneamente grazie alla parallelizzazione, rendendolo estremamente veloce e scalabile. Il cuore dell'architettura è rappresentato dalle matrici di attenzione, che calcolano quanto ogni elemento della sequenza influisce sugli altri, e dai vettori di embedding che aggiungono informazioni posizionali per mantenere l'ordine dei dati. Questa combinazione consente al modello di gestire lunghe dipendenze nel testo, catturando significati complessi e interazioni profonde tra le parole. Questa efficienza computazionale e la capacità di generalizzazione hanno reso il Transformer la base per i moderni modelli di intelligenza artificiale.