Concepts / Tensors and Shapes in PyTorch

Tensors and Shapes in PyTorch

The raw text is not passed to the tensor step; only the list of token IDs is.

  • Programming

Why a Prompt Has to Be Converted Before a Model Can Use It

Suppose you want a language model to continue a short piece of text. The concrete goal here is a prompt, the string "Hello, I am", which must be prepared as input for the generate_text_simple function. A model does not read characters the way a person does. Before the model can use the prompt, the text has to pass through two conversions: first into token IDs, and then into a tensor. Each conversion produces something the next stage can consume, and the whole path is a chain in which every stage depends on the one before it.

This article follows that chain one stage at a time. You will see what each stage receives, what it returns, and how the shape of the data changes along the way. By the end you should be able to look at a printed list and a printed shape and say which stage a problem most likely came from.

Tracing the Prompt Through Each Stage

The preparation takes four lines of logic. One line assigns the prompt to a variable. The second encodes it. The third converts the encoded result into a tensor with a batch dimension. The remaining two lines print what the encoding and tensor stages produced, so you can inspect each intermediate result instead of trusting the final output alone.

python
Notes on the code
  • input_text holds the raw string prompt
  • tokenizer.encode(input_text) returns a Python list of integers, not text
  • torch.tensor(encoded) builds a tensor from that list of IDs
  • .unsqueeze(0) adds a new leading dimension of size 1, the batch dimension
  • The final print reports the shape that the model will receive

The first stage is tokenizer.encode(input_text). Its input is a string and its output is a list of integers. Those integers are the representation the rest of the pipeline works with. The raw text is not passed to the tensor step; only the list of token IDs is. This means that everything downstream depends on what the encoder returned, so the printed list is the first thing to check.

The second stage is torch.tensor(encoded), which converts the Python list of token IDs into a PyTorch tensor. A list of four integers becomes a one-dimensional tensor with four elements. The values stay the same; what changes is that the data now lives in the tensor type that PyTorch operations expect.

The third stage is the call .unsqueeze(0), which adds a new dimension at index 0. This 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. Only this final stage adds the batch dimension, which is why its effect appears in the shape printed at the end of the trace.

Hello, I aminput stringtokenizer.encodestring to token IDslist of token IDsPython list of integerstorch.tensorshape (4,)unsqueeze(0)adds batch dimensionmodel inputshape (1, 4)stringintegersencoded list1-D tensor2-D tensor
At which stage does the shape first change, and what does each stage do to the data?

The diagram is a pipeline, so each stage consumes the output of the previous one. A problem at any stage propagates to every later stage. If the encoder returns the wrong integers, the tensor will faithfully hold the wrong integers. If the tensor is built correctly but the batch dimension is missing, the model receives the right values with the wrong shape.

Reading a Shape as Nested Brackets

A shape tells you how many values sit at each level of nesting. Each number in a shape corresponds to one level of brackets. A shape of $(4,)$ describes a single bracketed group containing four values, such as a flat list of four token IDs. A shape of $(1, 4)$ describes an outer group containing exactly one inner group, and that inner group holds four values. The first number is the outermost level and the last number is the innermost.

StageShapeOuter levelInner level
After torch.tensor(encoded)$(4,)$4 token IDs, no batch wrappernone
After .unsqueeze(0)$(1, 4)$1 batch entry4 token IDs

How the same four IDs are grouped before and after the batch dimension is added

Worked example

Reading the shape of an encoded prompt

The encoder returns a list of four integer IDs. What shape does torch.tensor(encoded) produce, and what shape does the model receive after unsqueeze(0)?

  1. 1
    Count the elements

    The Python list has four integers, so the one-dimensional tensor has one dimension of size 4.

  2. 2
    Write the first shape

    A one-dimensional tensor with four elements has shape $(4,)$.

  3. 3
    Apply unsqueeze(0)

    unsqueeze(0) inserts a new dimension at index 0 with size 1. The four IDs are unchanged, but they are now wrapped in one batch entry.

  4. 4
    Write the second shape

    The new shape is $(1, 4)$: a batch dimension of size 1 followed by the sequence of four token IDs.

Result

torch.tensor(encoded) has shape $(4,)$, and after unsqueeze(0) the model input has shape $(1, 4)$.

What unsqueeze(0) Changes and What It Does Not

The method call .unsqueeze(0) adds a new dimension at index 0. Index 0 is the front of the shape, so the new dimension becomes the outermost level of nesting. The token values themselves are not altered and not reordered. The call only changes how those values are grouped. This is why the source describes the step as adding a batch dimension: it wraps the existing sequence so that the model sees a batch containing one item.

Before
encoded_tensor = torch.tensor(encoded)
# shape: (4,)

A one-dimensional tensor holding four token IDs directly.

After
encoded_tensor = torch.tensor(encoded).unsqueeze(0)
# shape: (1, 4)

A two-dimensional tensor whose first dimension is the batch of size 1.

shape (4,)one dimension4 token IDsdimension 0 has size 4shape (1, 4)two dimensionsbatch dimensiondimension 0 has size 14 token IDsdimension 1 has size 4holdsouterinner
Before and after unsqueeze(0), how do the shape and the nesting change, and where does the new size-1 dimension appear?

Reading the diagram from left to right shows the transformation. The four token IDs survive intact. What changes is that a new outer level of size 1 now encloses them, and that level sits at index 0 because the call specifies 0. Calling unsqueeze with a different index would place the size-1 dimension somewhere else, but the batch dimension described in the source is the one created at index 0.

Tokens as Integers and Vocabulary Lookup

The encoder is the stage that converts text into the integer representation the rest of the pipeline uses. The source states the key contract: tokenizer.encode(input_text) converts a string into a list of integer token IDs. Each integer stands in for a piece of the input. The exact integer values depend on the tokenizer, so the number you see for a given word is a property of that tokenizer rather than something you can derive from the text alone.

In the real world

Imagine a prompt that encodes to the list [101, 7, 42, 9]. These numbers are hypothetical and chosen only for illustration. The tensor step turns that list into a tensor of shape $(4,)$, and unsqueeze(0) turns it into a tensor of shape $(1, 4)$. Notice that the values 101, 7, 42, and 9 are the same in every stage; only their grouping changes.

Mistakes That Hide the Real Source of a Shape Problem

The source gives a practical way to separate two kinds of failure. Checking the printed encoded list and the printed tensor shape tells you whether the problem is in encoding or in tensor preparation. The following mistakes are the ones most often confused with each other.

  • Common mistake

    Assuming the tensor is wrong when the encoded list is already wrong

    What it looks like

    The printed list contains unexpected IDs, and the developer starts changing unsqueeze or torch.tensor calls.

    Why it fails

    Each stage consumes the output of the previous one, so a bad encoding propagates into the tensor. The tensor faithfully stores bad values.

    Do this instead

    Print the encoded list first. If the integers are wrong, the problem is in the encoding stage, not the tensor stage.

  • Common mistake

    Forgetting the batch dimension

    What it looks like

    The tensor has shape (4,) and the model is given it directly as input.

    Why it fails

    The model expects its input to carry a batch dimension, so a one-dimensional tensor does not match what it expects.

    Do this instead

    Apply unsqueeze(0) after torch.tensor(encoded) so the shape becomes (1, 4).

  • Common mistake

    Thinking unsqueeze(0) changes the token values

    What it looks like

    The developer expects the IDs to be modified or reordered after calling unsqueeze(0).

    Why it fails

    The call only inserts a new dimension of size 1 at index 0. The values are unchanged.

    Do this instead

    Compare the elements before and after; they should match, while only the nesting and shape differ.

  • Common mistake

    Passing the raw string into the tensor step

    What it looks like

    Calling torch.tensor on the prompt string instead of the encoded list.

    Why it fails

    The tensor step receives only the list of token IDs. The text itself is not passed forward.

    Do this instead
    Pass the output of tokenizer.encode(input_text) into torch.tensor.

Predicting the Shape Before Running the Code

What do you think happens?

If the encoded list has four integers, what is the shape of torch.tensor(encoded).unsqueeze(0)?

  • (4,)

  • (1, 4)

  • (4, 1)

  • (1, 1, 4)

Reveal answer
Answer:

(1, 4)

torch.tensor(encoded) produces shape (4,). unsqueeze(0) inserts a new dimension of size 1 at index 0, giving (1, 4).

MEDIUM

A colleague reports that the model fails with an input shape error. The printed encoded list looks correct, but the printed shape is (7,) when the model expects a batch dimension. Which stage should you inspect, and what single call would most likely fix the shape?

Hints
  • The encoded list is correct, so the encoding stage is not the first suspect.
  • A missing leading dimension of size 1 is the signature of a missing unsqueeze(0).

Pipeline Recap

  1. 1
    tokenizer.encode(input_text) converts a string into a list of integer token IDs, and only that list is passed to the tensor step.
  2. 2
    torch.tensor(encoded) turns the Python list into a PyTorch tensor, producing shape $(4,)$ for four IDs.
  3. 3
    unsqueeze(0) adds a batch dimension at index 0, producing shape $(1, 4)$ so the model receives input in the form it expects.
  4. 4
    Printing the encoded list and the tensor shape separates encoding problems from tensor-preparation problems.

Key Takeaways

  • tokenizer.encode(input_text) converts a string into a list of integer token IDs.
  • torch.tensor(encoded) converts that Python list into a PyTorch tensor without changing the values.
  • unsqueeze(0) adds a batch dimension at index 0, so a shape of (4,) becomes (1, 4).
  • Each stage consumes the previous stage's output, so checking the printed list and shape locates the source of a mismatch.

Related concepts

More concepts starting with T