ROFORMER: TRASFORMATORE POTENZIATO CON INTEGRAZIONE IN POSIZIONE ROTATIVA

ROFORMER: TRASFORMATORE POTENZIATO CON INTEGRAZIONE IN POSIZIONE ROTATIVA

I modelli basati su Transformer sono famosi per la loro capacità di analizzare e interpretare testi complessi. Si basano sulla comprensione dell'ordine e del contesto delle parole, compiti in cui i tradizionali metodi di codifica posizionale hanno mostrato i loro limiti. Affrontando questa lacuna, il modello ROFORMER, basato sul Rotary Position Embedding (RoPE), ridefinisce il nostro approccio alla codifica posizionale.

Codifica posizionale tradizionale

I trasformatori trattano il testo come una serie di token e consentono l'elaborazione parallela delle sequenze per una maggiore efficienza. Tuttavia, questa forza ha comportato una sfida: l’agnosticismo intrinseco del modello all’ordine simbolico. La codifica posizionale è stata la risposta, fornendo a ciascun token una firma univoca che denota la sua posizione nella sequenza.

Incorporamenti di posizioni assolute

Inizialmente, modelli come BERT utilizzavano incorporamenti di posizioni assolute, assegnando un vettore fisso a ciascuna posizione in una sequenza. Questo metodo, sebbene semplice, intrinsecamente manca della capacità di adattarsi alle variazioni della lunghezza della sequenza o di enfatizzare le distanze relative tra i token, fondamentali per comprendere molti costrutti linguistici.

Incorporamenti di posizioni relative

Per catturare la natura dinamica del linguaggio, sono stati introdotti gli incorporamenti di posizione relativa, concentrandosi sulla distanza tra i token piuttosto che sulle loro posizioni assolute. Nonostante il loro vantaggio concettuale, questi incorporamenti hanno introdotto complessità computazionale e non sono riusciti a integrarsi perfettamente nel meccanismo di auto-attenzione di Transformers, limitando la loro efficacia.

ROFORMER e incorporamento della posizione rotante

Riconoscendo i limiti delle strategie di codifica posizionale esistenti, ROFORMER introduce Rotary Position Embedding (RoPE), un approccio che combina i vantaggi delle informazioni sulla posizione assoluta e relativa senza i rispettivi svantaggi.

Incorporamento della posizione rotante

RoPE codifica le informazioni sulla posizione utilizzando matrici di rotazione, consentendo al modello di comprendere non solo dove si trova un token, ma come si relaziona con ogni altro token in una sequenza.

Reformer.pngCredit: Original Paper

Funziona attraverso una lente geometrica, trattando le posizioni dei token come punti in uno spazio multidimensionale che vengono ruotati per segnare le loro relazioni sequenziali. Questa rotazione consente al modello di preservare e sfruttare segnali posizionali sia assoluti che relativi all'interno del suo meccanismo di auto-attenzione.

Implementazione del RoPE

L'implementazione di RoPE implica la codifica della posizione di ciascun token in una matrice di rotazione e l'applicazione di questa matrice all'interno del meccanismo di auto-attenzione del Transformer. Questo processo consente un'interpretazione flessibile e dinamica delle informazioni posizionali, adattando lunghezze di sequenza variabili e catturando l'essenza delle interrelazioni dei token senza un significativo sovraccarico computazionale.

Per prima cosa avrai bisogno di una funzione per generare gli incorporamenti rotanti, quindi integrerai questi incorporamenti nel tuo modello. L'esempio seguente presuppone che tu abbia familiarità con la creazione di livelli personalizzati in Keras.

Passaggio 1: definire la funzione di incorporamento rotatorio

Questa funzione genera gli incorporamenti rotanti data la lunghezza massima della sequenza e la dimensionalità degli incorporamenti.

from tensorflow.keras.layers import Layer
import numpy as np

def get_rotary_embedding(dim, max_seq_len):
    inv_freq = 1.0 / (10000 ** (tf.range(0, dim, 2, dtype=tf.float32) / dim))
    t = tf.range(max_seq_len, dtype=tf.float32)
    freqs = tf.einsum('i,j->ij', t, inv_freq)
    emb = tf.concat((tf.cos(freqs), tf.sin(freqs)), axis=-1)
    return emb

inv_freq = 1.0 / (10000 ** (tf.range(0, dim, 2, dtype=tf.float32) / dim))

Questa linea calcola l'inverso delle frequenze scalate esponenzialmente in base agli indici di posizione. Queste frequenze vengono utilizzate per generare modelli sinusoidali per incorporamenti rotanti, che aiutano a codificare le relative informazioni posizionali in sequenze. Questo meccanismo è particolarmente utile in attività in cui la comprensione dell'ordine e del posizionamento relativo degli elementi è cruciale, come nell'elaborazione del linguaggio naturale o nell'analisi delle serie temporali.

Nei dettagli:

  • tf.range(0, dim, 2, dtype=tf.float32) crea un intervallo di valori a partire da 0 fino a dim (esclusivo), incrementando di 2. L'argomento dtype=tf.float32 specifica che gli elementi di questo tensore sono numeri in virgola mobile a 32 bit. Se "dim" è 8, ad esempio, ciò produrrebbe "[0, 2, 4, 6]".

  • Il tensore prodotto da tf.range viene quindi diviso per la dimensionalità ("dim") degli incastri. Questa operazione ridimensiona questi indici fino a un intervallo compreso tra 0 e 1 (esclusivo se dim è pari, leggermente inclusivo se dim è dispari, poiché il passaggio dell'intervallo salta ogni altro valore). Continuando l'esempio con "dim" = 8, dividendo per 8 si ottiene "[0.0, 0.25, 0.5, 0.75]".

  • L'operazione 10000 ** (...) eleva 10.000 alla potenza di ciascun elemento nel tensore precedentemente scalato. La base di 10.000 è alquanto arbitraria, ma viene scelta per garantire che le frequenze varino in un ampio intervallo, il che aiuta il modello a distinguere tra diverse posizioni in modo più efficace. Per "[0.0, 0.25, 0.5, 0.75]", applicherebbe l'operazione di potenza a ciascuno, ottenendo valori molto più grandi per gli elementi più alti.

  • Infine, la frequenza inversa si ottiene prendendo il reciproco (1/x) dei valori del passaggio precedente. Le frequenze inverse sono più piccole per gli indici più alti, il che significa che gli elementi più avanti nella sequenza avranno frequenze più piccole, influenzando il modo in cui le loro posizioni sono codificate nel modello. Ciò consente agli incorporamenti di ridimensionarsi in modo tale da poter dedurre le posizioni relative attraverso i meccanismi di attenzione del modello.

La linea:

freqs = tf.einsum('i,j->ij', t, inv_freq)

utilizza la funzione "tf.einsum" di TensorFlow, uno strumento che consente un'espressione concisa ed efficiente delle operazioni tensoriali utilizzando la notazione di sommatoria di Einstein.

Questa operazione calcola effettivamente il prodotto esterno dei vettori "t" e "inv_freq", ottenendo una matrice in cui ciascun elemento "(i, j)" è il prodotto dell'elemento "i"-esimo di "t" e di " j`-esimo elemento di "inv_freq". Questa matrice ("freqs") rappresenta le frequenze utilizzate per generare i modelli sinusoidali per gli incastri rotanti.

Passaggio 2: livello Keras personalizzato per incorporamenti rotanti

Ora creiamo un livello Keras personalizzato che applica incorporamenti rotanti al tensore di input. Questo livello presuppone che il tensore di input abbia la forma "(batch_size, sequence_length, embedding_dim)".

class RotaryEmbeddingLayer(Layer):
    def __init__(self, dim, max_seq_len, **kwargs):
        super().__init__(**kwargs)
        self.dim = dim
        self.max_seq_len = max_seq_len
        self.rotary_embeddings = get_rotary_embedding(dim, max_seq_len)

    def call(self, inputs):
        seq_len = tf.shape(inputs)[1]
        embeddings = self.rotary_embeddings[:seq_len]
        
        cos_emb = embeddings[:, None, :self.dim // 2]
        sin_emb = embeddings[:, None, self.dim // 2:]
        
        # Decompose inputs into sine and cosine components
        inputs_cos = inputs[..., :self.dim // 2]
        inputs_sin = inputs[..., self.dim // 2:]
        
        # Apply rotary embeddings
        rotated_cos = inputs_cos * cos_emb - inputs_sin * sin_emb
        rotated_sin = inputs_sin * cos_emb + inputs_cos * sin_emb
        
        return tf.concat([rotated_cos, rotated_sin], axis=-1)

    def get_config(self):
        config = super().get_config()
        config.update({
            "dim": self.dim,
            "max_seq_len": self.max_seq_len
        })
        return config

La riga embeddings = self.rotary_embeddings[:seq_len] seleziona il sottoinsieme appropriato di incorporamenti rotanti precalcolati in base alla lunghezza della sequenza di input corrente. Poiché la lunghezza delle sequenze può variare da un lotto all'altro, questa operazione di suddivisione garantisce che vengano utilizzati solo gli incorporamenti corrispondenti alla lunghezza effettiva della sequenza.

La variabile "embeddings" ora contiene un tensore di forma "(seq_len, embedding_dim)", dove "seq_len" è la lunghezza delle sequenze nel batch corrente e "embedding_dim" è la dimensionalità degli incorporamenti. Questo tensore contiene gli incorporamenti posizionali rotanti per ciascuna posizione nella sequenza fino a seq_len.

emb = tf.concat((tf.cos(freqs), tf.sin(freqs)), axis=-1) combina le trasformazioni seno e coseno delle frequenze posizionali in un unico tensore:

-tf.cos(freqs) e tf.sin(freqs) applicano rispettivamente le trasformazioni coseno e seno al tensore freqs. Il tensore "freqs" contiene valori di frequenza per ciascuna posizione nella sequenza di input e ciascuna dimensione dello spazio di inclusione, calcolati in base alle posizioni della sequenza e alle frequenze inverse delle dimensioni di inclusione. Le funzioni seno e coseno vengono applicate per elemento, risultando in due tensori della stessa forma di "freqs". Queste trasformazioni aiutano a codificare la posizione in un modo che cattura la natura ciclica delle relazioni posizionali, facilitando la capacità del modello di comprendere le posizioni relative.

-tf.concat((tf.cos(freqs), tf.sin(freqs)), axis=-1) concatena i tensori trasformati coseno e seno lungo l'ultimo asse (indicato con axis=-1). Concatenando questi tensori fianco a fianco si raddoppia effettivamente la dimensionalità del tensore "freqs", con la prima metà che rappresenta valori trasformati in coseno e la seconda metà che rappresenta valori trasformati in seno per ciascuna posizione. La concatenazione garantisce che ciascuna codifica posizionale contenga sia informazioni seno che coseno, il che consente la conservazione delle informazioni sia sull'ampiezza che sulla fase dei segnali posizionali.

  • Il tensore concatenato emb ora contiene le inclusioni rotanti complete per le posizioni di input. La forma di "emb" sarà la stessa di "freqs" nelle sue prime due dimensioni (corrispondenti alle posizioni della sequenza e alle dimensioni di incorporamento), ma la sua ultima dimensione sarà due volte più grande, tenendo conto sia dei valori seno che coseno. Questi incorporamenti vengono utilizzati per modulare gli incorporamenti di input aggiungendo informazioni sulla posizione in modo rotazionalmente equivariante.

-cos_emb = incorporamenti[:, None, :self.dim // 2]:

  1. I primi due punti : significano "seleziona tutti gli elementi in questa dimensione", che, in questo caso, si riferisce a tutte le posizioni nella sequenza.

  2. "None" viene utilizzato per aggiungere una dimensione aggiuntiva, rendendo il tensore tridimensionale. Questo viene spesso fatto per garantire la compatibilità con determinate operazioni che prevedono input di un numero specifico di dimensioni. Ad esempio, quando si esegue la moltiplicazione per elemento con un altro tensore tridimensionale, le forme devono allinearsi secondo le regole di trasmissione.

  3. :self.dim // 2, seleziona la prima metà delle dimensioni nell'ultimo asse. Poiché la "dimensione_incorporamento" viene raddoppiata per includere sia i valori seno che coseno, la divisione per 2 seleziona effettivamente solo le componenti coseno degli incorporamenti.

Passaggio 3: integrazione con un modello Keras

Dopo aver definito il RotaryEmbeddingLayer, puoi integrarlo nel tuo modello Keras. Questo livello deve essere applicato agli incorporamenti prima di inserirli nei livelli di attenzione o in eventuali livelli di modello successivi.

Ecco un esempio semplificato di come integrare gli incorporamenti rotanti in un modello:

from tensorflow.keras.models import Model
from tensorflow.keras.layers import Input, Embedding, Dense

max_seq_len = 512
embedding_dim = 64

inp = Input(shape=(max_seq_len,))
x = Embedding(input_dim=10000, output_dim=embedding_dim)(inp)
x = RotaryEmbeddingLayer(dim=embedding_dim, max_seq_len=max_seq_len)(x)
# Add your model's layers here, e.g., Transformer blocks
x = Dense(1, activation='sigmoid')(x)

model = Model(inputs=inp, outputs=x)
model.summary()

Code Labs Academy © 2025 Tutti i diritti riservati.