O, come mi piace chiamarla, “attenzione sotto steroidi”.. 💉💊

Photo by Arseny Togulev on Unsplash

No, questo articolo non riguarda la serie americana di film d’azione fantascientifici – non c’è nessun Optimus Prime qui. Non si tratta nemmeno del dispositivo elettrico utilizzato per trasferire energia da un circuito elettrico a un altro. Di cosa si tratta allora, vi chiederete?

Si tratta di uno dei campi più fantascientifici di tutti i tempi, l’Intelligenza Artificiale – in particolare l’Elaborazione del Linguaggio Naturale, che è piuttosto ottimale per il trasferimento di informazioni e viene utilizzato in modo primario. (Vedete cosa ho fatto. :P)

Questo post si basa sul documento: Attention is All You Need. P.S. Gli autori non scherzavano quando hanno scelto questo titolo, perché per questo lavoro avrete bisogno di tutta l’attenzione a vostra disposizione. Ma non lasciatevi spaventare, ne vale davvero la pena!!!

Che cos’è un trasformatore?

Il Transformer in NLP è un’architettura innovativa che mira a risolvere compiti di sequenza-sequenza gestendo con facilità le dipendenze a lungo raggio. Si basa interamente sull’autoattenzione per calcolare le rappresentazioni dei suoi input e output SENZA utilizzare RNN allineate alla sequenza o la convoluzione. 🤯

Se ricordate il mio post precedente, “Capire l’attenzione nel deep learning”, abbiamo discusso come e perché molti modelli non riescono a gestire le dipendenze a lungo raggio. Il concetto di attenzione ci ha permesso in qualche modo di superare questo problema e ora in Transformers ci baseremo sull’attenzione per liberare tutto il suo potenziale.

Poche cose da sapere prima di immergersi in Transformers

Autoattenzione

Cominciamo con il rivedere che cos’è l’attenzione nell’universo della PNL. Comprendere l’attenzione nell’apprendimento profondo. (Mi scuso per questa palese auto-pubblicità, ma dategli seriamente una lettura. Vi aiuterà a capire meglio i Transformers. Lo prometto).

L’attenzione ci ha permesso di concentrarci su parti della nostra sequenza di input mentre predicevamo la nostra sequenza di output. Se il nostro modello ha previsto la parola “rouge” [traduzione francese del colore rosso], è molto probabile che nella nostra sequenza di input ci sia un peso elevato per la parola “rosso”. Quindi l’attenzione, in un certo senso, ci ha permesso di mappare una qualche connessione/correlazione tra la parola di input “rouge” e la parola di output “red”.

L’autoattenzione, a volte chiamata intraattenzione, è un meccanismo di attenzione che mette in relazione diverse posizioni di una singola sequenza per calcolare una rappresentazione della sequenza stessa.

In termini più semplici, l’autoattenzione ci aiuta a creare connessioni simili ma all’interno della stessa frase. Guardate il seguente esempio:

“I poured water from the bottle into the cup until it was full.”
it => cup“I poured water from the bottle into the cup until it was empty.”
it=> bottle

Cambiando una parola “pieno” – > “vuoto” l’oggetto di riferimento per “esso” è cambiato. Se stiamo traducendo una frase di questo tipo, vorremo sapere a quale parola si riferisce “it”.

I tre tipi di attenzione possibili in un modello:

  1. Attenzione tra codificatore e decodificatore: Attenzione tra la sequenza di ingresso e quella di uscita.
  2. Attenzione autonoma nella sequenza di ingresso: Presta attenzione a tutte le parole della sequenza di ingresso.
  3. Autoattenzione nella sequenza di uscita: Un aspetto da tenere presente in questo caso è che la portata dell’autoattenzione è limitata alle parole che si verificano prima di una determinata parola. In questo modo si evitano fughe di informazioni durante l’addestramento del modello. Ciò avviene mascherando le parole che si verificano dopo di essa per ogni passo. Quindi, per il passo 1, solo la prima parola della sequenza di output NON viene mascherata, per il passo 2, le prime due parole NON vengono mascherate e così via.

Chiavi, valori e query:

Le tre parole a caso che ho appena lanciato in questo titolo sono vettori creati come astrazioni utili per calcolare l’autoattenzione; maggiori dettagli su ciascuno di essi sono riportati di seguito. Vengono calcolati moltiplicando il vettore di input (X) con le matrici di peso che vengono apprese durante l’allenamento.

– Vettore Query: q= X * Wq. È la parola corrente.

– Vettore chiave: k= X * Wk. È un meccanismo di indicizzazione per il vettore Value. Simile al modo in cui abbiamo le coppie chiave-valore nelle mappe hash, dove le chiavi sono usate per indicizzare in modo univoco i valori.

– Vettore valore: v= X * Wv. Consideratelo come l’informazione contenuta nella parola in ingresso.

Vogliamo prendere la query q e trovare la chiave k più simile, facendo un prodotto di punti per q e k. Il prodotto query-chiave più vicino avrà il valore più alto, seguito da un softmax che porterà i q.k con valori più piccoli vicino a 0 e i q.k con valori più grandi verso 1. Questa distribuzione softmax viene moltiplicata per v. I vettori di valore moltiplicati per ~1 riceveranno più attenzione, mentre quelli ~0 ne riceveranno meno. Le dimensioni di questi vettori q, k e v sono indicate come “dimensione nascosta” da varie implementazioni.

The values represent the index for q, k and i.

Tutte queste matrici Wq, Wk e Wv vengono apprese durante l’addestramento del modello.

Calcolo dell’autoattenzione da q, k e v:

Formula for self-attention. Source: paper.

Se stiamo calcolando l’autoattenzione per #i parole in ingresso,

– Passo 1: moltiplicare qᵢ per il vettore chiave kⱼ della parola.

– Fase 2: dividere il prodotto per la radice quadrata della dimensione del vettore chiave.

Questo passaggio viene eseguito per migliorare il flusso del gradiente, che è particolarmente importante nei casi in cui il valore del prodotto del punto nel passaggio precedente è troppo grande. L’uso diretto di questi valori potrebbe spingere il softmax in regioni con un flusso di gradiente molto ridotto.

– Fase 3: Una volta ottenuti i punteggi per tutti i j, li passiamo attraverso un softmax. Otteniamo un valore normalizzato per ogni j.

– Fase 4: moltiplicare i punteggi softmax per ogni j con il vettore vᵢ.

L’idea/scopo qui è, con un’attenzione molto simile, di mantenere solo i valori v delle parole in ingresso su cui vogliamo concentrarci moltiplicandoli con i punteggi ad alta probabilità di softmax ~1, e di rimuovere il resto portandoli verso 0, cioè rendendoli molto piccoli moltiplicandoli con i punteggi a bassa probabilità ~0 di softmax.

Calculating output of self attention for the ith input word. If you are looking for an analogy between self attention and attention, think of z serving the purpose of context vectors and not global alignment weights.

Il trasformatore

⚠️ Una parola di cautela: il contenuto di questa immagine può sembrare esponenzialmente più complicato di quanto non sia. Scomporremo questa bestia spaventosa in piccole bestiole e tutto avrà un senso. (Promesso #2)

(left) The Transformer architecture. Source: paper. (right) An abstracted version of the same for better understanding.

Bestia #1: stack di codificatori-decodificatori

Codificatore: L’encoder mappa una sequenza in ingresso di rappresentazioni di simboli (x₁, …, xₙ) in una sequenza di rappresentazioni z = (z₁, …, zₙ). Considerateli come gli output dell’autoattenzione con una certa post-elaborazione.

Ogni codificatore ha due sottolivelli.

  1. Un meccanismo di autoattenzione a più teste sui vettori di ingresso (si pensi a un fratello parallelizzato ed efficiente dell’autoattenzione).
  2. Una semplice rete feed-forward completamente connessa in base alla posizione (si pensi alla post-elaborazione).

Date un’occhiata a questo diagramma 3D di assoluta potenza del blocco encoder utilizzato nel BERT. Davvero, non potete perdervelo!!! È un nuovo livello di comprensione.

Decodificatore: Dato z, il decodificatore genera una sequenza di simboli in uscita (y₁, …, yₘ) un elemento alla volta.

Ogni decodificatore ha tre sottolivelli.

  1. Un meccanismo di autoattenzione multitesta mascherato sui vettori di uscita dell’iterazione precedente.
  2. Un meccanismo di attenzione multitesta sull’uscita del codificatore e un’attenzione multitesta mascherata nel decodificatore.
  3. Una semplice rete feed-forward completamente connessa in senso posizionale (si pensi alla post-elaborazione).

Alcuni punti aggiuntivi:

– Nell’articolo originale, erano presenti 6 strati nella pila dell’encoder (versione a 2 sottolivelli) e 6 nella pila del decoder (versione a 3 sottolivelli).

– Tutti i sottolivelli del modello, così come i livelli di incorporazione, producono uscite della stessa dimensione. Questo per facilitare le connessioni residue.

Bestia #2 All’interno degli stack Encoder-Decoder – Attenzione a più teste:

The three kinds of attention in encoder and decoder stacks along with feed forward neural networks.

Abbiamo appena notato che l’output di ogni sottolivello deve avere la stessa dimensione, che nel nostro documento è di 512 dimensioni.

=> zᵢ deve essere di 512 dimensioni.

=> vᵢ deve avere 512 dimensioni, poiché zᵢ è una sorta di somma pesata di vᵢ.

Inoltre, vogliamo permettere al modello di concentrarsi su posizioni diverse calcolando l’autoattenzione più volte con diverse serie di vettori q, k e v, per poi fare una media di tutti questi risultati e ottenere lo z finale.

Quindi, invece di gestire questi vettori enormi e di fare la media di più risultati, riduciamo la dimensione dei nostri vettori k, q e v a una dimensione più piccola – riducendo anche la dimensione delle matrici Wq, Wk e Wv. Manteniamo gli insiemi multipli (h) di k, q e v e ci riferiamo a ciascun insieme come a una “testa di attenzione”, da cui il nome di attenzione a più teste. Infine, invece di fare la media per ottenere lo z finale, li concateniamo.

La dimensione del vettore concatenato sarà troppo grande per essere inviata al sottolivello successivo, quindi la ridimensioniamo moltiplicandola con un’altra matrice appresa Wo.

(left) Scaled Dot-Product Attention. (right) Multi-Head Attention consists of several attention layers running in parallel. Source: paper.

Le teste di attenzione multiple hanno permesso al modello di partecipare congiuntamente alle informazioni provenienti da diversi sottospazi di rappresentazione in posizioni diverse, cosa che era inibita dalla media in una singola testa di attenzione.

Bestia #3- Pre-elaborazione dell’input e dell’output:

Le parole in ingresso vengono rappresentate utilizzando una forma di embedding. Questo viene fatto sia per il codificatore che per il decodificatore.

L’embedding delle parole di per sé manca di qualsiasi informazione posizionale, che si ottiene nelle RNN in virtù della loro natura sequenziale. Nel caso dell’autoattenzione, invece, a causa del softmax, tale informazione posizionale viene persa.

Per preservare l’informazione posizionale, il trasformatore inietta un vettore nei singoli embedding di ingresso (potrebbe utilizzare embedding di parole per corrispondere alle parole in ingresso). Questi vettori seguono una specifica funzione periodica (esempio: combinazione di vari seni/coseni con frequenza diversa, in breve non sincronizzati tra loro) che il modello apprende ed è in grado di determinare la posizione delle singole parole rispetto alle altre sulla base dei valori.

Questo vettore iniettato è chiamato “codifica posizionale” e viene aggiunto agli embeddings in ingresso alla base di entrambi gli stack di codifica e decodifica.

Bestia #4 – Pila di decodifica: Rivisto

L’uscita della pila di decodifica a ogni passo viene restituita al decodificatore nel passo temporale successivo, in modo piuttosto simile a come le uscite dei passi precedenti nelle RNN vengono utilizzate come stati nascosti successivi. E proprio come abbiamo fatto con gli ingressi del codificatore, incorporiamo e aggiungiamo la codifica posizionale a questi ingressi del decodificatore per preservare la posizione di ogni parola. Questa combinazione di codifica posizionale e incorporazione di parole viene poi inserita in un’autoattenzione mascherata a più teste.

Questo sottolivello di autoattenzione nello stack del decodificatore viene modificato per impedire che le posizioni siano attente a quelle successive: non si possono guardare le parole future. Questo mascheramento assicura che le previsioni per la posizione i possano dipendere solo dalle uscite note nelle posizioni inferiori a i.

Gli output della pila di codificatori vengono quindi utilizzati come serie multiple di vettori chiave k e vettori valore v, per il livello “attenzione del codificatore decodificatore”, indicato in verde nel diagramma. Questo strato aiuta il decodificatore a concentrarsi sulle parti contestualmente rilevanti della sequenza di ingresso per quella fase. (Il vettore q proviene dallo strato “autoattenzione in uscita”.

Una volta ottenuto l’output dal decodificatore, si esegue nuovamente un softmax per selezionare le probabilità finali delle parole.

Conclusione

Concludiamo con una rapida revisione.

– Abbiamo iniziato con il capire cos’è l’autoattenzione e come calcolare l’autoattenzione da questi vettori v, k ,q.

– L’attenzione a più teste è una modifica efficiente dell’autoattenzione che utilizza più insiemi più piccoli di v, k ,q e concatena i risultati di ciascun insieme per ottenere lo z finale.

– Abbiamo poi visto come e dove i tre tipi di autoattenzione vengono utilizzati nel modello.

– Seguono le preelaborazioni effettuate sugli ingressi per gli stack di codifica e decodifica.

Riferimenti e letture consigliate

– Understanding Deep Attention in Deep Learning se avete affrontato problemi di attenzione.

– The Illustrated Transformer – Presenta ottime visualizzazioni insieme alle spiegazioni.

– https://www.analyticsvidhya.com/blog/2019/06/understanding-transformers-nlp-state-of-the-art-models/

– Video YouTube #1: Una grande risorsa per ottenere l’intuizione del modello. Soprattutto se volete saperne di più sulla codifica posizionale.

– Video YouTube #2: Discorso di Lukasz Kaiser su questo documento che spiega l’autoattenzione – . P.S. È uno degli autori di questo documento.

Usa il codice 𝗕𝗟𝗔𝗖𝗞𝗙𝗥𝗜𝗗𝗔𝗬𝟲𝟬 e approfitta del 60% di sconto su tutti i corsi singoli 😉
dai un lancio alla tua carriera con i corsi di dli 🚀