Tokenisierung ist eine Systemdesign-Entscheidung, kein Vorverarbeitungsschritt
Wie Byte-Level-BPE das Vokabular baut, das Ihr LLM sieht, warum Tokenzahlen je nach Sprache schwanken, welche Fehler daraus entstehen und was man messen sollte, bevor man dem Modell die Schuld gibt.
Vier Symptome, eine Ursache
Früher oder später stoßen die meisten, die mit LLMs bauen, auf eines davon:
- Ein Modell mit beworbenem 128k-Kontextfenster geht bei nicht-englischen oder codelastigen Eingaben viel früher der Platz aus als erwartet.
- Ein Feature kostet pro Anfrage spürbar mehr für Nutzer, die Japanisch, Hindi oder Arabisch schreiben, als für englischsprachige.
- Das Modell scheitert selbstbewusst daran, Buchstaben zu zählen oder ein Wort umzudrehen, egal wie der Prompt formuliert ist.
- Zwei Teams melden unterschiedliche Tokenzahlen für Text, der identisch aussieht, und niemand kann sagen, warum.
Den Prompt umzuformulieren löst nichts davon, und das Modell ist auch nicht einfach dumm. Schuld ist die Tokenisierung, die Stufe der Pipeline, die Entwickler meist am wenigsten kennen. Sie sitzt zwischen Ihrem Code und dem Modell, arbeitet unsichtbar und fällt erst auf, wenn sie versagt. Im Folgenden geht es um den Mechanismus, seinen Platz in einem echten System, den Schaden, den er anrichten kann, und eine Checkliste für die Zeit, bevor man dem Modell die Schuld gibt.
Was das Modell tatsächlich bekommt
Ein Transformer kann Text nicht direkt verarbeiten, also wird er zuerst in Ganzzahlen umgewandelt. Diese Umwandlung ist die Tokenisierung, und sie beruht auf einem Vokabular, das vorab festgelegt wurde. Nichts in Ihrem Prompt kann es während der Laufzeit ändern.
Stellen Sie sich einen festen Satz Lego-Steine vor. Häufige englische Wörter wie „the“, „is“ und „running“ sind meist ein Stein. Seltenere Wörter werden zerlegt, „tokenization“ etwa in „token“ plus „ization“. Text, den der Tokenizer kaum gesehen hat, etwa eine seltene Sprache, ein ungewöhnliches Unicode-Zeichen oder ein obskurer Produktname, zerfällt in mehr und kleinere Steine.
Ihre ursprünglichen Zeichen erreichen das Modell nie. Dort kommt an, was die Segmentierung des Tokenizers ergeben hat, und Abrechnung, Kontextverbrauch und bestimmte Denkfehler gehen alle darauf zurück, wo er geschnitten hat. Das Wort „strawberry“ ist ein gutes Beispiel: Das Modell sieht ein paar Tokens und keine zehn Buchstaben.
Wie das Vokabular entsteht
Die meisten Vokabulare stammen aus Byte Pair Encoding (BPE). Es kommt aus der Datenkompression, nicht aus der Sprache: Philip Gage beschrieb es 1994 als Methode, Daten zu verkleinern, indem man das am häufigsten benachbarte Symbolpaar durch ein neues Symbol ersetzt.
Sennrich, Haddow und Birch übertrugen es 2016 mit dem Paper „Neural Machine Translation of Rare Words with Subword Units“ auf die neuronale maschinelle Übersetzung. Vokabulare auf Wortebene tun sich schwer mit seltenen Wörtern, Tippfehlern und Wortformen, und ihre Antwort war ein offenes Vokabular, in dem seltene Wörter aus häufigen, kleineren Subwort-Stücken zusammengesetzt werden.
Die GPT-2-Arbeit von OpenAI (Radford et al., 2019, „Language Models are Unsupervised Multitask Learners“) trieb die Idee auf die Byte-Ebene. Das Zusammenführen beginnt bei rohen UTF-8-Bytes statt bei Zeichen, was ein Startalphabet von genau 256 Symbolen ergibt. Allein diese Änderung erlaubt es einem modernen Tokenizer, jeden String in jeder Sprache darzustellen, auch fehlerhaften Text und Emojis, ohne an ein unbekanntes Token zu stoßen. Fast jede große Modellfamilie nutzt heute eine Variante davon, darunter GPT und Llama.
Das Training passiert einmal, offline, in diesen Schritten:
- Mit Basiseinheiten beginnen, entweder den 256 Bytes oder Zeichen in der ursprünglichen Formulierung.
- Einen großen Korpus durchgehen und zählen, wie oft jedes benachbarte Symbolpaar vorkommt.
- Das häufigste Paar nehmen, zu einem neuen Symbol verschmelzen und dieses ins Vokabular aufnehmen.
- Jedes Vorkommen dieses Paars im Korpus durch das neue Symbol ersetzen.
- Schritte 2 bis 4 wiederholen, bis das Vokabular eine Zielgröße erreicht, üblicherweise zwischen einigen Zehntausend und ein paar Hunderttausend Tokens.
Nach dem Training ist das Kodieren schnell und vollständig deterministisch. Auf neuen Text werden die gelernten Merges in derselben Reihenfolge angewendet, in der sie gelernt wurden, und Stücke werden gierig verbunden, bis nichts mehr verschmelzen kann. Verständnis ist nicht im Spiel. Es ist Nachschlagen und Verschmelzen, und dieselbe Eingabe ergibt immer dieselben Tokens.
SentencePiece, eine eigene Bibliothek von Google (2018), geht einen anderen Weg, den man kennen sollte. Sie trainiert direkt auf Rohtext und behandelt Leerzeichen als gewöhnliches Symbol statt als besonderes Trennzeichen. Das zählt bei Chinesisch, Japanisch und Thai, die Wörter nicht durch Leerzeichen trennen: Dort bricht ein leerzeichenbasierter Pretokenizer zusammen, während SentencePiece einheitlich zurechtkommt.
Wo sie in einer Anfrage sitzt
Die Tokenisierung läuft vor dem Transformer. Der Weg einer echten Anfrage sieht so aus:
User Text
↓
Pretokenizer (split on whitespace/punctuation rules, handle Unicode)
↓
BPE Encoder (apply learned merges → token IDs)
↓
Token IDs (integers)
↓
Embedding Lookup Table
↓
Transformer (attention + feedforward layers)
↓
Output Token IDs
↓
Decoder (token IDs → text)
↓
Response Text
Zwei Komponenten vergisst man leicht. Erstens wird die Embedding-Tabelle über die Token-ID indiziert, also bestimmt die Vokabulargröße die Größe der Embedding- und Output-Projektionsschichten. Ein größeres Vokabular verbraucht mehr Parameter für die Abbildung von Tokens auf Vektoren, bevor überhaupt eine Denkschicht läuft. Zweitens ist der Decoder der Tokenizer in umgekehrter Richtung. Eine Antwort Token für Token zu streamen und wieder in lesbaren Text zu verwandeln, beruht auf demselben festen Vokabular wie das Kodieren.
Mit tiktoken nachsehen
Statt das zu glauben, kann man es mit tiktoken ansehen, der Tokenizer-Bibliothek von OpenAI:
import tiktoken
enc = tiktoken.encoding_for_model("gpt-4o")
samples = {
"english": "The invoice was approved yesterday.",
"japanese": "請求書は昨日承認されました。",
}
for label, text in samples.items():
tokens = enc.encode(text)
print(f"{label}: {len(tokens)} tokens -> {tokens}")
print([enc.decode([t]) for t in tokens])
Bei einem solchen Vergleich sollte man zwei Dinge prüfen. Erstens schwanken Tokens pro Zeichen oder Wort je nach Sprache stark. Dieselbe Information kann spürbar mehr Tokens kosten, je nachdem, wie gut eine Sprache beim Training des Tokenizers vertreten war. Die Tokenizer-Dokumentation von OpenAI zeigt das selbst: Eine kurze Phrase zerfällt unter älteren, englischzentrierten Kodierungen in mehr Teile als unter neueren, und nicht-lateinische Schriften brauchen im Allgemeinen mehr Tokens pro Zeichen als englische Prosa. Zweitens fallen Tokengrenzen selten mit Zeichen oder auch nur Silben zusammen. Genau deshalb fallen LLMs Buchstabenzählen und das Umdrehen von Zeichenketten standardmäßig schwer: Die Arbeitsdarstellung bietet einzelne Zeichen nicht als vollwertige Einheit an.
Ein mehrsprachiges Budget-Beispiel
Stellen Sie sich einen Support-Agenten vor, der englisch-, japanisch- und arabischsprachige Nutzer mit einem harten Tokenbudget pro Anfrage bedient. Ein- und Ausgabetokens pro Aufruf zu deckeln, um Kosten und Latenz zu begrenzen, ist gängige Praxis im Betrieb. Wer das Budget nur mit englischen Tests bemisst, benachteiligt systematisch alle anderen. Derselbe Gesprächsverlauf und derselbe System-Prompt verbrauchen auf Japanisch oder Arabisch mehr vom Budget und lassen weniger Platz für die Antwort.
Daran ist nichts exotisch. Überwiegend auf Englisch trainierte BPE-Tokenizer verhalten sich ab Werk so, und das Ergebnis ist ein stiller Qualitätsverlust durch abgeschnittenen Kontext, ausgerechnet bei den internationalen Nutzern, die das Produkt erreichen soll. Ein cleverer Prompt behebt das nicht. Was hilft: beim Testen echte Tokens pro Sprache zählen und dann Budgets, Kürzungs- oder Zusammenfassungsregeln für jede Sprache einzeln festlegen.
Ein günstiges Experiment
Bevor Sie Annahmen über das Verhalten des Tokenizers vertrauen, probieren Sie Folgendes. Nehmen Sie 20 bis 30 inhaltsgleiche Sätze, professionell ins Englische, Japanische, Arabische und Hindi übersetzt (Support-Transkripte eignen sich gut). Kodieren Sie jeden mit einem Tokenizer, etwa o200k_base über tiktoken, und messen Sie Tokens pro Satz und pro Zeichen für jede Sprache.
Die zu prüfende Hypothese (kein gesicherter Befund) lautet, dass Japanisch, Arabisch und Hindi ein deutlich höheres Verhältnis von Tokens zu Zeichen haben als Englisch. Das würde bedeuten, dass dasselbe Kontextfenster und dasselbe Budget pro Anfrage diesen Nutzern weniger effektiven Raum für denselben Inhalt lassen. Das genaue Verhältnis hängt von Tokenizer-Version und Sprachen ab, also messen Sie es an Ihren eigenen Daten.
Fehlerbilder, denen Sie im Betrieb begegnen
Tokenisierung erzeugt eine bestimmte, wiederkehrende Gruppe von Bugs.
- Fehler bei Aufgaben auf Zeichenebene. Buchstaben zählen, Strings umdrehen, buchstabieren und manche mehrstellige Arithmetik scheitern, weil das Modell über Subwort-Tokens und nicht über Zeichen schlussfolgert. Das Symptom ist ein Modell, das bei etwas für Menschen Trivialem selbstbewusst falschliegt. Besseres Prompting allein behebt das nicht. Besser funktioniert, das Modell das Wort mit Trennzeichen ausbuchstabieren zu lassen, was Tokens auf Zeichenebene erzwingt, oder die Aufgabe an eine Codeausführung zu übergeben.
- Tokenizer passt nicht zum Modell. Wer Kosten oder Kontextverbrauch mit dem Tokenizer eines anderen Modells schätzt, bekommt systematisch falsche Zahlen. Lokale Tokenzählungen weichen dann von der API-Rechnung ab. Verwenden Sie die Kodierung, die für das aufgerufene Modell dokumentiert ist.
- Überlauf, den niemand bemerkt. Ein Prompt, der auf Englisch locker passt, kann übersetzt das Limit überschreiten, und dasselbe gilt für Inhalte voller Code, JSON oder seltsamer Symbole. Das Symptom ist abgeschnittener Kontext und schlechtere Antworten, in manchen Pipelines ohne explizite Fehlermeldung. Messen Sie echte Tokenzahlen an echten Produktivinhalten.
- Empfindlichkeit für Leerzeichen und Formatierung. Führende Leerzeichen, wiederholter Whitespace und uneinheitliche Formatierung können Tokengrenzen und -zahlen verschieben, manchmal erheblich, sodass zwei Prompts, die für Menschen gleich aussehen, sich in Tokens und Kosten unterscheiden.
- Vokabular-Mismatch nach Fine-Tuning oder Modellwechsel. Der Umstieg auf eine andere Modellfamilie mit anderem Tokenizer, ohne Prompt-Budgets oder Few-Shot-Längen neu zu prüfen, kann Annahmen, auf die sich das System verlassen hat, still brechen. Was gut lief, wird nach einem angeblich nahtlosen Austausch schlechter.
Was im Betrieb zu tun ist
- Kosten: Tokens sind die echte Abrechnungseinheit, also sollten mehrsprachige Produkte pro Sprache budgetieren und nicht von einem globalen Durchschnitt ausgehen.
- Kontextplanung: RAG-Chunks, das Kürzen des Gesprächsverlaufs und Few-Shot-Beispiele in Tokens des konkret verwendeten Tokenizers bemessen, nie in Wörtern oder Zeichen.
- Geschwindigkeit: Tokenisierung selbst ist schnell und selten der Engpass, aber eine Schätzung der Prompt-Länge mit der falschen Kodierung vor einem Aufruf kann zu unnötigen Wiederholungen oder Kürzungen führen, und beides kostet Zeit.
- Beobachtbarkeit: tatsächliche Ein- und Ausgabetokens pro Anfrage loggen, nach Sprache aufgeschlüsselt, wenn Sie mehrere Locales bedienen. Das ist billig einzubauen und fängt Kosten- und Kontextprobleme ab, bevor Nutzer sie melden.
- Konsistenz: dieselbe Tokenizer-Version für lokale Tests, Kostenschätzung und den produktiven API-Aufruf verwenden. Eine abweichende Kodierung liefert selbstbewusst falsche Zahlen.
Wann man das alles ignorieren kann
Bei einer rein englischen Anwendung mit kurzen Texten und geringem Volumen, etwa einem internen Tool, einem Prototyp oder einem wenig genutzten Chatbot, lohnen Tokenizer-Themen wie Kostenunterschiede zwischen Sprachen keine Entwicklungszeit. Die Standardeinstellungen der Bibliothek reichen, und die Mühe ist anderswo besser investiert.
Ebenso gibt es nichts zu umgehen, wenn das Produkt nie Arbeit auf Zeichenebene braucht, etwa Buchstabieren, Buchstabenzählen oder exaktes Umdrehen von Strings. Relevant wird es erst, wenn der Anwendungsfall das Modell wirklich um solche Aufgaben bittet, und dann besteht die übliche Lösung darin, sie an einen Codeausführungsschritt zu delegieren, statt per Prompt gegen den Tokenizer zu kämpfen.
Die Kurzfassung
Modelle sehen nie Zeichen oder Wörter, sondern nur Tokens aus einem festen, vorab gebauten Vokabular, meist mit Byte-Level-BPE. BPE begann 1994 als Kompressionsalgorithmus, kam 2016 in die NLP und wurde 2019 vom GPT-2-Team auf Bytes erweitert, weshalb moderne Tokenizer praktisch nie unbekannte Tokens ausgeben. Tokenzahlen, nicht Wort- oder Zeichenzahlen, bestimmen Ihre echten Kosten und Ihren Kontextverbrauch. Dieselbe Information kann in manchen Sprachen mehr Tokens kosten, was nicht-englische Nutzer still benachteiligt, wenn man nicht pro Sprache budgetiert. Fehler beim Zeichenzählen, Buchstabieren und Umdrehen kommen aus der Darstellung des Tokenizers und sind kein Denkdefizit, und unpassende Tokenizer verursachen vermeidbare Bugs im Betrieb. Messen Sie die Tokenisierung und behalten Sie sie als Teil des Designs im Blick, statt sie nach der ersten Integration zu vergessen.
Quellen:
- Gage (1994), „A New Algorithm for Data Compression“, The C Users Journal – der ursprüngliche BPE-Kompressionsalgorithmus.
- Sennrich, Haddow und Birch (2016), „Neural Machine Translation of Rare Words with Subword Units“, ACL 2016, DOI 10.18653/v1/P16-1162.
- Radford et al. (2019), „Language Models are Unsupervised Multitask Learners“ – das GPT-2-Paper, das Byte-Level-BPE einführte.
- Kudo und Richardson (2018) – das SentencePiece-Paper von Google, ein sprachunabhängiger Subwort-Tokenizer und -Detokenizer.
- Der Leitfaden im OpenAI Cookbook zum Zählen von Tokens mit tiktoken sowie die
tiktoken-Dokumentation zu den Kodierungen cl100k_base und o200k_base.