Implementing Transformer Blocks in GPT Architecture
A TransformerBlock is a component that processes input data, and in this source's example it is called as output = block(x).
Predicting the Output Shape
This article is about running a TransformerBlock on sample data and checking what comes back. The approach is to look at the code first, predict what it should produce, and then confirm the prediction from the real output. Each claim here can be checked by running the same few lines yourself.
What do you think happens?
You create an input tensor x with shape [2, 4, 768] and pass it through a TransformerBlock with output = block(x). What shape do you expect output to have?
Reveal answer
torch.Size([2, 4, 768])
The source reports that the printed output shape matches the input shape. The TransformerBlock maintains the input shape, so the batch size, the number of tokens, and the embedding dimension all stay the same.
Reading the Three Dimensions
The sample input is built with the line x = torch.rand(2, 4, 768). Its shape is described as [batch_size, num_tokens, emb_dim]. Read in that order, the three numbers mean: 2 sequences in the batch, 4 tokens in each sequence, and 768 values for each token. Each dimension answers a different question about the data.
The last dimension is the embedding dimension. This is why the source uses 768 as the embedding size for this configuration. Each of the four token positions in each sequence is represented by one vector of 768 values. If you change only one number, you change only one property of the data: the batch size changes how many sequences you process, the token count changes how long each sequence is, and the embedding size changes how many values describe each token.
| Dimension | Position in shape | Value in the sample | What it counts |
|---|---|---|---|
| batch_size | First | 2 | Sequences in the batch |
| num_tokens | Second | 4 | Token positions in each sequence |
| emb_dim | Third (last) | 768 | Values that describe each token |
Meaning of each dimension in the sample input shape [2, 4, 768]
Running the Call and Reading Both Shapes
The snippet is short. It creates a random input tensor, passes it through a TransformerBlock, and prints the shape of both the input and the output. The source gives the configuration as GPT_CONFIG_124M and assumes that both the TransformerBlock class and this configuration already exist. This step only uses them.
import torch
block = TransformerBlock(GPT_CONFIG_124M)
x = torch.rand(2, 4, 768)
output = block(x)
print("Input shape:", x.shape)
print("Output shape:", output.shape)- 1Stage one creates the input: x holds random values with shape 2 by 4 by 768.
- 2Stage two constructs the block from GPT_CONFIG_124M.
- 3Stage three calls the block on x and stores the result in output.
- 4Stage four prints the shape of x and the shape of output, one per line.
- The import line brings in torch, the library that provides tensors.
- The block line constructs the TransformerBlock from the configuration. The source assumes this class and GPT_CONFIG_124M already exist.
- The x line builds the sample input with shape [
batch_size,num_tokens,emb_dim] = [2, 4, 768]. - The output line is the single call that matters: the input x enters
block(x)and an output comes back. - The two print lines show both shapes so you can compare them directly.
Input shape: torch.Size([2, 4, 768])
Output shape: torch.Size([2, 4, 768])The random values in x will differ between runs, but the printed shapes are the thing to check. The source reports both shapes as torch.Size([2, 4, 768]), so the printed output confirms that the block keeps the input shape. The source describes the block only as a component that processes input data, so this step does not make claims about what happens inside it.
Shape Before and After the Call
A shape check is a comparison, not a guess. Printing both shapes side by side gives two values you can compare position by position: the first number with the first, the second with the second, and the third with the third. When every position matches, the block has preserved the shape.
In the source's run, the two boxes carry identical shapes. The batch size is still 2, the token count is still 4, and the embedding dimension is still 768. The diagram shows only the single call and its two shapes; it does not claim anything about the steps between them.
Locating Where a Mismatch Would Appear
When a shape check does not match what you expect, the useful question is where in the code the result diverged. In this snippet there are four stages: the input is created, the block is constructed, the block is called, and the shapes are printed. The printed lines are the last stage, so a mismatch that shows up between the input and output lines points to the call output = block(x) or to the block itself, not to the print statements.
The pipeline shows the order of the four stages. The shape check is most informative at the last stage, where both shapes are printed. Use the printed values to narrow the search before you change any code. If the two lines differ, the call output = block(x) is the first place to look, because that single line is where the block receives x and returns output.
Mistakes Beginners Make When Checking Shapes
Common mistake Reading the shape numbers in the wrong order
What it looks likeTreating 768 as the number of sequences in the batch.
Why it failsThe source describes the shape as [
batch_size,num_tokens,emb_dim], so the first number is the batch size and the last number is the embedding dimension.Do this insteadRead the shape left to right: 2 sequences, 4 tokens each, 768 values per token.
Common mistake Printing only the output shape
What it looks likePrinting output.shape alone and assuming it matches the input.
Why it failsA single printed value gives nothing to compare against. The check depends on two values side by side.
Do this insteadPrint both x.shape and output.shape, then compare them position by position.
Common mistake Assuming the random values determine the shape
What it looks likeExpecting the shape to change because torch.rand produces different numbers on each run.
Why it failsThe source's check is about the shape, and the shape is set by the arguments 2, 4, and 768.
Do this insteadCompare the shape values, not the individual numbers inside the tensor.
Common mistake Blaming the print statements for a mismatch
What it looks likeRewriting the print lines after the two shapes disagree.
Why it failsThe source locates a mismatch between the input and output lines in the call output =
block(x)or in the block itself.Do this insteadInspect the call and the block construction first, since the printing stage only reports values.
Working Through a Shape Mismatch
Checking whether a block preserved the shape
You run the source's snippet and see the two printed lines below. Does the block preserve the shape, and if not, which stage should you investigate?
- 1Read the input shape
The input line reads
torch.Size([2, 4, 768]): 2 sequences, 4 tokens each, 768 values per token. - 2Read the output shape
Suppose the output line reads
torch.Size([2, 4, 512]). The batch size and token count match, but the last number differs. - 3Compare position by position
Position one: 2 and 2 match. Position two: 4 and 4 match. Position three: 768 and 512 do not match.
- 4Locate the stage
The mismatch sits in the
emb_dimposition, and the pipeline points to the call output =block(x)or the block itself, since the input stage and the printing stage are not the source of a changed dimension.
The block did not preserve the shape in this hypothetical run. The next step is to inspect the call and the block's construction, not the print statements. The source's own run, by contrast, reports torch.Size([2, 4, 768]) for both lines, so the shape is preserved there.
Checking Your Own Run
Run the snippet with the input x = torch.rand(2, 4, 768) and the block call output = block(x). Print both shapes. Then change only the batch size in x to 3, keeping the token count and embedding size the same. Predict the printed output shape before you run the code, and say which of the three dimensions you changed.
Hints
- Read the shape as [
batch_size,num_tokens,emb_dim] and change only the first number. - Compare the printed input and output shapes position by position after the run.
- If the output shape keeps the same three numbers as the input, the block has preserved the shape.
Summary of the Shape Check
- 1The sample input has shape [
batch_size,num_tokens,emb_dim], given in the source as [2, 4, 768]. - 2The source's run reports
torch.Size([2, 4, 768])for both the input and the output, so the TransformerBlock maintains the input shape. - 3To verify a shape, print both the input and output shapes and compare them position by position.
- 4If the shapes differ, the mismatch points to the call output =
block(x)or to the block itself, not to the print statements. - 5The source describes the block only as a component that processes input data, so the check confirms the shape without claiming anything about the internal steps.
At a glance
- The sample input has shape [
batch_size,num_tokens,emb_dim], given in the source as [2, 4, 768]: 2 sequences, 4 tokens each, 768 values per token. - The source's run prints
torch.Size([2, 4, 768])for both the input and the output, so the TransformerBlock maintains the input shape. - To verify a shape, print both the input and output shapes and compare them position by position.
- A mismatch between the input and output lines points to the call output =
block(x)or to the block itself, not to the print statements.