Encoding Text into Token IDs
The output tensor from the model has three dimensions: batch, one row per input token, and embedding values per token.
Try it: Text Vectorization: Tokens to Vectors
The text-to-tensor pipeline: standardize, split into tokens, build a frequency-ranked vocabulary with padding and [UNK], map tokens to integer ids, pad or truncate, then one-hot or multi-hot encode and look the ids up in an embedding table.
How it works
- Standardize: lowercase and delete punctuation.
- Split on whitespace into tokens.
- Build the vocabulary: 0 = padding, 1 = [UNK], then words by frequency (ties: first appearance), capped at max tokens.
- Map each token to its id (1 if it did not make the vocabulary), then pad with 0 or truncate to the sequence length.
- Encode: one-hot rows per position or one multi-hot row per text; embed: id k selects row k of the embedding table.
Default run (18 steps): Raw text (59 characters): "The movie was great. The plot was great, the acting wasn't!". A model cannot read strings, so it is turned into numbers step by step. … Embedding lookup: id k selects row k of a fixed 8 x 3 table, giving a (8, 3) dense matrix instead of (8, 8) sparse one-hots. Row 0 (padding) is looked up too; a trained Embedding layer would learn these rows.
Simplified: Bounded ASCII text (up to 200 characters, 40 tokens), whitespace splitting only, and a small FIXED embedding table (a real Embedding layer learns its rows). The multi-hot vector keeps the padding slot (always 0) so its positions line up with the ids.
Loading the simulation…
What the Model Returns Instead of an Answer
Once text has been converted into encoded token IDs, the next step is to hand those IDs to the model and see what comes back. A reasonable first guess is that a model asked whether a message is spam would answer with a yes or a no. It does not. The model returns a tensor full of numbers. This article is about understanding how that tensor is organized and how to pick out the part that matters for a classification task, such as deciding whether a message is 'spam' or 'not spam'.
What do you think happens?
When you pass encoded text into the model, what kind of result do you expect to get back?
Reveal answer
A tensor full of numbers
The model does not return a yes or no answer. It returns a tensor of numbers, and the task is to understand how that tensor is organized and which part of it matters for classification.
The program in this lesson runs the model on a small encoded input and prints both the output tensor and its shape. The shape is the key to everything else in this section. Read what the printed shape reports before reading any explanation of it, because the shape tells you how the numbers are arranged.
Reading the Three Dimensions of the Output
In the lesson's example the output tensor has three dimensions, and the reported shape is 1 x 4 x 2. Each dimension answers a different question. The first dimension has size 1 and holds the batch of inputs. In this example there is a single input, so the batch holds one item. The second dimension has size 4, and each entry along it corresponds to one input token. The third dimension has size 2, and it describes the embedding dimension, which is the number of values the model produces for each token.
The number of rows matches the number of input tokens, which is four in this case. Every row is one output vector, and that vector is what the model has produced to represent one token position. The rows do not represent words or labels yet. They are intermediate numeric descriptions of each position in the input. Each row contains exactly two numbers, which is why the third dimension has size 2.
Why Only the Last Token Row Matters
Fine-tuning for classification does not require the model to make a decision for all four output rows. The section focuses on a single output token, specifically the last row, which corresponds to the last input token. The model is being adjusted so that its output becomes a class label, a decision between 'spam' and 'not spam'. Only one decision is needed per input, so the model does not have to give a separate verdict for every token. The final token's row is chosen as the single point where the classification will be read from.
A useful way to think about this is that the last row is the only place a single decision is read from, so the rest of the output is not wasted but simply is not the part the classifier reads. The source does not explain in detail how information from earlier tokens reaches the last row, so this article does not claim more than that. What it does establish is the goal: one row per input, taken from the final token position.
Selecting the Last Row with Slicing
To pull out the last token's row, the section uses a slice that reads from the output tensor. The slice is outputs[:, -1, :]. Read it from left to right, one dimension at a time. The first position is a colon, which keeps the batch dimension whole. The second position is -1, which reduces the token dimension to its last position. The third position is a colon again, which keeps the embedding dimension whole.
The four rows are indexed by token position, and the last one is the row for the last input token. The source highlights this row as Row 3 in its numbering, which is the fourth row when counting from zero. Because -1 means the last position along an axis, the slice picks that final row without you needing to know how many tokens there are.
Slicing out the last token's row
The output tensor has shape 1 x 4 x 2. What shape does outputs[:, -1, :] produce, and why?
- 1Start with the full shape
The output has three dimensions: batch of 1, four tokens, and two embedding values per token.
- 2Keep the batch dimension
The first colon keeps the whole batch dimension, which has size 1.
- 3Reduce the token dimension to its last position
The -1 along the token dimension selects only the last of the four rows, so that dimension collapses to size 1 in the result.
- 4Keep the embedding dimension
The last colon keeps all two embedding values in the selected row.
- 5Count the dimensions of the result
The result has two dimensions, a batch of one row with two values, instead of the three dimensions of the full output.
The result has shape 1 x 2. It is the single row for the last input token, and its two values are the numbers the classifier reads.
From Text to Token IDs
The starting point is a short text prompt, the string "Hello, I am". Before the model can use it, the text has to pass through two conversions, first into token IDs and then into a tensor. The first conversion is performed by tokenizer.encode(input_text). Its input is a string and its output is a list of integers. The integers are the representation the rest of the pipeline works with.
The text itself is not passed to the tensor step. Only the list of IDs is. Each piece of the input text corresponds to a specific integer in the encoded list, and the position of that integer in the list matches the position of the token in the input. The example in this lesson encodes its prompt into four token IDs, which is why the model output has four rows.
- 1Start with the input text, a string such as "Hello, I am".
- 2Call
tokenizer.encode(input_text)to produce a Python list of integer token IDs. - 3Convert that list into a PyTorch tensor with
torch.tensor(encoded). - 4Add a batch dimension with
unsqueeze(0)so the tensor matches what the model expects. - 5Pass the prepared tensor to the model and inspect the output tensor and its shape.
Each stage consumes the output of the previous one, so a problem at any stage propagates to every later stage. Checking the printed encoded list and the tensor shape separates encoding problems from tensor-preparation problems. If the list of IDs looks wrong, the trouble is in encoding. If the list looks right but the shape is wrong, the trouble is in tensor preparation.
Turning the ID List into a Tensor
The expression torch.tensor(encoded) converts the Python list of token IDs into a PyTorch tensor. The values inside the tensor do not change during this step. What changes is the container: a plain Python list becomes a tensor that the model can operate on. The code below uses a short list of illustrative IDs, not the real IDs for the prompt, to show the shape reported at each stage.
encoded = [5, 12, 7, 3]
ids = torch.tensor(encoded)
print(ids)
print(ids.shape)
batch = ids.unsqueeze(0)
print(batch.shape)- 1Line 1 assigns the list of token IDs. In the lesson these come from
tokenizer.encode(input_text), and the values here are placeholders. - 2Line 2 converts the Python list into a PyTorch tensor with
torch.tensor(encoded). - 3Line 3 prints the tensor so you can check the values survived conversion unchanged.
- 4Line 4 prints the shape of the tensor before any batch dimension is added. It is one dimension of size 4.
- 5Line 5 adds the batch dimension at index 0 with
unsqueeze(0). - 6Line 6 prints the shape after unsqueeze, which is 1 x 4.
- encoded is a Python list of four integer token IDs
torch.tensor(encoded)converts the list into a tensor with the same four values- The tensor has one dimension of size 4, one entry per token
unsqueeze(0)adds a new dimension at index 0, the batch dimension- The batch tensor has shape 1 x 4
tensor([ 5, 12, 7, 3])
torch.Size([4])
torch.Size([1, 4])Adding the Batch Dimension with unsqueeze
The method call .unsqueeze(0) 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 the final stage of the pipeline adds the batch dimension, which is why its effect shows up in the shape printed at the end of the trace.
ids = torch.tensor(encoded)
print(ids.shape)torch.Size([4]): one dimension, one entry per token, no batch dimension
batch = ids.unsqueeze(0)
print(batch.shape)torch.Size([1, 4]): a batch of one input containing four tokens
What do you think happens?
A tensor of token IDs has shape [4]. What shape do you get after calling unsqueeze(0) on it?
Reveal answer
[1, 4]
unsqueeze(0) inserts a new dimension of size 1 at index 0. The original four entries move into the second position, giving a batch of one with four tokens.
Mistakes Beginners Make with Shapes
Most shape problems in this pipeline come from a small set of errors. The items below show each mistake with the correction the source's reasoning supports.
Common mistake Treating each output row as a word or a label
What it looks likeReading the 1 x 4 x 2 output and assuming row 0 is the word Hello.
Why it failsThe rows are intermediate numeric descriptions of each token position. They do not represent words or labels yet.
Do this insteadThink of each row as a two-number vector for one token position, and remember that the label is not produced in this section.
Common mistake Selecting the wrong slice and getting the wrong shape
What it looks likeWriting outputs[:, -1] and expecting the 1 x 2 result from the lesson.
Why it failsThe slice outputs[:, -1, :] keeps the batch and embedding dimensions and selects the last token. Dropping the final colon changes which dimensions are kept.
Do this insteadUse outputs[:, -1, :] and confirm the result has shape 1 x 2 by printing its shape.
Common mistake Blaming the model for a shape error
What it looks likeAssuming the model is broken when the slice prints the wrong shape.
Why it failsIf the slice does not come out as 1 x 2, the error lies in the indexing, not in the model.
Do this insteadPrint the shape of the slice and check the indexing before changing anything in the model.
Common mistake Skipping unsqueeze(0) and then wondering why the shape is off
What it looks likePassing a tensor of shape [4] to the model with no batch dimension.
Why it failsThe model expects its input to carry a batch dimension, and this step adds it. The token values do not change, so the shape is what differs.
Do this insteadAdd the batch dimension with unsqueeze(0) before passing the tensor into the model.
Common mistake Confusing an encoding problem with a tensor-preparation problem
Print the encoded list, the tensor shape, and the slice shape at each stage. If the list is wrong, look at encoding. If the list is right but the shape is wrong, look at the tensor step. If the model output looks right but the slice is wrong, look at the indexing.
Practice: Tracing Shapes Through the Pipeline
A model output tensor has shape 1 x 6 x 2. Without running code, state what each dimension stands for, then state the shape of outputs[:, -1, :] and name the row it selects.
Hints
- The first dimension is the batch, the second is one row per input token, and the third is the embedding values per token.
- A -1 along the token dimension selects the last position.
- Keeping the batch and embedding dimensions whole gives a result with two dimensions.
You encode a prompt and get a Python list of five token IDs. Describe the shape after torch.tensor(encoded), the shape after unsqueeze(0), and at which stage a shape problem would first appear if the model rejected the input for lacking a batch dimension.
Hints
- The tensor from a flat list of five values has one dimension.
unsqueeze(0)inserts a size-1 batch dimension at index 0.- A missing batch dimension is a tensor-preparation issue, so it appears at the unsqueeze stage, not during encoding.
Key Takeaways
- The model returns a three-dimensional output tensor: batch, one row per input token, and embedding values per token, with shape such as 1 x 4 x 2 in the lesson's example.
- The slice outputs[:, -1, :] keeps the batch and embedding dimensions and selects the last token's row, producing a 1 x 2 result that the classifier is fine-tuned on.
tokenizer.encode(input_text)turns a string into a list of integer token IDs, andtorch.tensor(encoded)converts that list into a PyTorch tensor.unsqueeze(0)adds a batch dimension at index 0, which the model requires even though the token values are unchanged.- When a shape is wrong, print the encoded list, the tensor shape, and the slice shape at each stage to find whether the problem is in encoding, tensor preparation, or indexing.