L'art de la conception rapide : limites rapides et guérison des jetons

May 09 2023
Ceci (écrit conjointement avec Marco Tulio Ribeiro) est la partie 2 d'une série sur l'art de la conception rapide (partie 1 ici), où nous parlons de contrôler les grands modèles de langage (LLM) avec des conseils. Dans cet article, nous expliquerons comment les méthodes de tokenisation gourmandes utilisées par les modèles de langage peuvent introduire un biais subtil et puissant dans vos invites, conduisant à des générations déroutantes.
Toutes les images sont des créations originales.

Ceci (écrit conjointement avec Marco Tulio Ribeiro ) est la partie 2 d'une série sur l'art de la conception rapide (partie 1 ici ), où nous parlons de contrôler les grands modèles de langage (LLM) avec guidance.

Dans cet article, nous expliquerons comment les méthodes de tokenisation gourmandes utilisées par les modèles de langage peuvent introduire un biais subtil et puissant dans vos invites, conduisant à des générations déroutantes.

Les modèles de langage ne sont pas formés sur du texte brut, mais plutôt sur des jetons, qui sont des morceaux de texte qui apparaissent souvent ensemble, comme des mots. Cela a un impact sur la façon dont les modèles de langage "voient" le texte, y compris les invites (puisque les invites ne sont que des ensembles de jetons). Les modèles de style GPT utilisent des méthodes de tokenisation telles que Byte Pair Encoding (BPE), qui mappent tous les octets d'entrée aux identifiants de jeton de manière gourmande. C'est bien pour la formation, mais cela peut entraîner des problèmes subtils lors de l'inférence, comme le montre l'exemple ci-dessous.

Un exemple de problème de frontière rapide

Prenons l'exemple suivant, où nous essayons de générer une chaîne d'URL HTTP :

import guidance

# we use StableLM as an example, but these issues impact all models to varying degrees
guidance.llm = guidance.llms.Transformers("stabilityai/stablelm-base-alpha-3b", device=0)

# we turn token healing off so that guidance acts like a normal prompting library
program = guidance('The link is <a href="http:{{gen max_tokens=10 token_healing=False}}')
program()

      
                
Notebook output.

guidance('The link is <a href="http{{gen max_tokens=10 token_healing=False}}')()

      
                

print_tokens(guidance.llm.encode('The link is <a href="http:'))

      
                

print_tokens(guidance.llm.encode('The link is <a href="http://www.google.com/search?q'))

      
                

Alors que les URL en formation sont encodées avec le jeton 1358 ( ://), notre invite fait que le LLM voit le jeton 27( :) à la place, ce qui annule l'achèvement en divisant artificiellement ://.

En fait, le modèle peut être à peu près sûr que voir le jeton 27( :) signifie que ce qui vient ensuite est très peu susceptible d'être quelque chose qui aurait pu être encodé avec les deux-points en utilisant un «jeton plus long» comme ://, car dans les données d'entraînement du modèle, ces caractères seraient ont été encodés avec les deux-points (une exception à cela dont nous parlerons plus tard est la régularisation des sous-mots pendant la formation). Le fait que voir un jeton signifie à la fois voir l'intégration de ce jeton et aussi que tout ce qui vient ensuite n'a pas été compressé par le tokenizer gourmand est facile à oublier, mais c'est important dans les limites rapides.

Cherchons dans la représentation sous forme de chaîne de tous les jetons du vocabulaire du modèle, pour voir ceux qui commencent par deux-points :

print_tokens(guidance.llm.prefix_matches(":"))

      
                

print_tokens(guidance.llm.prefix_matches("http"))

      
                

# Accidentally adding a space, will lead to weird generation
guidance('I read a book about {{gen max_tokens=5 token_healing=False temperature=0}}')()

      
                
# No space, works as expected guidance('I read a book about{{gen max_tokens=5 token_healing=False temperature=0}}')()

guidance('An example ["like this"] and another example [{{gen max_tokens=10 token_healing=False}}')()

      
                

print_tokens(guidance.llm.prefix_matches(" ["))

      
                

Corriger les biais involontaires avec la "guérison symbolique"

Que pouvons-nous faire pour éviter ces biais involontaires ? Une option consiste à toujours terminer nos invites avec des jetons qui ne peuvent pas être étendus à des jetons plus longs (par exemple, une balise de rôle pour les modèles basés sur le chat), mais il s'agit d'une limitation sévère.

Au lieu de cela, guidancea une fonctionnalité appelée "guérison de jeton", qui sauvegarde automatiquement le processus de génération d'un jeton avant la fin de l'invite, puis contraint le premier jeton généré à avoir un préfixe qui correspond au dernier jeton de l'invite. Dans notre exemple d'URL, cela signifierait supprimer le :, et forcer la génération du premier jeton à avoir un :préfixe. La guérison des jetons permet aux utilisateurs d'exprimer des invites comme ils le souhaitent, sans se soucier des limites des jetons.

Par exemple, réexécutons certains des exemples d'URL ci-dessus avec la guérison des jetons activée (elle est activée par défaut pour les modèles Transformer, nous supprimons donc token_healing=False):

# With token healing we generate valid URLs,
# even when the prompt ends with a colon:
guidance('The link is <a href="http:{{gen max_tokens=10}}')()

      
                
# With token healing, we will sometimes generate https URLs, # even when the prompt ends with "http": program = guidance('''The link is <a href="http{{gen 'completions' max_tokens=10 n=10 temperature=1}}''') program()["completions"]

# Accidentally adding a space will not impact generation
program = guidance('''I read a book about {{gen max_tokens=5 temperature=0}}''')
program()

      
                
# This will generate the same text as above program = guidance('''I read a book about{{gen max_tokens=6 temperature=0}}''') program()

guidance('An example ["like this"] and another example [{{gen max_tokens=10}}')()

      
                

Si vous savez comment les modèles de langage sont entraînés, vous vous demandez peut-être comment la régularisation des sous-mots s'intègre dans tout cela. La régularisation des sous-mots est une technique où, lors de la formation, des tokenisations sous-optimales sont introduites de manière aléatoire pour augmenter la robustesse du modèle. Cela signifie que le modèle ne voit pas toujours la meilleure tokenisation gourmande. La régularisation des sous-mots est excellente pour aider le modèle à être plus robuste aux limites des jetons, mais elle ne supprime pas complètement le biais que le modèle a envers la tokenisation gourmande standard. Cela signifie qu'en fonction de la quantité de régularisation des sous-mots pendant la formation, les modèles peuvent présenter plus ou moins de biais de frontières symboliques, tous les modèles ont toujours ce biais. Et comme indiqué ci-dessus, cela peut toujours avoir un impact puissant et inattendu sur la sortie du modèle.

Conclusion

Lorsque vous écrivez des invites, n'oubliez pas que la segmentation gourmande en jetons peut avoir un impact significatif sur la façon dont les modèles de langage interprètent vos invites, en particulier lorsque l'invite se termine par un jeton qui peut être étendu à un jeton plus long. Cette source de biais facile à manquer peut avoir un impact sur vos résultats de manière surprenante et involontaire.

Pour résoudre ce problème, terminez votre invite avec un jeton non extensible ou utilisez quelque chose comme guidancela fonction de "guérison de jeton" afin que vous puissiez exprimer vos invites comme vous le souhaitez, sans vous soucier des artefacts de limite de jeton.

Pour reproduire vous-même les résultats de cet article, consultez la version notebook .