Concepts / Building a Simple Tokenizer with encode and decode

Building a Simple Tokenizer with encode and decode

The encode method converts a text string into a list of integer token IDs, and the allowed_special parameter tells it which special tokens to accept.

  • Programming

Running the Tokenizer Before Explaining It

Before looking at how a BPE tokenizer works inside, it helps to run one and watch what happens. The code in this article takes a sentence that contains a special marker, turns it into a list of integers, and then turns that list back into text. Every step is visible in the code and its printed output, so you can judge the behavior yourself before reading the explanation.

The tokenizer used here is the BPE tokenizer from the previous step. Its usage is similar to the SimpleTokenizerV2 class you implemented earlier, and it exposes the same style of encode method. This article is about two calls. The encode call goes from text to token IDs, and the decode call goes from token IDs back to text.

What do you think happens?

The input text is built from two adjacent string literals, "In the sunlit terraces" and "of someunknownPlace.". Python joins adjacent literals with no separator. Between 'terraces' and 'of', does the original text contain a space?

  • Yes, a space

  • No space

Reveal answer
Answer:

No space. The original text contains the characters terracesof with nothing between them.

Python joins adjacent string literals without inserting anything between them, so the input is terracesof. The printed output in the example reads terraces of with a space. The example does not settle whether a space was typeset in the sample output that the code does not contain, or whether the round trip changed the text. Keep this question in mind; it is the central check of this article.

Reading the Encoded Output

The example runs the encode call on a sentence containing <|endoftext|>, passes the resulting list to decode, stores the returned text in a variable named strings, and prints it. The sequence of operations is shown in the code below. The variable names are the ones used in the example, and the sample text is a generated variant that includes the special marker so you can follow what happens to it.

python
Notes on the code
  • The two adjacent string literals are joined with no separator, the same way the source example joins its literals.
  • The allowed_special argument is passed explicitly in the encode call.
  • The integer list produced by encode is passed directly to decode.

Read the first printed line as a list of integers, not as words. Each integer is a token ID the tokenizer produced for a piece of the input. Two things stand out. The list is not the same length as the sentence in characters, because a token can cover more or fewer characters than one. Also, the value sitting where the special marker appeared is 50256 in the source output. That single integer stands for the whole marker, which is the key idea behind the special-token behavior explained later.

Encode: From Text to Token IDs

Definition

The encode method converts a text string into a list of integer token IDs. Its allowed_special parameter tells the tokenizer which special tokens it is permitted to recognize in the input.

Encoding works on the input text and produces one integer per token. Ordinary text is converted into ordinary token IDs. A special marker such as <|endoftext|> is a different case, because it is not meant to be read as plain characters. Whether the tokenizer treats it as a special token depends on the allowed_special argument. The diagram below shows the decision that encode makes when the input contains the marker.

Input textContains<|endoftext|>?Listed inallowed_special?Single special ID50256 in the source outputList of token IDsOrdinary text tokensmarker treated as plaincharactersscanyesyesno
When the input contains <|endoftext|>, which branch does encode take when allowed_special permits it, and what happens to the text in each case?

Following the diagram, the marker takes the left-hand branch only when the allowed_special argument names it. In that case the marker becomes one special ID, and the output list contains the value 50256 at that position. If the marker is not allowed, the text is handled as ordinary characters and split into ordinary text tokens. The source states this directly: the special marker is handled as a special token rather than being ignored as plain characters when the argument is passed.

The same contrast can be seen by comparing the two token sequences for the marker. The diagram below sets them side by side.

<|endoftext|>as ordinary textseveral text tokensone ID per piece<|endoftext|>allowed as specialID 50256one special IDsplitsingle ID
How does the token sequence for <|endoftext|> differ when it is encoded as one special ID compared with being split into ordinary text tokens?

Decode: From Token IDs Back to Text

The decode method runs the process in reverse. It takes a list of token IDs and returns the text they represent. Encode takes text to integers, and decode takes integers to text. Together they form a round trip. The sample code passes the integers produced by encode directly to decode, and the returned text is stored in strings and printed.

Original textencodetext to token IDsToken IDsa list of integersdecodetoken IDs to textReturned textinputIDsIDsoutput
After encoding a string to IDs and decoding those IDs back, what text comes out, and at which point can it differ from the original input?

The diagram shows the path that a check must follow. The returned text is compared with the original input. Any mismatch appears at the end of the path, but its cause could be at any stage: the input construction, the encode step, or the decode step. This is why the next section treats the comparison as a careful, character-level task rather than a glance at the printed output.

Checking the Round Trip

The most useful check is whether decode(encode(text)) reproduces the original text exactly. Return to the adjacent-literal question from the opening. The input contains terracesof with no space, because Python joins the two literals directly. The printed output in the example shows terraces of with a space. Two explanations are possible. Either a space was typeset in the sample output that the code does not contain, or the round trip changed the text. The example does not settle which, so do not assume either one.

Worked example

Locating where a round trip differs

Given an input string built from adjacent literals, check whether decode(encode(text)) returns the same string, and if not, find the first position where they differ.

  1. 1
    Build the input exactly as the code does

    Join the literals in the order written. Python adds nothing between them, so the input is a single string with no space at the join.

  2. 2
    Encode, then decode

    Pass the input to encode with allowed_special set as required, store the list, then pass that list to decode and store the returned text.

  3. 3
    Compare character by character

    Do not judge by eye from the printed output. Compare the two strings position by position, because a single missing or added space is easy to overlook when reading a line of text.

  4. 4
    Decide which stage changed the text

    If the strings match, the round trip is exact. If they differ, first inspect how the input was built. Only after ruling out the input should you suspect the tokenizer.

Result

The round trip is judged by the exact string comparison, not by how the printed output looks. In the terraces example, the input text itself contains no space at the join, so a space in the printed output must be checked before it is explained.

Mistakes Beginners Make with Round Trips

  • Common mistake

    Leaving out allowed_special when the input contains a special marker

    What it looks like
    tokenizer.encode(text) on a string that contains <|endoftext|>
    Why it fails

    The marker is then not accepted as a special token and is broken into ordinary text tokens, so the list no longer carries the special ID at that position.

    Do this instead

    Pass the marker explicitly, for example tokenizer.encode(text, allowed_special={"<|endoftext|>"}).

  • Common mistake

    Judging the round trip from the printed text alone

    What it looks like

    Reading terraces of in the output and assuming the input had the same space

    Why it fails

    The input built from adjacent literals may contain no space at that point, so a visual check can hide a real difference.

    Do this instead

    Compare the original and returned strings character by character.

  • Common mistake

    Reading the token ID list as words

    What it looks like

    Expecting the integers to look like the letters of the sentence

    Why it fails

    Each integer is a lookup key for a token, and the list length does not match the character count.

    Do this instead

    Use decode to recover text from the IDs, and do not read the integers directly.

Practice the Round Trip

MEDIUM

Write a short script that builds a string from two adjacent literals, encodes it with allowed_special containing <|endoftext|>, decodes the result, and checks whether the returned string equals the original. If they differ, print the first index where the characters disagree, and state whether the difference comes from how the input was built or from the round trip.

Hints
  • Build the input first and print it with repr so hidden or missing spaces are visible.
  • Compare the two strings with a loop over indices, not by eye.
  • Run the same check once with and once without allowed_special for a string that contains the marker, and compare the lists.

Key Takeaways

  • The encode method converts a text string into a list of integer token IDs, and decode takes those IDs back to text.
  • The allowed_special parameter tells encode which special tokens it may accept. Passed as a set containing <|endoftext|>, the marker becomes a single special ID, such as 50256 in the source output.
  • Without allowed_special, the marker is not treated as a special token and is split into ordinary text tokens, producing a different list.
  • A round trip is exact only if decode(encode(text)) reproduces the original text character by character. When it differs, inspect how the input was built before assuming the tokenizer is at fault.

Related concepts

More concepts starting with B