Nella mia ultima articolo, abbiamo approfondito i Google Titans – un modello che spinge i confini del richiamo di contesti lunghi introducendo un modulo di memoria dinamica che si adatta al volo, in modo simile a come funziona la nostra memoria. È un paradosso strano. Abbiamo un'IA che può analizzare un documento di dieci milioni di parole, eppure si confonde ancora su domande come: "Quante 'r' ci sono nella parola strawberry?".
Il problema non risiede nel "cervello" dell'IA; risiede nei suoi "occhi". Il primo passo nel modo in cui questi modelli leggono, la tokenizzazione, essenzialmente pre-elabora il linguaggio per loro. Così facendo, essa rimuove i dettagli ricchi e disordinati di come le lettere formano le parole; l'intero mondo delle informazioni a livello di sub-parola semplicemente svanisce.
1. Perso nella tokenizzazione: dove muore la semantica sub-lessicale
Il linguaggio, per gli esseri umani, inizia come suono, parlato molto prima di essere scritto. Eppure è attraverso la scrittura e l'ortografia che iniziamo a comprendere la struttura compositiva del linguaggio. Le lettere formano sillabe, le sillabe formano parole, e da lì costruiamo conversazioni. Questa comprensione a livello di carattere ci permette di correggere, interpretare e inferire anche quando il testo è rumoroso o ambiguo. Al contrario, i modelli linguistici saltano completamente questa fase. Non sono mai esposti ai caratteri o al testo grezzo così com'è; invece, la loro intera percezione del linguaggio è mediata da un tokenizer.
Questo tokenizer, ironicamente, è l'unico componente nell'intera pipeline che non è appreso. È "stupido", fisso e interamente basato su euristiche, nonostante si trovi nel punto d'ingresso di un modello progettato per essere profondamente adattivo. In effetti, la tokenizzazione prepara il terreno per l'apprendimento, ma senza alcun apprendimento proprio.
Inoltre, la tokenizzazione è estremamente fragile. Un errore di battitura minore, diciamo "strawverry" invece di "strawberry", può produrre una sequenza di token completamente diversa, nonostante l'intento semantico rimanga ovvio per qualsiasi lettore umano. Questa sensibilità, invece di essere gestita immediatamente, viene passata a valle, costringendo il modello a interpretare un input corrotto. Peggio ancora, le tokenizzazioni ottimali sono altamente dipendenti dal dominio. Un tokenizer addestrato su testo inglese di uso quotidiano può funzionare splendidamente per il linguaggio naturale, ma fallire miseramente quando incontra codice sorgente, producendo lunghe e semanticamente scomode catene di token per nomi di variabili come user_id_to_name_map.
Come il "midollo spinale", che è la pipeline del linguaggio, più in alto viene compromesso, più paralizza tutto ciò che si trova a valle. Seduto proprio in cima, un tokenizer difettoso distorce l'input prima ancora che il modello inizi il ragionamento. Non importa quanto sia intelligente l'architettura, sta lavorando con segnali corrotti fin dall'inizio.
(Fonte: Autore)
Spiegazione: Come un semplice errore di battitura può potenzialmente sprecare la "capacità di pensiero" di un LLM per rettificarlo.
2. Ecco! Il Byte Latent Transformer
Se la tokenizzazione è la fragile base che trattiene i moderni LLM, la domanda naturale che ne consegue è: perché non eliminarla del tutto? È proprio questa la direzione radicale intrapresa dai ricercatori di Meta AI con il Byte Latent Transformer (BLT) (Pagnoni et al. 2024)1. Piuttosto che operare su parole, sub-parole o persino caratteri, il BLT modella il linguaggio da byte grezzi – la rappresentazione più fondamentale del testo digitale. Questo permette agli LLM di apprendere il linguaggio fin dalle fondamenta, senza che il tokenizer sia lì a erodere la semantica sub-lessicale.
Ma modellare direttamente i byte è tutt'altro che banale. Un Transformer ingenuo a livello di byte si soffocherebbe su lunghezze di input molte volte più lunghe del testo tokenizzato – un milione di parole diventano quasi cinque milioni di byte (1 parola = 4,7 caratteri in media, e 1 carattere = 1 byte), rendendo il calcolo dell'attenzione infattibile a causa della sua scalabilità quadratica. Il BLT aggira questo problema introducendo un sistema dinamico a due livelli: i segmenti di byte facili da prevedere vengono compressi in "patch" latenti, accorciando significativamente la lunghezza della sequenza. Il modello completo, ad alta capacità, viene quindi applicato selettivamente, concentrando le sue risorse computazionali solo dove la complessità linguistica lo richiede.
(Fonte: Adattato da Pagnoni et al. 2024, Figura 2)
Vista complessiva dell'architettura completa del Byte Latent Transformer.
2.1 Come funziona?
Il modello può essere concettualmente diviso in tre componenti principali, ciascuno con una responsabilità distinta:
2.1.1 L'Encoder Locale:
La funzione primaria dell'Encoder Locale è quella di trasformare una lunga sequenza di input di Nbytes di byte grezzi, b = (b1, b2,…, bN_bytes), in una sequenza molto più corta di Npatches di rappresentazioni di patch latenti, p = (p1, p2,…, pN_patches).
Step 1: Segmentazione dell'input e embedding iniziale dei byte
La sequenza di input viene segmentata in patch basate su una strategia predefinita, come il patching basato sull'entropia. Questo fornisce informazioni sui confini delle patch ma non altera la sequenza di input stessa. Queste informazioni sui confini delle patch saranno utili in seguito.
(Fonte: Pagnoni et al. 2024, Figura 3)
Spiegazione: Diverse strategie di patching, visualizzate.
La prima operazione all'interno dell'encoder è mappare ogni valore di byte discreto (0-255) in una rappresentazione vettoriale continua. Ciò si ottiene tramite una matrice di embedding apprendibile, Ebyte (forma: [256, he]), dove he è la dimensione nascosta del modulo locale.
- Input: Un tensore di ID di byte di forma [B, Nbytes], dove B è la dimensione del batch.
- Output: Un tensore di embedding di byte, X (forma: [B, Nbytes, he]).
Step 2: Aumento contestuale tramite N-gram Hashing
Per arricchire ogni rappresentazione di byte con il contesto locale oltre la sua identità individuale, i ricercatori impiegano una tecnica di embedding di n-grammi basata su hash. Per ogni byte bi nella posizione i, un insieme di n-grammi precedenti, gi,n = {bi-n+1,…, bi}, viene costruito per più valori di n ∈ {3,…,8}.
Questi n-grammi vengono mappati tramite una funzione hash a indici all'interno di una seconda, separata tabella di embedding, Ehash (forma: [Vhash, he]), dove Vhash è una dimensione fissa e grande del vocabolario (ovvero, il numero di "hash bucket").
Gli embedding risultanti degli n-grammi vengono sommati con l'embedding del byte originale per produrre una rappresentazione aumentata, ei. Questa operazione è definita come:
ei = xi + Σn∈{3,…,8} Ehash(hash(gi,n))
(Fonte: Autore)
Spiegazione: Cerca l'hash dell'n-gramma nella tabella di embedding e aggiungilo all'embedding del byte corrispondente, per tutti gli n ∈ [3,8].
La forma del tensore E = {e1, e2,…,eN_bytes} rimane [B, Nbytes, he].
Step 3: Affinamento iterativo con strati Transformer e Cross-Attention
Il cuore dell'Encoder Locale è costituito da uno stack di le strati identici. Ogni strato esegue un processo a due stadi per raffinare le rappresentazioni dei byte e distillarle in rappresentazioni di patch.
Step 3a: Auto-attenzione locale:
L'input viene elaborato da un blocco Transformer standard. Questo blocco utilizza un meccanismo di auto-attenzione causale con una finestra di attenzione limitata, il che significa che ogni rappresentazione di byte viene aggiornata prestando attenzione solo a un numero fisso di rappresentazioni di byte precedenti. Ciò garantisce efficienza computazionale pur consentendo un affinamento contestuale.
- Input: Se è il primo strato, l'input è l'embedding di byte aumentato di contesto E; altrimenti, riceve l'output dallo strato precedente di auto-attenzione locale.
(Fonte: Autore)
Hl: Input per lo strato di auto-attenzione corrente. E: Embedding di byte aumentato di contesto dallo Step 2. H'l-1: Output dallo strato precedente di auto-attenzione.
- Output: Rappresentazioni di byte più consapevoli del contesto, H'l (forma: [B, Nbytes, he]).
Step 3b: Attenzione incrociata multi-testa:
Lo scopo dell'attenzione incrociata è quello di distillare le informazioni contestuali a grana fine catturate nelle rappresentazioni dei byte e iniettarle nelle rappresentazioni di patch più astratte, conferendo loro una ricca consapevolezza delle loro strutture sub-lessicali costituenti. Ciò si ottiene attraverso un meccanismo di attenzione incrociata in cui le patch "interrogano" i byte che contengono.
- Query (Q): Gli embedding di patch vengono proiettati utilizzando un semplice strato lineare per formare le query.
Per qualsiasi strato successivo (l > 0), gli embedding di patch sono semplicemente le rappresentazioni di patch raffinate generate dal blocco di attenzione incrociata dello strato precedente, P(l−1).
Tuttavia, per il primissimo strato (l = 0),