Che cosa sono i token di GPT e come aiutare il modello, con alcuni semplici trucchi, a superare il limite di token processabili per ogni scambio.
GPT fissa un limite al numero di token che possono essere processati nell’ambito di un singolo scambio prompt-risposta. È come se il modello avesse una memoria limitata, nel senso che non ricorda il contenuto degli scambi di dialogo con l’utente, quando questi eccedono una certa lunghezza. Non si tratta di un limite «cognitivo», ma dettato piuttosto da ragioni tecniche. Anche se, nel caso dell’intelligenza artificiale, è difficile distinguere fra i due concetti. Per capire meglio quale sia la radice del problema e come riuscire ad aggirarlo, almeno in una certa misura, proviamo innanzi tutto spiegare meglio che cosa sono i token di GPT.
I token, pezzi di parole
I token possono essere considerati come frammenti di parole. Per elaborare il prompt, ossia la richiesta formulata dall’utente, GPT scompone l’input in un certo numero di token. Questi non coincidono esattamente con le singole parole contenute nel prompt, ma possono includere spazi e persino sottoparole o emoji. Ciascun token è associato a metadati, che servono al modello linguistico per lavorare. Il token è l’unità di misura di tutto, anche del prezzo che paghiamo per usare le API di GPT. Per esempio, nel caso di GPT-3.5 Turbo, il modello di default di ChatGPT, il prezzo oscilla fra 0,0015 e 0,003 dollari ogni 1.000 token.
Nella tabella sottostante riportiamo i prezzi al momento praticati da OpenAI per ciascuno dei modelli disponibili via API.
Quando parliamo di tokenizzazione nell’ambito dell’elaborazione del linguaggio naturale, ci riferiamo a una procedura che consiste nella suddivisione di paragrafi e frasi in unità di dimensioni inferiori, alle quali è più facile assegnare un significato. La prima fase del processo di NLP (natural language processing) consiste proprio nel raccogliere i dati (una frase) e scomporli in parti comprensibili. Esistono tre forme di tokenizzazione: per parole, per caratteri, per sottoparole. Quest’ultimo approccio parte dal presupposto che, per comprendere il significato di un lessema, dobbiamo scomporlo in morfemi. Per esempio, la parola italiana pesantemente corrisponde a un lessema scomponibile in due morfemi: pesante e mente. Lo stesso vale per l’inglese tallest, composto dai morfemi tall ed est. Va detto che non tutti i linguisti concordano con questo approccio, il quale in ogni caso si è rivelato molto efficace nel caso dei LLMs (large language models), come GPT.
Quattro token per dirti che ti amo
È chiaro che la suddivisione delle parole in token dipende dalla lingua. Ad esempio, la frase ti amo (6 caratteri) contiene 4 token, mentre cómo estás («come stai» in spagnolo, 10 caratteri) ne contiene 5. Il rapporto più alto tra token e caratteri può rendere più costosa l’implementazione dell’API di GPT per le diverse lingue. Se volete divertirvi un po’, usate il Tokenizer di OpenAI: potete scrivere quello che volete e conoscere instantaneamente il numero di token in cui il modello suddivide il vostro input (per dire, il primo articolo della Costituzione italiana misura 58 token).
Ogni token riceve un valore, che è inversamente proporzionale alla sua probabilità/frequenza. Quanto più probabile/frequente è un token, tanto più basso è il valore ad esso assegnato. La cosa più interessante è che la stessa parola può generare token con valori differenti, a seconda del contesto frasale. Nell’esempio proposto dalla stessa OpenAI per illustrare il concetto, sono messe a confronto le seguenti tre frasi in inglese:
- My favorite color is red.
- My favorite color is Red.
- Red is my favorite color.
La parola red non cambia, ma è inserita nelle frasi in tre modi differenti:
- in fondo alla frase, con l’iniziale minuscola
- in fondo alla frase, con l’iniziale maiuscola
- all’inizio della frase
Ebbene, queste differenze sono alla base di token con valori diversi:
- ‘ red’ (valore: 2266)
- ‘ Red’ (valore: 2297)
- ‘Red’ (valore: 7738)
Si noti che, nei primi due casi inclusi nell’esempio, il token include lo spazio precedente la parola. Come già detto, infatti, le API di GPT incorporano nel proprio dizionario dei token anche gli spazi che separano le parole e li utilizzano per definire il significato dei token stessi. Questo spiega perché i prompt che terminano con il carattere ‘spazio’ possono produrre un risultato di qualità inferiore.
Modificare le probabilità che un token appaia nella risposta
OpenAI permette di modificare i valori di default definiti dal modello per ciascun token. A tale scopo si utilizza uno dei binding ufficiali di Python resi disponibili dalla stessa OpenAI (si veda il documento di API Reference). Si tratta di logit_bias, con il quale è possibile associare un valore di bias (compreso fra -100 e 100) a uno specifico token ID. L’effetto varia a seconda del modello, ma in generale il logit_bias tende a diminuire o aumentare la probabilità di selezione del token specificato, fino a determinarne l’esclusione.
Come dicevamo, OpenAI ha definito dei limiti per garantire un’efficiente amministrazione delle risorse di calcolo e quindi assicurare l’efficacia complessiva dei propri LLMs. A seconda del modello di GPT utilizzato, il numero di token utilizzabili per ciascuna sequenza prompt-risposta varia in misura notevole. Nel caso del modello di default impiegato da ChatGPT, per esempio, si possono utilizzare fino a 4.097 token condivisi tra richiesta e risposta. Se dunque la richiesta che forniamo in input è di 4.000 token, GPT avrà un massimo di 97 token per formulare la risposta.
Nella tabella sottostante sono indicati i limiti al numero di token fissati da OpenAI per ciascuno dei modelli disponibili via API.
Giocare coi limiti: looping e gestione del contesto
Come possiamo gestire questi limiti, quando usiamo GPT e gli altri modelli di OpenAI per risolvere problemi che richiedono prompt molto lunghi?
In generale, per gestire i problemi derivanti dal limite nel numero di token conviene spesso troncare, omettere o riformulare la richiesta. Ecco perché è importante riflettere bene, quando si definisce il testo del nostro prompt. Una buona tattica consiste nel concludere la conversazione in corso, creando un riassunto prima che il limite venga superato, per poi iniziare la conversazione successiva con quel riassunto. Un’altra tattica è quella di scrivere prompt molto lunghi, con l’idea di tentare una conversazione unica: in questo modo si mette a disposizione del modello tutto ciò che si sa, per porlo in condizione di formulare una sola risposta.
In termini più specifici, è sempre utile immaginare come dividere un prompt lungo in una sequenza di richieste-risposte più brevi. Questo approccio è definito looping. Di seguito proponiamo alcune buone pratiche di looping.
- Definire la struttura del loop: Impostare una logica di iterazione attraverso la query ed elaborarle in parti più piccole. Ogni iterazione deve gestire una parte della query che rientra nel limite di token.
- Conservazione del contesto: Mantenere il contesto necessario tra un’iterazione e l’altra, garantendo in tal modo la continuità della conversazione. In pratica, si tratta di salvare le informazioni rilevanti dell’iterazione precedente e passarle come contesto all’iterazione successiva.
- Concatenazione e token di concatenazione: Utilizzare tecniche di concatenazione per combinare i risultati di ogni iterazione in una risposta coerente. È anche possibile introdurre speciali token di concatenazione per marcare i confini tra i pezzi durante la concatenazione.
- Gestione delle risposte incomplete: In alcuni casi, il modello può generare risposte incomplete. Per risolvere questo problema, è possibile utilizzare tecniche come la post-elaborazione o la ripetizione del modello sulla risposta incompleta per ottenere un output completo e significativo.
- Ottimizzazione delle prestazioni: Considerare i vincoli di tempo e di calcolo associati al looping. Regolare la dimensione del blocco (chunk) e il numero di iterazioni in base ai requisiti e ai limiti specifici dell’applicazione.
Un ottimo esempio di looping è offerto dalla stessa OpenAI, nel documento Summarizing books with human feedback.
Chicche per smanettoni
Se poi si hanno competenze di programmazione, si riesce a fare anche qualcosa di più. Per esempio, è possibile consolidare i blocchi in un unico testo non tokenizzato, mediante strumenti come NLTK, Transformers o Tiktoken (ne parla Sung Kim in How to Get Around OpenAI GPT-3 Token Limits, in “Medium”, 6 febbraio 2023). Jeremy Nguyen, poi, suggerisce di creare una sintesi iterativa di ogni conversazione, chiedendo a GPT di produrre un riassunto in formato JSON ogni cinque messaggi. In questo modo, anche se GPT «dimentica» l’inizio della conversazione, può recuperarne il riassunto andando indietro di pochi passaggi.
Va peraltro detto che non tutti i LLMs hanno la memoria corta come quelli di OpenAI. Anthropic, per esempio, ha messo a punto il modello Claude, che offre una finestra contestuale molto ampia, fino a 100mila token.
Infine, per restare in tema date un’occhiata al recente Prompt engineering: quando i LLMs chiedono un «aiutino» da casa., nel quale ci occupiamo di chain-of-thought (CoT) prompting e di reprompting.