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).
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.
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
- 1Line 1 defines
text_to_token_ids, which accepts the prompt text and the tokenizer. - 2Line 2 calls tokenizer.encode on the text. The result is a plain list of integers, one per token.
- 3Line 3 wraps that list in a tensor and adds a batch dimension with
unsqueeze(0). - 4Line 4 returns the batched tensor, which is what the model call receives.
- 5The
token_ids_to_textfunction reverses the batch step withsqueeze(0), converts the result to a list, and decodes it.
- tokenizer.encode(text,
allowed_special={'<|endoftext|>'}) turns the prompt string into a list of integer token IDs; theallowed_specialargument 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 listtokenizer.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.
token_ids = [ID 1, ID 2, ID 3]
shape: (3,)One-dimensional list of three token IDs
batched = [[ID 1, ID 2, ID 3]]
shape: (1, 3)Two-dimensional tensor: a batch of one sequence of three IDs
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?
- 1Encode the prompt
tokenizer.encode returns a list of three integers, one per token. As a tensor, this has shape (3,).
- 2Apply unsqueeze(0)
The call inserts a new leading axis at position 0. The shape becomes (1, 3).
- 3Read the leading dimension
The leading dimension of size 1 holds the single prompt. The model sees one batch containing one sequence of three IDs.
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.
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.
output = [[ID 4, ID 5, ID 6, ID 7]]
shape: (1, 4)Two-dimensional output with a batch axis of size 1
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?
Reveal 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.
Common mistake Blaming the conversion functions for incoherent text
Why it failsThe source states that incoherent output reflects an untrained model, not necessarily a failure in the conversion functions.
Do this insteadVerify 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 failsBoth directions must use the same tokenizer so that token IDs and text stay consistent.
Do this insteadPass 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 failsThe decoder expects a flat sequence of IDs. The model output still carries the batch axis added on the way in.
Do this insteadApply 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
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_idsencodes the prompt with the tokenizer's encode method, then wraps the IDs in a tensor and adds a batch dimension withunsqueeze(0).token_ids_to_textreverses the batch step withsqueeze(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.