Die Kunst des prompten Designs: Prompt Boundaries und Token Healing

May 09 2023
Dies (gemeinsam mit Marco Tulio Ribeiro geschrieben) ist Teil 2 einer Serie über die Kunst des Prompt-Designs (Teil 1 hier), in der wir über die Steuerung großer Sprachmodelle (LLMs) mit Anleitung sprechen. In diesem Beitrag besprechen wir, wie die von Sprachmodellen verwendeten gierigen Tokenisierungsmethoden eine subtile und starke Verzerrung in Ihre Eingabeaufforderungen einführen können, was zu rätselhaften Generationen führt.
Alle Bilder sind Originalkreationen.

Dies (gemeinsam mit Marco Tulio Ribeiro geschrieben ) ist Teil 2 einer Serie über die Kunst des Prompt-Designs (Teil 1 hier ), in der wir über die Steuerung großer Sprachmodelle (LLMs) mit sprechen guidance.

In diesem Beitrag besprechen wir, wie die von Sprachmodellen verwendeten gierigen Tokenisierungsmethoden eine subtile und starke Verzerrung in Ihre Eingabeaufforderungen einführen können, was zu rätselhaften Generationen führt.

Sprachmodelle werden nicht mit Rohtext trainiert, sondern mit Tokens, bei denen es sich um Textblöcke handelt, die oft zusammen vorkommen, ähnlich wie Wörter. Dies wirkt sich darauf aus, wie Sprachmodelle Text „sehen“, einschließlich Eingabeaufforderungen (da Eingabeaufforderungen nur Sätze von Token sind). Modelle im GPT-Stil verwenden Tokenisierungsmethoden wie Byte Pair Encoding (BPE), die alle Eingabebytes auf gierige Weise Token-IDs zuordnen. Dies ist für das Training in Ordnung, kann jedoch zu subtilen Problemen während der Inferenz führen, wie im folgenden Beispiel gezeigt.

Ein Beispiel für ein Prompt-Boundary-Problem

Betrachten Sie das folgende Beispiel, in dem wir versuchen, eine HTTP-URL-Zeichenfolge zu generieren:

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'))

      
                

Während URLs im Training mit dem Token 1358 ( ://) kodiert sind, lässt unsere Eingabeaufforderung den LLM stattdessen das Token 27( ) sehen, was die Vervollständigung durch künstliches Aufteilen von .:://

Tatsächlich kann das Modell ziemlich sicher sein, dass das Sehen von Token 27( :) bedeutet, dass das, was als nächstes kommt, sehr unwahrscheinlich etwas ist, das zusammen mit dem Doppelpunkt unter Verwendung eines „längeren Tokens“ wie codiert werden könnte ://, da diese Zeichen in den Trainingsdaten des Modells dies tun würden wurden zusammen mit dem Doppelpunkt kodiert (eine Ausnahme davon, die wir später besprechen werden, ist die Teilwortregularisierung während des Trainings). Die Tatsache, dass das Sehen eines Tokens bedeutet, sowohl die Einbettung dieses Tokens zu sehen als auch , dass alles, was als nächstes kommt, nicht vom gierigen Tokenizer komprimiert wurde, ist leicht zu vergessen, aber es ist wichtig in Prompt-Grenzen.

Lassen Sie uns die Zeichenfolgendarstellung aller Token im Vokabular des Modells durchsuchen, um zu sehen, welche mit einem Doppelpunkt beginnen:

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(" ["))

      
                

Unbeabsichtigte Voreingenommenheit mit „Token-Healing“ beheben

Was können wir tun, um diese unbeabsichtigten Verzerrungen zu vermeiden? Eine Möglichkeit besteht darin, unsere Eingabeaufforderungen immer mit Token zu beenden, die nicht zu längeren Token erweitert werden können (z. B. ein Rollen-Tag für chatbasierte Modelle), aber dies ist eine schwerwiegende Einschränkung.

Stattdessen guidanceverfügt es über eine Funktion namens „Token-Healing“, die den Generierungsprozess automatisch um ein Token vor dem Ende der Eingabeaufforderung sichert und dann das erste generierte Token so einschränkt, dass es ein Präfix hat, das mit dem letzten Token in der Eingabeaufforderung übereinstimmt. In unserem URL-Beispiel würde dies bedeuten, das zu entfernen :und die Generierung des ersten Tokens mit einem :Präfix zu erzwingen. Token-Healing ermöglicht es Benutzern, Eingabeaufforderungen nach Belieben auszudrücken, ohne sich Gedanken über Token-Grenzen machen zu müssen.

Lassen Sie uns beispielsweise einige der obigen URL-Beispiele mit aktivierter Token-Healing erneut ausführen (für Transformer-Modelle ist sie standardmäßig aktiviert, daher entfernen wir 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}}')()

      
                

Wenn Sie damit vertraut sind, wie Sprachmodelle trainiert werden, fragen Sie sich vielleicht, wie die Teilwortregularisierung in all dies passt. Teilwortregularisierung ist eine Technik, bei der während des Trainings suboptimale Tokenisierungen zufällig eingeführt werden, um die Robustheit des Modells zu erhöhen. Das bedeutet, dass das Modell nicht immer die beste gierige Tokenisierung sieht. Die Regularisierung von Teilwörtern trägt hervorragend dazu bei, dass das Modell robuster gegenüber Token-Grenzen ist, beseitigt jedoch nicht vollständig die Voreingenommenheit, die das Modell gegenüber der Standard-Greedy-Tokenisierung hat. Dies bedeutet, dass, während Modelle je nach Ausmaß der Teilwortregularisierung während des Trainings mehr oder weniger Token-Grenzen-Bias aufweisen können, alle Modelle immer noch diesen Bias aufweisen. Und wie oben gezeigt, kann es immer noch starke und unerwartete Auswirkungen auf die Modellausgabe haben.

Abschluss

Denken Sie beim Schreiben von Eingabeaufforderungen daran, dass die gierige Tokenisierung einen erheblichen Einfluss darauf haben kann, wie Sprachmodelle Ihre Eingabeaufforderungen interpretieren, insbesondere wenn die Eingabeaufforderung mit einem Token endet, das zu einem längeren Token erweitert werden könnte. Diese leicht zu übersehende Verzerrungsquelle kann Ihre Ergebnisse auf überraschende und unbeabsichtigte Weise beeinflussen.

Um dies zu beheben, beenden Sie entweder Ihre Eingabeaufforderung mit einem nicht erweiterbaren Token oder verwenden Sie so etwas wie die guidance„Token-Healing“-Funktion von , damit Sie Ihre Eingabeaufforderungen nach Belieben ausdrücken können, ohne sich Gedanken über Token-Grenzartefakte machen zu müssen.

Um die Ergebnisse in diesem Artikel selbst zu reproduzieren, sehen Sie sich die Notebook- Version an.