Concepts / Generating Text with GPT

Generating Text with GPT

text_to_token_ids uses a tokenizer's encode method to turn text into token IDs, and then adds a batch dimension with unsqueeze(0).

  • Programming

Why the Model Never Sees Words

A GPT-style model does not read words the way a person does. It operates on numbers. That means every prompt you give it must first be converted into a sequence of integer token IDs before the model can work with it, and every sequence of token IDs the model produces must be converted back into text before a person can read it. Text in, numbers through the model, text out: this two-way conversion is the subject of this article.

The source focuses on two utility functions that handle those conversions. One turns text into token IDs. The other turns token IDs back into text. Your job is not to memorize the code but to understand what each line is responsible for and to predict what the data looks like at each stage. Before going further, try to predict what happens to a prompt as it moves through the pipeline. Which conversions must happen, and in which order?

The Tokenizer as the Text-to-Number Bridge

The source uses the tiktoken library and requests the gpt2 encoding through tiktoken.get_encoding('gpt2'). The tokenizer object is the component that knows how to map pieces of text to integer IDs and back again. Both utility functions receive the tokenizer as an argument. This matters because the same tokenizer must be used in both directions. If the encoder and decoder disagreed about what each integer means, the text that came out would not match the text that went in.

Definition

Token ID: an integer that the tokenizer assigns to a piece of text. The model works with these integers. The tokenizer's decode method maps them back to the text they represent.

Think of the tokenizer's vocabulary as a lookup table that runs in both directions. Encoding looks up each piece of text and returns its integer. Decoding takes the integer and returns the text it stands for. Because the table is shared, a round trip through the same tokenizer should recover the original text.

Tracing text_to_token_ids Step by Step

python
Walk through it
  1. 1
    Line 1 defines text_to_token_ids, which accepts the prompt text and the tokenizer.
  2. 2
    Line 2 calls tokenizer.encode on the text. The result is a plain list of integers, one per token.
  3. 3
    Line 3 wraps that list in a tensor and adds a batch dimension with unsqueeze(0).
  4. 4
    Line 4 returns the batched tensor, which is what the model call receives.
  5. 5
    The token_ids_to_text function reverses the batch step with squeeze(0), converts the result to a list, and decodes it.
Notes on the code
  • tokenizer.encode(text, allowed_special={'<|endoftext|>'}) turns the prompt string into a list of integer token IDs; the allowed_special argument permits the special marker '<|endoftext|>' and treats it as a special token rather than ordinary text
  • torch.tensor(encoded) wraps the list of IDs in a tensor
  • .unsqueeze(0) adds a leading batch dimension so the model receives input with the shape it expects
  • .squeeze(0) removes that leading batch dimension before decoding
  • flat.tolist() converts the tensor back into a plain Python list
  • tokenizer.decode(...) asks the tokenizer to turn that list of IDs into text

The source code does not show a torch import, and it refers to model and GPT_CONFIG_124M without defining them. Those names come from earlier material in the same chapter and must already exist when the code runs. Treat the snippet above as the two functions in standard Python form, with the calls exactly as the source gives them.

What unsqueeze(0) Does to the Shape

Encoding produces a flat, one-dimensional list of IDs. A flat list of three IDs has a shape of three elements, with no leading axis. The model, however, expects a batch: a leading dimension that holds one or more input sequences. unsqueeze(0) inserts a new axis at position 0. The result has a leading dimension of size 1, which holds the single prompt, followed by the axis of token IDs. The original sequence is unchanged; it is just wrapped one level deeper.

Before
token_ids = [ID 1, ID 2, ID 3]
shape: (3,)

One-dimensional list of three token IDs

After
batched = [[ID 1, ID 2, ID 3]]
shape: (1, 3)

Two-dimensional tensor: a batch of one sequence of three IDs

Worked example

Tracing a prompt into the model input

Suppose a prompt encodes to three token IDs. What shape does the input have when it leaves text_to_token_ids, and what does the leading dimension contain?

  1. 1
    Encode the prompt

    tokenizer.encode returns a list of three integers, one per token. As a tensor, this has shape (3,).

  2. 2
    Apply unsqueeze(0)

    The call inserts a new leading axis at position 0. The shape becomes (1, 3).

  3. 3
    Read the leading dimension

    The leading dimension of size 1 holds the single prompt. The model sees one batch containing one sequence of three IDs.

Result

The input has shape (1, 3). Its leading dimension is the batch dimension, and it contains the one prompt that was encoded.

Pipeline from Prompt to Generated Text

The full round trip follows a fixed order. The prompt starts as a string. The tokenizer encodes it into a list of integer token IDs. text_to_token_ids wraps that list in a tensor and adds a batch dimension so the model receives input with the expected shape. The model call generate_text_simple then produces more token IDs. token_ids_to_text removes the batch dimension, converts the tensor back to a plain list, and asks the tokenizer to decode that list into text. The diagram below shows the sequence and where the model sits within it.

Prompt texttokenizer.encodetext to token IDsToken ID listunsqueeze(0)add batch dimensionBatched tensorshape (1, N)generate_text_simplethe model produces more IDssqueeze(0)remove batch dimensiontokenizer.decodetoken IDs to textGenerated textstringlist of intswrap as tensorbatched inputmodel calloutput IDsflat IDsdecoded
What happens to the prompt text at each step, from the moment it is encoded to the moment the output text is decoded, and where does the model sit in that sequence?

Notice that the model sits in the middle of the chain, between two conversions. It never receives raw text and it never returns raw text. Everything it reads and writes is a sequence of token IDs. This is why the encoding and decoding stages are essential rather than cosmetic.

Removing the Batch Dimension Before Decoding

The two batch-dimension operations are paired. unsqueeze(0) is applied on the way into the model, and squeeze(0) is applied on the way out, before decoding. Because they mirror each other, the decoder receives a flat sequence of IDs rather than a nested one. The tokenizer's decode method expects that flat list, so the squeeze step is what makes the final conversion possible.

Before
output = [[ID 4, ID 5, ID 6, ID 7]]
shape: (1, 4)

Two-dimensional output with a batch axis of size 1

After
flat = [ID 4, ID 5, ID 6, ID 7]
shape: (4,)

One-dimensional sequence ready for tolist() and decode

Follow the data in that example. The model output still has the batch axis from the input. squeeze(0) drops that axis, flat.tolist() turns the tensor into a Python list of four integers, and tokenizer.decode converts that list into the text those four IDs represent.

Predicting the Output of an Untrained Model

What do you think happens?

When you run the listing before training, the printed text looks like nonsense. Is the problem most likely in the conversion functions or in the model?

  • The encoding or decoding functions are broken

  • The model has not yet been trained, so its generated text is not coherent

Reveal answer
Answer:

The model has not yet been trained, so its generated text is not coherent

The source states that the model has not yet undergone training, so the generated text is not coherent, and that the conversion functions are not the cause of that output. If the round trip itself is correct, the incoherent text reflects the untrained model rather than a failure in encoding or decoding.

This distinction is the core of debugging the round trip. Incoherent output does not automatically mean the conversions failed. The source treats the round trip as something to check separately from the quality of the model's output.

Locating the Source of a Wrong Output

When a generated result looks wrong, check the stages of the round trip in order rather than guessing. The flowchart below suggests a sequence of checks. Each check narrows the cause before you move on, so that a problem in one stage is not blamed on another.

Unexpected outputCheck encoded IDsdoes the prompt become theexpected IDs?Check tensor shapeis there one batch axis ofsize 1?Check decoding pathsqueeze, tolist, decodewith the same tokenizerSuspect the untrainedmodelwhen the round trip checksoutstart hereIDs correctshape correctdecoding correct
Which stage of the round trip (encoding, tensor reshaping, model generation, or decoding) should be checked first when the generated text looks wrong?
  • Common mistake

    Blaming the conversion functions for incoherent text

    Why it fails

    The source states that incoherent output reflects an untrained model, not necessarily a failure in the conversion functions.

    Do this instead

    Verify the round trip separately first. If encoding and decoding recover the expected IDs and text, the model is the more likely cause.

  • Common mistake

    Using different tokenizers for encoding and decoding

    Why it fails

    Both directions must use the same tokenizer so that token IDs and text stay consistent.

    Do this instead

    Pass the same tokenizer object to both text_to_token_ids and token_ids_to_text.

  • Common mistake

    Forgetting the batch dimension when decoding

    Why it fails

    The decoder expects a flat sequence of IDs. The model output still carries the batch axis added on the way in.

    Do this instead

    Apply squeeze(0) before tolist() and decode, mirroring the unsqueeze(0) applied before the model.

  • Common mistake

    Passing a prompt string directly to the model instead of its token IDs, because the model does not read words

Looking Ahead to Loss and Evaluation

The source raises a natural follow-up question. If incoherent output is expected from an untrained model, how do we define what makes text coherent or high quality, and how do we measure it? The source answers by introducing a numerical method, a loss metric, that evaluates the generated content. The loss metric is described as a progress and success indicator during training, and it is the subject of the next subsection of the chapter. Later chapters will also review additional methods for assessing model quality when the LLM is fine-tuned.

Practice: Tracing the Round Trip

MEDIUM

A prompt encodes to five token IDs. Trace its shape through text_to_token_ids. Then suppose the model returns a batched output of shape (1, 7). Trace that output through token_ids_to_text. State the shape at each stage and say which function changes it.

Hints
  • Encoding returns a flat list, so start with one dimension of size 5.
  • unsqueeze(0) adds a leading axis of size 1.
  • squeeze(0) removes the leading axis, which must have size 1 to be removed cleanly.

Key Takeaways

  • A GPT-style model works on integer token IDs, not raw text, so the prompt must be encoded before the model runs and the output must be decoded afterward.
  • text_to_token_ids encodes the prompt with the tokenizer's encode method, then wraps the IDs in a tensor and adds a batch dimension with unsqueeze(0).
  • token_ids_to_text reverses the batch step with squeeze(0), converts the tensor to a list, and uses the same tokenizer's decode method to return text.
  • Incoherent output from an untrained model does not by itself indicate a conversion bug, so check the round trip stage by stage before blaming the encoding or decoding.

Related concepts

More concepts starting with G