Stanford CS336 Assignment 1 is titled Building a Transformer LM. It covers the main ideas behind a decoder-only language model: BPE tokenization, Transformer architecture, cross-entropy, SGD, AdamW, learning-rate schedules, gradient clipping, and decoding with temperature and top-p sampling.
This post is not a line-by-line translation of the handout, nor is it mainly about software engineering. Instead, it organizes the assignment as a review of the large-language-model concepts behind training and inference.
The central pipeline is:
During training, the model learns to predict the next token from the preceding tokens. During inference, it feeds its own predictions back into the context and generates a sequence one token at a time.
1. What Does a Language Model Learn?
Autoregressive language modeling
Suppose a text sequence has been tokenized as:
An autoregressive language model decomposes the probability of the whole sequence using the chain rule:
At every position, the model performs the same task: given a prefix, predict the next token.
Training maximizes the log-likelihood of the data:
Equivalently, we minimize the negative log-likelihood, which becomes the next-token cross-entropy loss:
The important point is that training does not require running the model separately for every position. One forward pass can produce predictions for all positions in parallel. The causal mask ensures that position cannot access future tokens.
Teacher forcing
During training, position receives the ground-truth token and is trained to predict :
This is called teacher forcing. The model always receives the correct history during training, rather than its own previous prediction.
Teacher forcing makes training highly parallelizable, but it creates exposure bias: during inference, the model must condition on tokens that it generated itself, including possible earlier mistakes.
2. Tokenization: Why Start with Byte-Level BPE?
Unicode, UTF-8, and byte vocabulary
Unicode assigns each character an abstract code point. For example:
1 | ord("s") # 115 |
Using Unicode code points directly as tokens would create a large and highly imbalanced vocabulary. CS336 instead encodes text as UTF-8 bytes before learning subword tokens.
A byte has a value between and , so the initial vocabulary contains only 256 possible byte tokens. UTF-8 can represent arbitrary Unicode text and remains compatible with ASCII.
The tradeoff is sequence length: one Unicode character may occupy several UTF-8 bytes. Byte-level tokenization almost eliminates unknown tokens, but it can produce longer sequences and therefore increase Transformer computation.
The compression idea behind BPE
BPE, or Byte-Pair Encoding, is a compromise between byte-level and word-level tokenization.
Suppose a neighboring pair occurs frequently. BPE merges it into a new token :
The algorithm starts with 256 byte tokens and repeatedly merges the most frequent adjacent pair. After merge operations, the vocabulary size is approximately:
BPE is not learning word semantics directly. It is learning a useful compression scheme for frequent byte sequences. Common words may become a single token, while rare words can fall back to shorter subword or byte sequences.
Why pre-tokenization matters
Before counting byte pairs, the corpus is split into pre-tokens. This has two purposes:
- It reduces the amount of pair counting that needs to be performed.
- It prevents arbitrary merges across word-like or document boundaries.
The assignment uses a GPT-2-style regular expression and preserves leading spaces. As a result, " text" and "text" can be represented differently.
Special tokens create hard boundaries. For example, <|endoftext|> should remain one indivisible token and should not allow ordinary BPE merges to cross from one document into the next.
Encoding and decoding
After BPE training, the tokenizer stores a vocabulary and an ordered list of merges. Encoding proceeds as follows:
- Pre-tokenize the input text.
- Convert each pre-token into UTF-8 bytes.
- Apply the learned merges in their training order.
- Map the resulting byte strings to integer token IDs.
Decoding performs the reverse operation: look up the byte string for every token ID, concatenate the bytes, and decode them as UTF-8. An arbitrary sequence of token IDs may not form valid UTF-8, so malformed bytes are usually replaced with the Unicode replacement character U+FFFD.
One distinction is worth remembering: BPE training learns merge rules from a corpus, while encoding applies an already learned set of rules to new text.
3. The Overall Transformer Language Model
Given token IDs,
the token embedding maps them to dense vectors:
The hidden states pass through several Transformer blocks and are finally projected to vocabulary logits:
The full model can be summarized as:
The model normally outputs logits rather than probabilities. During training, cross-entropy can be computed directly from logits. During inference, logits are converted into a probability distribution for decoding.
4. Self-Attention: How Does the Model Aggregate Context?
Query, Key, and Value
For hidden states , attention applies three learned linear transformations:
An intuitive interpretation is:
- Query: what information is the current position looking for?
- Key: what information does each position offer?
- Value: what content is actually aggregated?
The dot product between a query and a key measures how relevant one position is to another. The values are then averaged according to those relevance scores.
Scaled dot-product attention
The basic attention operation is:
Why divide by ? If the components of and have variance close to 1, the variance of their dot product grows with . Without scaling, large head dimensions can produce very large scores, causing softmax saturation and small gradients.
The scaling factor keeps the score distribution in a more manageable range.
Causal masking
An autoregressive language model cannot look at future tokens. The causal mask is:
After adding this mask, future positions receive zero probability after softmax. Position can attend only to .
This is one of the most important constraints in language-model training. If the mask direction is reversed, the model can see the answer during training. The training loss may look excellent, while generation completely fails.
Multi-head attention
Multi-head attention splits the model dimension into smaller heads:
Each head computes attention independently. The results are concatenated and passed through an output projection. Different heads can learn different types of dependencies, such as local syntax, long-range references, or positional relationships.
5. RoPE: Where Does Positional Information Come From?
Without positional information, self-attention is permutation-equivariant: it has no way to tell that the order of the tokens has changed.
CS336 uses Rotary Position Embedding. RoPE treats every pair of dimensions as a two-dimensional plane and rotates query and key vectors by an angle determined by position:
For positions and :
The rotated inner product is:
Since rotation matrices satisfy , the attention score depends on the relative distance .
This is the central intuition behind RoPE: by rotating queries and keys, their dot products naturally encode relative position.
RoPE is applied to and , but not to . It changes how positions are matched; it does not alter the content carried by the values.
6. RMSNorm and Pre-Norm Transformers
RMSNorm
LayerNorm subtracts the mean and divides by the standard deviation. RMSNorm only normalizes the root mean square.
For a hidden vector :
Here is a learned gain parameter. RMSNorm does not force the mean to zero; it mainly controls the scale of the hidden vector.
Why is normalization useful? As depth increases, the scale of activations and gradients can change substantially. Normalization gives each sub-layer a more stable input distribution, which makes optimization easier.
Pre-norm block
The pre-norm Transformer block used in the assignment is:
Normalization happens before each sub-layer, while the residual addition remains outside the sub-layer. The original Transformer is closer to the post-norm form:
The key intuition behind pre-norm is that the residual stream provides a relatively direct path for both information and gradients. Each sub-layer learns an incremental correction to the stream instead of having to reconstruct a completely new representation.
This is why the assignment compares pre-norm and post-norm as an ablation. The location of normalization is not merely a formatting choice; it changes training stability and optimization behavior.
7. Feed-Forward Networks and SwiGLU
Attention mixes information across sequence positions. The feed-forward network applies the same nonlinear transformation independently to each position:
Modern language models often use the gated SwiGLU variant:
where:
is the gate branch. It is multiplied element-wise with , allowing the network to dynamically amplify or suppress features depending on the input.
SwiGLU uses three matrices, while a standard FFN uses two. To keep their parameter counts roughly comparable, the inner dimension of SwiGLU is often chosen near:
The TinyStories configuration in the assignment uses and . The latter is close to and is also divisible by 64, which is convenient for GPU hardware.
8. Cross-Entropy: Why Does the Loss Have This Form?
From logits to probabilities
At one sequence position, the model produces vocabulary logits:
Softmax converts them into probabilities:
If the correct token has index , the cross-entropy loss is:
Substituting the softmax gives:
The loss therefore has two effects: it rewards increasing the correct logit , while the log-sum-exp term accounts for competition with every vocabulary item.
The gradient is prediction minus target
Let be the softmax probability vector and let be the one-hot vector for the correct class. Then:
This is the most important result for softmax cross-entropy.
- For the correct class, the gradient is , usually negative, so gradient descent increases its logit.
- For an incorrect class, the gradient is , so gradient descent decreases its logit.
If the model already assigns almost all probability to the correct class, and the gradient is close to zero. If it is highly confident but wrong, the gradient is large.
Numerical stability
Directly computing can overflow. Let . Then:
Because , the exponentials are much safer to evaluate.
In practice, a cross-entropy implementation usually combines log_softmax and negative log-likelihood rather than materializing the full probability matrix first.
Perplexity
If the average per-token loss is , perplexity is defined as:
It can be interpreted loosely as the effective number of equally likely choices the model faces at each position. Lower perplexity is generally better, but perplexities from different tokenizers or datasets are not directly comparable.
9. Optimizers: How Do Parameters Learn?
SGD
The basic gradient-descent update is:
where is the learning rate and .
The learning rate controls the step size. If it is too small, training is slow. If it is too large, the loss may oscillate or diverge.
AdamW and its two moments
AdamW maintains an exponential moving average of the gradient and of the squared gradient:
tracks the direction of the gradient, while tracks its scale. Since both states start at zero, the early estimates are biased toward zero. Adam corrects this bias using:
The adaptive update is:
AdamW adds decoupled weight decay:
Combining the two terms gives:
Weight decay gently pulls parameters toward zero. The important distinction is that it is decoupled from the adaptive gradient update; it should not simply be added to the gradient and passed through Adam.
Typical LLM settings include , , and , although the best values depend on the model and the data.
Cosine learning-rate schedule
Transformer training commonly uses a learning-rate schedule with a warmup phase followed by cosine decay.
During warmup:
During cosine decay:
Here is the end of warmup and is the end of cosine decay. After , the learning rate remains at .
The intuition behind warmup is that the model has not yet reached a stable activation and gradient regime at the beginning of training. Starting immediately with a large learning rate may destabilize the optimization.
Gradient clipping
If a batch produces an unusually large gradient, we can scale the entire gradient vector so its L2 norm does not exceed :
When , the gradient is unchanged. Otherwise, every parameter gradient is multiplied by the same factor.
Gradient clipping does not make learning intrinsically faster. Its purpose is to prevent a small number of pathological batches from causing catastrophic parameter updates.
10. Training Compute and Resource Intuition
Parameter count
Ignoring biases, the main parameters in one Transformer block come from attention and the feed-forward network.
The , , , and output projections together contribute approximately:
The three SwiGLU matrices contribute approximately:
For a model with layers, a rough parameter-count formula is:
This includes the token embedding, each Transformer block, the final LM head, and the final RMSNorm. It assumes that the input embedding and output projection do not share weights. With weight tying, the two vocabulary-sized terms can be combined.
Attention as a scaling bottleneck
Self-attention computes , whose sequence dimension has quadratic complexity in context length. The main compute of a Transformer block can be summarized as:
The first term comes mainly from linear projections and the FFN. The second term comes from attention scores and the weighted sum over values.
For short sequences, the model dimension and FFN often dominate. For long contexts, the attention term becomes increasingly important.
Why training uses more memory than inference
If parameters, gradients, first moments, and second moments are all stored in float32, each parameter requires roughly:
This excludes activation memory. Training therefore has several distinct memory costs:
- model parameters;
- gradients;
- optimizer states;
- intermediate activations needed for backpropagation.
Inference usually requires less memory because it does not need backward activations or optimizer states. Increasing batch size increases activation memory, while increasing context length increases both token computation and the quadratic attention cost.
11. Inference: How Does the Model Generate Text?
Autoregressive decoding
Given a prefix , the Transformer produces logits at every position. To generate the next token, we use only the final position:
The logits are converted into a distribution:
We sample , append it to the prefix, and repeat until <|endoftext|> is generated or a maximum length is reached.
Training can compute all positions in a sequence in parallel. Naive generation is sequential because each new token depends on the previously generated result. This is the fundamental difference between training and autoregressive inference.
Greedy decoding and sampling
Greedy decoding selects the most likely token at every step:
It is stable but can produce repetitive and overly conservative text.
Sampling draws from the probability distribution itself. It preserves multiple plausible continuations, but it can also select low-quality tokens, so the distribution is often modified before sampling.
Temperature
Temperature rescales the logits before softmax:
- : a sharper distribution, closer to greedy decoding;
- : the original model distribution;
- : a flatter distribution with more randomness.
Temperature does not change the model parameters. It changes only the sampling distribution used during inference.
Top-p, or nucleus sampling
Suppose the probabilities are sorted as . Top-p sampling chooses the smallest candidate set whose cumulative probability reaches :
It then renormalizes and samples only from this set:
Top-p is adaptive. When the model is confident, only a few tokens are retained. When the model is uncertain, the candidate set becomes larger.
12. How Should We Read the Assignment Experiments?
TinyStories versus OpenWebText
The assignment starts with TinyStories and later moves to OpenWebText.
TinyStories has a relatively simple distribution, so a small model can learn stable grammar and story patterns quickly. It is therefore useful for studying architecture and hyperparameters. OpenWebText is more varied and noisy, so the same model and compute budget usually produce higher loss and worse generations.
Loss values from different datasets should not be compared without context. Loss depends on data difficulty, tokenizer compression, vocabulary size, and sequence distribution.
Learning rate and batch size
Learning-rate experiments typically reveal three regimes:
- Too small: stable but slow improvement.
- Appropriate: fast and stable loss reduction.
- Too large: oscillation or divergence.
The “edge of stability” intuition is that the best learning rate is often close to the largest rate that remains stable. A very conservative rate wastes the compute budget, while an aggressive rate may diverge.
Increasing batch size can improve hardware utilization and reduce the noise of the gradient estimate, but larger is not always better. When comparing batch sizes, we must specify whether we are holding the number of steps, the number of processed tokens, or wall-clock time fixed.
What do the ablations tell us?
The assignment asks us to compare several architectural variants:
- Remove RMSNorm and observe whether training becomes unstable.
- Replace pre-norm with post-norm.
- Remove RoPE and test whether the model can infer position from causal attention alone.
- Replace SwiGLU with an ungated SiLU FFN while approximately matching parameter count.
An ablation should not be judged only by its final loss. We should also inspect:
- the speed of learning-curve descent;
- whether the run diverges;
- activation, gradient, and parameter norms;
- the coherence and repetition of generated text.
If the model architecture changes together with the parameter count, training token budget, or learning rate, the final difference cannot be attributed cleanly to the component being studied.
13. Common Confusions
Logits, probabilities, and loss
- Logits are arbitrary real-valued outputs of the final linear layer.
- Softmax converts logits into a probability distribution over the vocabulary.
- Cross-entropy uses the probability assigned to the correct token.
- In practice, training usually computes cross-entropy directly from logits without explicitly materializing probabilities.
Training and inference
- Training uses teacher forcing and can process all positions in parallel.
- Inference is autoregressive: each generated token depends on the previous generated context.
- Training needs gradients, saved activations, and optimizer states.
- Inference needs only forward computation and a decoding strategy.
RoPE and causal masking
- RoPE provides positional information.
- The causal mask restricts which positions are visible.
- RoPE answers “where are these positions?”
- The causal mask answers “is the future allowed to be seen?”
RMSNorm and residual connections
- RMSNorm controls the scale of activations.
- Residual connections provide a direct information and gradient path.
- Pre-norm places normalization before attention or the FFN.
- Post-norm places normalization after residual addition.
Conclusion
The main lesson of CS336 Assignment 1 is not a collection of APIs. It is an understanding of why a language model can be trained and how it generates text after training.
BPE maps open-ended Unicode text into a finite token vocabulary. The Transformer uses attention to aggregate context. RoPE provides positional information. The causal mask enforces autoregressive factorization. Cross-entropy turns next-token prediction into an optimization objective. AdamW, learning-rate schedules, and gradient clipping control how the parameters learn. Temperature and top-p determine how the model chooses among possible continuations during inference.
Together, these ideas explain the full process behind:
A Transformer does not directly “understand” an entire document in one indivisible operation. At every position, it estimates a conditional distribution for the next token. With enough data, model capacity, and effective optimization, these local predictions can give rise to language understanding, knowledge recall, and increasingly complex reasoning behavior.
References
- Stanford CS336, Assignment 1: Building a Transformer LM.
- Vaswani et al., Attention Is All You Need, 2017.
- Sennrich, Haddow, and Birch, Neural Machine Translation of Rare Words with Subword Units, 2016.
- Zhang and Sennrich, Root Mean Square Layer Normalization, 2019.
- Loshchilov and Hutter, Decoupled Weight Decay Regularization, 2019.
- CS336 Assignment 1 repository