Text Generation with GPT Models
The model expects its input to carry a batch dimension, so this step is required even though the token values themselves do not change.
A Text Prompt Must Become Numbers
A GPT model does not read the characters of a sentence the way a person does. It works with numbers. Before any text generation can happen, the prompt has to be translated into a form the model can accept. This article follows one concrete goal from start to finish: take the short prompt "Hello, I am" and prepare it as input for the generate_text_simple function.
Preparing the prompt takes two conversions. The first turns the string into a list of integer token IDs. The second turns that Python list into a PyTorch tensor, and adds a batch dimension to it. Each conversion produces an intermediate result that you can inspect, and learning to inspect those results is the main skill of this article. When something goes wrong, the printed list and the printed tensor shape tell you which conversion caused the problem.
Tracing the Prompt Through Each Stage
Read the preparation code as a chain of stages. Each stage consumes the output of the previous one. The prompt string goes into the tokenizer, the tokenizer's list goes into torch.tensor, and the tensor is then reshaped with unsqueeze(0). Nothing in the chain can be skipped, and a problem introduced early is carried forward into every later stage.
Notice that the text itself does not travel past the first stage. Only the list of IDs is passed to the tensor step. This is why, if the final tensor looks wrong, you should first check whether the list is wrong. The tensor step can only convert what it is given.
What tokenizer.encode() Returns
tokenizer.encode(input_text) takes a string as its input and returns a list of integers. Each integer is a token ID. These IDs are the representation the rest of the pipeline works with.
Think of the encoder as a lookup from pieces of text to numbers. The string goes in, and a list of numbers comes out, with one number standing for each token the tokenizer identified in the text. The exact numbers depend on the tokenizer's vocabulary, so the same text can produce different IDs under a different tokenizer. For that reason, you should always print the encoded list rather than assume what it contains.
Turning the List into a Tensor
The expression torch.tensor(encoded) converts the Python list of token IDs into a PyTorch tensor. After this step the data has a shape, and that shape is what the model checks. A flat list of n token IDs becomes a one-dimensional tensor whose shape has a single entry, the number of tokens.
[101, 7, 42]
torch.Size([1, 3])- prompt holds the string that will be encoded
tokenizer.encode(prompt)returns a Python list of integer token IDstorch.tensor(encoded)converts that list into a tensor, thenunsqueeze(0)adds the batch dimension- The two print calls report the intermediate list and the final shape
Adding the Batch Dimension with unsqueeze(0)
The method call .unsqueeze(0) adds a new dimension at index 0. This new leading dimension is the batch dimension. The model expects its input to carry a batch dimension, so this step is required even though the token values themselves do not change. The same numbers are still there; they are simply wrapped in one more level of structure.
tensor([101, 7, 42]) # shape [3]A one-dimensional tensor with 3 token IDs
tensor([[101, 7, 42]]) # shape [1, 3]A two-dimensional tensor with one batch row containing the same 3 token IDs
What do you think happens?
If the tensor before unsqueeze(0) has shape [3], what shape does it have after unsqueeze(0)?
Reveal answer
[1, 3]
unsqueeze(0) inserts a new dimension of size 1 at index 0. The original dimension of size 3 moves to index 1, giving shape [1, 3]. The values are unchanged.
Tracing the shape of a longer prompt
Suppose the encoded list for a prompt has 5 token IDs. What shape does the tensor have before and after unsqueeze(0)?
- 1Start with the list
The encoded result is a flat Python list with 5 integers.
- 2Apply torch.tensor
The list becomes a one-dimensional tensor whose only dimension has size 5, so its shape is [5].
- 3Apply unsqueeze(0)
A new dimension of size 1 is inserted at index 0, and the existing dimension of size 5 shifts to index 1.
- 4Read the result
The final shape is [1, 5]: one batch entry containing a sequence of 5 token IDs.
Before unsqueeze(0) the shape is [5]; after unsqueeze(0) the shape is [1, 5].
Locating Where a Shape Mismatch Starts
When the model rejects its input, the shape is usually the first clue, but the shape alone does not say where the problem began. Because each stage consumes the previous stage's output, you trace backward through the chain. Start by printing the encoded list. If the list already has the wrong length or wrong content, the problem is in the encoding stage. If the list looks right but the tensor shape is unexpected, the problem is in the tensor-preparation stage, most often at torch.tensor or unsqueeze.
Mistakes When Preparing Model Input
Common mistake Passing the original string to torch.tensor
What it looks liketorch.tensor("Hello, I am")Why it failsThe tensor step is meant to receive the list of integer token IDs produced by the tokenizer. Only the list is passed to this step, and the text itself is not.
Do this insteadPass the encoded list: torch.tensor(encoded).Common mistake Forgetting that unsqueeze(0) is required
What it looks likeinput_tensor = torch.tensor(encoded)Why it failsThe model expects its input to carry a batch dimension. Without
unsqueeze(0), the tensor has shape [n] and lacks the leading batch dimension, even though the token values are correct.Do this insteadAdd the batch dimension with .unsqueeze(0) after creating the tensor.
Common mistake Assuming the shape change alters the token values
Why it failsunsqueeze(0)only adds a dimension. The same token IDs remain in the same order, so a mismatch is not caused by the batch dimension changing the content.Do this insteadCompare the printed list with the tensor contents to confirm that the values are unchanged.
Common mistake Treating the printed IDs as universal
Why it failsToken IDs come from the tokenizer's vocabulary, so the numbers depend on which tokenizer is used.
Do this insteadAlways print the encoded list from your own tokenizer rather than relying on numbers copied from elsewhere.
Practice Tracing the Pipeline
A colleague runs the preparation code on a prompt and reports that the model rejects the input. The printed encoded list has 4 integers, but the printed tensor shape is [4] instead of [1, 4]. Which stage is responsible, and what single change would fix it?
Hints
- Ask which stage adds the batch dimension.
- The encoded list is correct, since it has 4 integers.
- Look at whether
unsqueeze(0)was applied after torch.tensor.
Write the three lines that take a string called prompt, encode it, convert it to a tensor with a batch dimension, and print both the encoded list and the final shape. Then predict the printed shape for a prompt that encodes to 6 token IDs before you run it.
Hints
- Use
tokenizer.encode(prompt)for the first step. - Apply torch.tensor and then .unsqueeze(0) to the encoded list.
- A list of 6 IDs gives a shape of [1, 6] after the batch dimension is added.
Key Takeaways
tokenizer.encode(input_text)converts a string into a list of integer token IDs, and only that list moves on to the tensor step.torch.tensor(encoded)converts the Python list into a PyTorch tensor, whose shape reflects the number of token IDs.- .unsqueeze(0) adds a batch dimension at index 0, turning a shape [n] tensor into a shape [1, n] tensor without changing the token values.
- When a shape mismatch appears, print the encoded list first, then the tensor shape, to decide whether the problem is in encoding or tensor preparation.