Part of Language AI Handbook
Explains how causal language modeling trains AI to predict the next token. Topics include autoregressive factorization, cross-entropy loss, causal masking.
Choose your expertise level to adjust how many terms are explained. Beginners see more tooltips, experts see fewer to maintain reading flow. Hover over underlined terms for instant definitions.
Article links
Make inline references clickable
Causal Language Modeling
Language models learn to predict the next word. This simple objective, applied at massive scale, has produced the most capable AI systems ever built. GPT-4 and Claude, along with LLaMA, share this foundation with virtually every modern generative model: given a sequence of tokens, predict what comes next.
Causal Language Modeling (CLM) is the formal name for this training objective. The "causal" refers to the direction of information flow: predictions depend only on past tokens, never on future ones. This constraint mirrors how humans produce language, word by word, and makes the learned model directly usable for text generation without any modifications to the architecture at inference time.
CLM derives its training signal directly from raw text. You don't need labeled data, annotated categories, or hand-crafted features. Every piece of raw text is automatically a supervised learning example: the input is a prefix, and the label is the next token. This means the entire internet, all books ever digitized, every line of code ever written can be converted into training signal at zero additional annotation cost. The web itself becomes a teacher.
Think of it this way: every time a model reads the phrase "The Eiffel Tower is located in," it is tested on whether it predicts "Paris" as the next token. Every time it reads import numpy as np\nimport, it is tested on whether it knows common Python libraries. Every sentence in every document becomes a series of prediction problems, and the model that learns to solve all of them must, necessarily, have learned a great deal about how language works.
The simplicity of the objective also means it generalizes remarkably. A model that learns to predict text must represent grammar, factual knowledge, reasoning patterns, and even emotional tone, because all of these factors influence what word comes next. CLM is in this sense a universal objective: it forces the model to internalize whatever statistical regularities are needed to predict the next token accurately, across all contexts and all domains present in the training corpus.
In this chapter, we'll unpack the mathematics behind CLM, understand why it works so well, implement the loss function from scratch, and explore the training data and scaling properties that have driven recent breakthroughs.
The Autoregressive Factorization
Causal language modeling is built on a mathematical framework called the autoregressive factorization. Before diving into the training objective or the loss function, it helps to understand this underlying structure, because it explains both why the approach is tractable and why it naturally produces generative models. The central challenge is assigning probabilities to sequences of arbitrary length. The autoregressive factorization is the field's mathematically exact solution rather than an approximation.
How do you assign a probability to an entire sentence? This is the fundamental question that language models must answer. Given a sequence of tokens , we want to compute , the probability that this particular sequence occurs in natural language.
The Combinatorial Challenge
Consider what this means in practice. With a vocabulary of 50,000 tokens and sequences of length 100, we'd need to assign probabilities to possible sequences. That's more possibilities than atoms in the observable universe, raised to a power larger than the universe's age in seconds. Storing or computing such a distribution directly is impossible.
We need a way to break this intractable joint probability into manageable pieces. Fortunately, probability theory gives us exactly such a tool: the chain rule.
The Chain Rule of Probability
The chain rule states that any joint probability can be decomposed into a product of conditional probabilities. For a sequence, this means:
Let's trace through what each term represents:
- : The probability of the first token appearing at the start of a sequence. This has no conditioning context.
- : Given we've seen the first token, what's the probability of the second?
- : Given the first two tokens, what comes third?
- And so on, until : the final token, conditioned on everything before it.
This telescoping product captures the sequential nature of language. Each new word depends on what came before, exactly matching our intuition about how text is generated.
The Compact Notation
We can express this factorization more concisely using product notation:
where:
- : the token at position in the sequence
- : all tokens before position , that is, the sequence
- : the conditional probability of token given all preceding tokens
For the first position where , we define as empty, so .
Each factor is a conditional distribution over the entire vocabulary. Given everything we've seen so far, what's the probability of each possible next token? This is precisely the question a language model learns to answer.
A modeling approach where each output depends only on previous outputs, not future ones. The model generates sequences one step at a time, with each step conditioned on all prior steps. The term "autoregressive" comes from time series analysis where current values are regressed on past values.
Why This Factorization Works
This decomposition is mathematically exact, not an approximation. The chain rule holds for any joint distribution. What makes it practical is that we've transformed an impossible problem (representing probabilities) into a tractable one (learning a function that outputs a distribution over 50,000 tokens given any context).
The key insight is that while contexts vary enormously, they share statistical patterns. The word "the" tends to be followed by nouns. Questions end with question marks. Technical documents use technical vocabulary. A neural network can learn these patterns and generalize them to new contexts it has never seen before.
There is also a deep connection between this factorization and causality, which is where the name "causal" language modeling comes from. When a model predicts , it is computing what should come next based solely on the causal history, everything that appeared before. No information from the future leaks into the prediction. This mirrors real-world language generation: when a human writes a sentence, the word they choose at each moment is influenced by what they have already written, not by what they have not yet written. CLM formalizes this intuitive notion and enforces it architecturally through causal masking, which we'll cover later in the chapter.
Before neural language models, the autoregressive factorization was approximated using n-gram models. An n-gram model estimates by only conditioning on the previous tokens rather than the full history. A trigram model, for example, computes . This Markov approximation made computation feasible in the pre-neural era: you simply count how often each n-gram appears in the training corpus. The problem is that vocabulary grows exponentially with , so trigrams and 4-grams were roughly the practical limit. Most contexts you encounter at test time never appeared in training, leading to severe data sparsity. Neural language models broke through this ceiling by learning continuous representations that generalize across contexts, allowing effective conditioning on sequences of arbitrary length.
Notice that the factorization also implies a natural sampling procedure. To generate a sequence, you sample from , then sample from , then from , and so on. At each step you need only the distribution over the next single token, which the model can compute efficiently. This is why the same model used for training, which minimizes prediction loss, can be used directly for generation without any modifications.
The CLM Objective
Now that we understand how to decompose sequence probability, we need a way to train a model to produce good probability estimates. This requires two things: a loss function that measures prediction quality, and a mechanism to improve the model based on that measurement. The CLM objective is the standard answer to both. It is derived from maximum likelihood estimation, a classical statistical principle, applied to the autoregressive factorization we just derived. Understanding the derivation step by step reveals why the final formula looks the way it does and why the choices made along the way are the right ones.
From Maximum Likelihood to Minimum Loss
Our goal is to find model parameters that make the training data as probable as possible. Given a training sequence , we want to maximize:
Maximizing a product of many small probabilities is numerically unstable. As sequences grow longer, the product shrinks toward zero, causing underflow. The standard solution is to work with logarithms. Since is monotonically increasing, maximizing a probability is equivalent to maximizing its log:
The product becomes a sum, which is numerically stable and computationally convenient. Now, optimization algorithms typically minimize rather than maximize, so we flip the sign to get our loss function:
where:
- : the loss function we minimize during training
- : the model parameters (weights and biases of the neural network)
- : the length of the training sequence
- : the probability the model assigns to the correct token given the context
- : the log-probability, which is negative since probabilities lie in
The Connection to Cross-Entropy
This loss function has a beautiful interpretation: it's the cross-entropy between the model's predicted distribution and the true distribution (which puts all probability mass on the actual next token).
To see why, recall that cross-entropy measures how well a predicted distribution matches a true distribution :
At each position , the "true distribution" is a one-hot vector: probability 1 for the actual token , and 0 for everything else. The model predicts a distribution over all vocabulary tokens. The cross-entropy simplifies to:
Only the true token's probability matters. This is why cross-entropy loss is also called "negative log-likelihood" in language modeling contexts.
Understanding the Loss Signal
The loss function creates an intuitive learning signal. When the model assigns high probability to the correct token, the loss contribution is small. When the model is surprised, the loss contribution is large.

Consider these scenarios:
- Confident and correct (): . Small loss, weak gradient. The model is doing well here.
- Uncertain (): . Moderate loss. Room for improvement.
- Confident and wrong (): . Large loss, strong gradient. The model needs to update significantly.
This asymmetry is powerful: the model receives the strongest teaching signal precisely where it's making the biggest mistakes.
Efficient Learning from Every Token
A remarkable property of CLM loss is that it decomposes across positions. A single sequence of length provides independent gradient signals, one for each prediction. This is dramatically more efficient than classification tasks where a single input yields a single label.
Consider training on a document with 1,000 tokens. Each forward pass produces 1,000 predictions and 1,000 loss terms. The model learns from every token simultaneously, extracting maximum information from the training data. This efficiency is one reason language models can learn so much from their training corpora.
Implementation
Let's implement this loss function to see how it works in practice:
import torch.nn.functional as F
def compute_clm_loss(logits, targets, ignore_index=-100):
"""
Compute causal language modeling loss.
Args:
logits: Model outputs of shape (batch, seq_len, vocab_size)
targets: Target token IDs of shape (batch, seq_len)
ignore_index: Token ID to ignore in loss computation (e.g., padding)
Returns:
Scalar loss value (mean over all valid positions)
"""
# Reshape for cross-entropy: (batch * seq_len, vocab_size)
batch_size, seq_len, vocab_size = logits.shape
logits_flat = logits.view(-1, vocab_size)
targets_flat = targets.view(-1)
# Cross-entropy computes -log(softmax(logits)[target])
loss = F.cross_entropy(
logits_flat, targets_flat, ignore_index=ignore_index, reduction="mean"
)
return lossExample CLM loss: 4.7462 Perplexity: 115.15 Random baseline (vocab=100): loss=4.6052, ppl=100
With random logits, the loss is close to where is the vocabulary size, because the model assigns roughly uniform probability to all tokens. As training progresses, the model learns to concentrate probability mass on likely continuations, reducing the loss.


From Sequence to Training Examples
A key insight of CLM is that a single sequence yields multiple training examples. For a sequence of length , we predict tokens at positions 2 through using contexts of increasing length. This is what makes CLM so data-efficient compared to other supervised learning tasks. In image classification, a single image is a single training example. In CLM, a single sentence of 20 words provides 19 training examples, and a document of 2,000 tokens provides 1,999. The same bytes of raw text generate orders of magnitude more gradient signal than would be possible with explicit labeling.
Consider the sentence "The cat sat on the mat". We create training pairs:
| Context | Target |
|---|---|
| [START] | The |
| [START] The | cat |
| [START] The cat | sat |
| [START] The cat sat | on |
| [START] The cat sat on | the |
| [START] The cat sat on the | mat |
The model sees the same sequence but learns from every position simultaneously. This is implemented using a clever shifting trick:
def prepare_clm_batch(token_ids):
"""
Prepare input and target tensors for CLM training.
Input tokens are [0:n-1], targets are [1:n].
The model predicts each token given all previous tokens.
"""
# Input: all tokens except the last
input_ids = token_ids[:, :-1]
# Target: all tokens except the first (shifted by 1)
labels = token_ids[:, 1:]
return input_ids, labels
# Example: tokenized sequence
tokens = torch.tensor(
[[101, 2054, 3921, 2068, 1999, 2023, 102]]
) # [CLS] ... [SEP]
inputs, targets = prepare_clm_batch(tokens)Original sequence: [101, 2054, 3921, 2068, 1999, 2023, 102] Input tokens: [101, 2054, 3921, 2068, 1999, 2023] Target tokens: [2054, 3921, 2068, 1999, 2023, 102] Training pairs: Context: [101] → Target: 2054 Context: [101, 2054] → Target: 3921 Context: [101, 2054, 3921] → Target: 2068 Context: [101, 2054, 3921, 2068] → Target: 1999 Context: [101, 2054, 3921, 2068, 1999] → Target: 2023 Context: [101, 2054, 3921, 2068, 1999, 2023] → Target: 102
The inputs and targets are offset by one position. At position , the model receives tokens as input and predicts token . This offset is applied once during preprocessing, and the model processes all positions in parallel during training.
In practice, this parallel processing is why transformers are so much faster to train than recurrent neural networks. An RNN would process the sequence step by step, computing each hidden state sequentially before moving to the next. A transformer with causal masking computes all hidden states in a single forward pass, using the mask to enforce the left-to-right constraint. This enables much larger batch sizes and dramatically higher GPU utilization.
Causal Masking
For the autoregressive factorization to hold, the model must not "peek" at future tokens when predicting the current one. In transformer architectures, this is enforced through causal masking in the attention mechanism. Without this constraint, a model could trivially solve the prediction problem by looking ahead at the answer, which would produce a model that learns nothing useful and cannot generate text at test time.
The challenge is a structural one. Standard transformer attention is fully bidirectional: every position attends to every other position. This is powerful for understanding tasks, where you want to incorporate all available context. But for generation, it creates a problem. If position 3 can see positions 4, 5, and 6 during training, the model learns to use that future information. At inference time, positions 4, 5, and 6 have not been generated yet, so the model encounters a distribution shift it was never trained to handle.
Causal masking solves this by zeroing out attention from any position to any future position. The model is structurally prevented from accessing information it would not have access to during generation. In practice, this is implemented by adding a large negative constant (negative infinity) to the attention scores for disallowed positions before applying the softmax. After softmax, those positions receive attention weight of zero, as if they do not exist.
The attention pattern looks like this:

In code, the causal mask is applied to attention scores before the softmax:
def create_causal_mask(seq_len):
"""
Create a causal attention mask.
Returns a matrix where position (i, j) is:
- 0 if i >= j (can attend)
- -inf if i < j (cannot attend)
"""
# Create upper triangular matrix of ones (above diagonal)
mask = torch.triu(torch.ones(seq_len, seq_len), diagonal=1)
# Replace 1s with -inf to zero out attention after softmax
mask = mask.masked_fill(mask == 1, float("-inf"))
return mask
# Example usage in attention
def masked_attention(query, key, value, mask):
"""Simplified scaled dot-product attention with causal masking."""
d_k = query.size(-1)
scores = torch.matmul(query, key.transpose(-2, -1)) / (d_k**0.5)
# Apply causal mask
scores = scores + mask
# Softmax: -inf becomes 0 probability
attn_weights = F.softmax(scores, dim=-1)
return torch.matmul(attn_weights, value), attn_weightsCausal mask (4 positions): [[ 0. -inf -inf -inf] [ 0. 0. -inf -inf] [ 0. 0. 0. -inf] [ 0. 0. 0. 0.]] Attention weights after masking and softmax: [[1. 0. 0. 0. ] [0.5 0.5 0. 0. ] [0.333 0.333 0.333 0. ] [0.25 0.25 0.25 0.25 ]]
The negative infinity values become zero probability after softmax, effectively blocking information flow from future positions. Each row sums to 1.0, distributing attention only over valid (past and present) positions.
A Working Example: Training a Tiny CLM
Let's train a minimal causal language model to see the complete pipeline. We'll use a character-level model on a small text to keep things interpretable. A character-level model treats each individual character as a token rather than each word or subword. This gives us a vocabulary of only 26 letters plus punctuation, making the prediction problem simple enough to solve with a tiny model in a few hundred steps. The core CLM machinery: the autoregressive factorization, the cross-entropy loss, the causal masking, and the generation loop, is identical to what large-scale models use. Only the vocabulary and capacity differ.
import torch
# Sample text for training
text = """To be or not to be that is the question
Whether tis nobler in the mind to suffer
The slings and arrows of outrageous fortune"""
# Character-level tokenization
chars = sorted(set(text))
char_to_idx = {c: i for i, c in enumerate(chars)}
idx_to_char = {i: c for c, i in char_to_idx.items()}
vocab_size = len(chars)
# Encode the text
encoded = torch.tensor([char_to_idx[c] for c in text])Vocabulary size: 22 characters Characters: TWabdefghilmnoqrstuw Text length: 124 characters Encoded (first 50): [2, 15, 1, 5, 7, 1, 15, 17, 1, 14, 15, 19, 1, 19, 15, 1, 5, 7, 1, 19, 10, 4, 19, 1, 11, 18, 1, 19, 10, 7, 1, 16, 20, 7, 18, 19, 11, 15, 14, 0, 3, 10, 7, 19, 10, 7, 17, 1, 19, 11]
Our tiny corpus contains only 27 unique characters (letters and spaces, plus newlines). This small vocabulary means the model has fewer options to choose between at each step, making learning feasible even with limited data. The encoded representation converts each character to its integer index, ready for embedding lookup.
Now let's define a simple transformer-based language model:
class TinyCausalLM(nn.Module):
def __init__(
self, vocab_size, d_model=64, n_heads=4, n_layers=2, max_len=128
):
super().__init__()
self.d_model = d_model
# Token and position embeddings
self.token_emb = nn.Embedding(vocab_size, d_model)
self.pos_emb = nn.Embedding(max_len, d_model)
# Transformer layers
encoder_layer = nn.TransformerEncoderLayer(
d_model=d_model,
nhead=n_heads,
dim_feedforward=d_model * 4,
dropout=0.1,
batch_first=True,
)
self.transformer = nn.TransformerEncoder(
encoder_layer, num_layers=n_layers
)
# Output projection
self.output = nn.Linear(d_model, vocab_size)
# Register causal mask buffer
self.register_buffer("causal_mask", None)
def forward(self, x):
batch_size, seq_len = x.shape
# Create position indices
positions = torch.arange(seq_len, device=x.device).unsqueeze(0)
# Combine embeddings
h = self.token_emb(x) + self.pos_emb(positions)
# Create causal mask if needed
if self.causal_mask is None or self.causal_mask.size(0) < seq_len:
mask = torch.triu(
torch.ones(seq_len, seq_len, device=x.device), diagonal=1
)
self.causal_mask = mask.masked_fill(mask == 1, float("-inf"))
# Apply transformer with causal masking
h = self.transformer(
h, mask=self.causal_mask[:seq_len, :seq_len], is_causal=True
)
# Project to vocabulary
logits = self.output(h)
return logitsModel parameters: 110,998
Model architecture: TinyCausalLM(
(token_emb): Embedding(22, 64)
(pos_emb): Embedding(128, 64)
(transformer): TransformerEncoder(
(layers): ModuleList(
(0-1): 2 x TransformerEncoderLayer(
(self_attn): MultiheadAttention(
(out_proj): NonDynamicallyQuantizableLinear(in_features=64, out_features=64, bias=True)
)
(linear1): Linear(in_features=64, out_features=256, bias=True)
(dropout): Dropout(p=0.1, inplace=False)
(linear2): Linear(in_features=256, out_features=64, bias=True)
(norm1): LayerNorm((64,), eps=1e-05, elementwise_affine=True, bias=True)
(norm2): LayerNorm((64,), eps=1e-05, elementwise_affine=True, bias=True)
(dropout1): Dropout(p=0.1, inplace=False)
(dropout2): Dropout(p=0.1, inplace=False)
)
)
)
(output): Linear(in_features=64, out_features=22, bias=True)
)With roughly 56,000 parameters, this is a tiny model by modern standards (GPT-3 has 175 billion). Yet even this small architecture captures the essential CLM structure: embeddings, transformer layers with causal masking, and a final projection to vocabulary logits.
Let's train this model for a few hundred steps:
def get_batch(data, batch_size=32, seq_len=32):
"""Get a random batch of sequences."""
# Random starting positions
starts = torch.randint(0, len(data) - seq_len - 1, (batch_size,))
# Extract sequences
x = torch.stack([data[s : s + seq_len] for s in starts])
y = torch.stack([data[s + 1 : s + seq_len + 1] for s in starts])
return x, y
# Training loop
model = TinyCausalLM(vocab_size)
optimizer = torch.optim.AdamW(model.parameters(), lr=1e-3)
losses = []
for step in range(500):
# Get batch
x, y = get_batch(encoded, batch_size=16, seq_len=32)
# Forward pass
logits = model(x)
loss = F.cross_entropy(logits.view(-1, vocab_size), y.view(-1))
# Backward pass
optimizer.zero_grad()
loss.backward()
optimizer.step()
losses.append(loss.item())Initial loss: 3.1818 (random baseline: 3.0910) Final loss: 0.1173 Final perplexity: 1.12
The loss dropped significantly from the random baseline. Starting near (uniform distribution over 27 characters), the model converged to a much lower loss. The final perplexity indicates the model is roughly 4-5 characters uncertain at each position, down from 27 at initialization.
Let's visualize the training dynamics:

Now let's generate text from the trained model using autoregressive sampling:
def generate(model, start_text, max_new_tokens=100, temperature=0.8):
"""Generate text autoregressively."""
model.eval()
# Encode the prompt
tokens = [char_to_idx[c] for c in start_text]
tokens = torch.tensor(tokens).unsqueeze(0)
with torch.no_grad():
for _ in range(max_new_tokens):
# Get predictions for last position
logits = model(tokens)
next_logits = logits[0, -1, :] / temperature
# Sample from distribution
probs = F.softmax(next_logits, dim=-1)
next_token = torch.multinomial(probs, num_samples=1)
# Append to sequence
tokens = torch.cat([tokens, next_token.unsqueeze(0)], dim=1)
# Decode
generated = "".join([idx_to_char[t.item()] for t in tokens[0]])
return generatedThe temperature parameter controls how "peaked" or "flat" the probability distribution becomes before sampling. Let's visualize this effect:

Generated text samples: -------------------------------------------------- Prompt: 'To be' Output: To be or or not be that is the ques questinonoblestioble gestiobl Prompt: 'The ' Output: The slings and arrows of outrageous fouthe fows f ofonge utrags Prompt: 'Whether' Output: Whether tis nobler in the mind to gsle to towsd sroblto sufetind mi
The generated text isn't perfect, but the model has learned character-level patterns from just 170 characters of Shakespeare. It produces plausible letter sequences and occasionally hits recognizable words. With more data and capacity, this same objective scales to GPT-4.
Training Data for CLM
The quality and scale of training data fundamentally shapes what a causal language model learns. Modern LLMs are trained on datasets containing trillions of tokens, carefully curated from diverse sources. Understanding where that data comes from, how it is processed, and what tradeoffs different sources introduce gives you a much clearer picture of why different models behave the way they do.
Common training data sources include:
- Web crawls: Common Crawl and C4, plus similar filtered web scrapes, provide broad coverage of internet text. Heavy filtering removes spam and duplicates, along with low-quality content.
- Books: Project Gutenberg, Books3, and licensed book corpora provide long-form, well-edited text that teaches narrative structure and coherent reasoning.
- Code: GitHub, Stack Overflow, and code documentation help models understand programming languages and technical reasoning.
- Scientific literature: Papers from arXiv and PubMed, as well as Semantic Scholar, provide technical depth and formal reasoning.
- Curated datasets: Wikipedia, news articles, and human-written examples balance quality with scale.
Data quality matters enormously. A model trained on Reddit comments writes like Reddit. A model trained on textbooks writes like textbooks. The mixture of sources directly influences the model's capabilities and style, as well as its failure modes. This is why organizations release detailed data cards alongside their models, and why replicating a model's behavior often requires replicating its data mixture.
Deduplication is critical: repeated text causes models to memorize rather than generalize. Modern pipelines use MinHash, exact substring matching, or embedding-based deduplication to remove near-duplicate documents. Without deduplication, a model may see certain passages thousands of times during training, learning to recite them verbatim rather than learning the underlying patterns. The Pile and RedPajama datasets, for example, both describe extensive deduplication pipelines as a first-class concern, not an afterthought.
Data Preprocessing and Tokenization
Raw text cannot be fed directly to a language model. The first step is tokenization: converting text into a sequence of integer token IDs drawn from a fixed vocabulary. Modern models use subword tokenization schemes like Byte Pair Encoding (BPE) or SentencePiece. These methods split rare words into smaller pieces while keeping common words intact. The word "tokenization" might become "token", "ization" as two subword units, while "the" remains a single token. This balances vocabulary size against coverage: a vocabulary of around 50,000 to 100,000 tokens is standard, large enough to represent most content efficiently but small enough for the output projection layer to remain manageable.
After tokenization, documents are typically concatenated into long streams and then chunked into fixed-length windows, often 2,048 or 4,096 tokens. This ensures the model sees context that spans document boundaries, which helps it learn transition patterns and avoids the artificial constraint of always resetting context at document edges. The resulting data loader simply iterates through these fixed-length windows during training, which is highly efficient for GPU throughput.
Data Mixture and Domain Weighting
Not all data is created equal for training. Raw web data contains enormous amounts of low-value content: spam, boilerplate HTML, generated text, near-duplicate articles. High-quality sources like curated books and peer-reviewed papers improve reasoning and factual accuracy but are available in smaller quantities. Training on only high-quality data produces models that write elegantly but may lack breadth. Training on only web data produces models with broad knowledge but inconsistent quality.
The practical answer is careful mixture weighting. Models like LLaMA 3 weight code-heavy data higher than raw web crawl because code trains logical and structured reasoning that transfers broadly. Wikipedia and books typically receive upsampling, meaning the model sees them multiple times per training run despite being a small fraction of the raw data volume. Getting this mixture right is an active area of research, and small changes in data weights can produce measurable differences in downstream benchmark performance.
Scaling Properties
Perhaps the most remarkable property of CLM is how predictably it scales. As we increase model size, dataset size, and compute, performance improves following consistent power laws. This predictability is what distinguishes language model scaling from most engineering disciplines. Usually, when you make something bigger, you encounter bottlenecks, diminishing returns, and phase transitions that are difficult to predict. CLM scaling instead follows remarkably smooth trajectories, making it possible to predict the behavior of a trillion-parameter model by fitting a curve to experiments with much smaller models.
The scaling laws discovered by Kaplan et al. (2020) and refined by Hoffmann et al. (2022) empirically characterize how test loss depends on model size and training data. The key finding is that loss follows a power-law relationship with both factors:
where:
- : the cross-entropy loss on held-out test data, as a function of model size and data
- : the number of trainable model parameters (excluding embeddings)
- : the number of training tokens the model has seen
- : the scaling exponent for model size (how quickly loss improves as grows)
- : the scaling exponent for data (how quickly loss improves as grows)
- and : fitted constants that set the scale (roughly and respectively)
- : the irreducible loss, representing fundamental uncertainty in language that no model can eliminate
The formula has three additive terms. The first term captures model capacity limitations: smaller models have higher loss. The second term captures data limitations: less training data means higher loss. The third term is the floor, around 1.69 nats, representing the inherent unpredictability of natural language.
This equation reveals that loss decreases as a power law with both model size and data. There's no plateau in sight: 10x more compute yields roughly 0.1 lower loss, consistently across many orders of magnitude.

The practical implication is clear: if you want a better language model, train a bigger model on more data with more compute. This insight has driven the race to scale, from GPT-2's 1.5 billion parameters to models with hundreds of billions.
The Chinchilla Finding and Compute-Optimal Training
The original Kaplan et al. scaling laws suggested that given a fixed compute budget, you should spend most of it on model size and train on relatively little data. GPT-3, trained with 175 billion parameters on roughly 300 billion tokens, reflected this philosophy.
Hoffmann et al. (2022), in a paper introducing the Chinchilla model, revised this picture significantly. Their experiments showed that the optimal training strategy is to scale model size and data size proportionally. For every doubling of parameters, you should also double the number of training tokens. By this analysis, GPT-3 was substantially undertrained: a much smaller model trained on far more data would have achieved equal performance at much lower cost.
This finding reoriented the field. Subsequent models like LLaMA and Mistral, along with Gemma, were designed with the Chinchilla recipe in mind, training smaller models on larger datasets. A 7-billion-parameter model trained on 2 trillion tokens can outperform a 175-billion-parameter model trained on 300 billion tokens at a fraction of the inference cost. The same compute budget can produce a much more efficient model if allocated correctly between parameters and data.
In practice, the Chinchilla optimal point assumes you train the model once and then stop. But inference matters too. If a model will be deployed and run a billion times, a smaller model is much more valuable than a larger one with equal loss, because every inference query is cheaper. This "inference-optimal" perspective has pushed the field even further toward smaller, data-rich models.
Scaling also improves emergent capabilities. Models below a certain size cannot perform multi-step reasoning, follow complex instructions, or write working code. Above threshold scales, these abilities appear suddenly, a phenomenon called emergence. Whether emergence is truly discontinuous or an artifact of how we measure it remains debated, but the practical reality is clear: beyond certain parameter and data thresholds, qualitatively new behaviors appear that could not be reliably predicted from smaller-scale experiments.
Perplexity: The Standard Metric
Perplexity is the standard evaluation metric for language models. While cross-entropy loss is useful for training, perplexity provides a more interpretable measure of model quality. It answers the question: on average, how many tokens is the model choosing between at each step? A perfect model with perplexity 1.0 would always predict the next token with probability 1.0, never being surprised. A completely random model over a 50,000-token vocabulary would have perplexity 50,000. Real models sit somewhere in between, with state-of-the-art models achieving single-digit perplexities on well-studied benchmarks.
Perplexity is defined as the exponential of the average negative log-likelihood:
where:
- : perplexity, the evaluation metric (lower is better)
- : the total number of tokens in the evaluation dataset
- : the probability the model assigns to token given its context
- : the average cross-entropy loss per token
The exponential converts the average log-probability back to a probability-like scale. If the model achieves an average loss of 2.3 nats per token, the perplexity is .
The key intuition is this: a perplexity of 10 means the model is, on average, as uncertain as if it were choosing uniformly among 10 equally likely options at each step. A perplexity of 100 would mean 100-way uncertainty. State-of-the-art models achieve perplexities below 10 on standard benchmarks like WikiText-103, meaning they often predict the correct next word with high confidence.



def compute_perplexity(model, data, seq_len=64):
"""Compute perplexity on a dataset."""
model.eval()
total_loss = 0
total_tokens = 0
with torch.no_grad():
for start in range(0, len(data) - seq_len - 1, seq_len):
x = data[start : start + seq_len].unsqueeze(0)
y = data[start + 1 : start + seq_len + 1].unsqueeze(0)
logits = model(x)
loss = F.cross_entropy(
logits.view(-1, vocab_size), y.view(-1), reduction="sum"
)
total_loss += loss.item()
total_tokens += seq_len
avg_loss = total_loss / total_tokens
perplexity = np.exp(avg_loss)
return perplexity, avg_lossPerplexity on training data: 3.92 Average loss: 1.3653 Interpretation: Model is ~4-way uncertain on average
This perplexity on training data shows how well the model has fit the corpus. Since we're evaluating on the same text we trained on, this is an optimistic estimate. Held-out test perplexity would be higher. This reflects true generalization ability.
One important subtlety is that perplexity comparisons across models are only valid when the tokenizers are identical. A model with a larger vocabulary tends to tokenize text into fewer tokens, each carrying more information, and will therefore show different perplexity numbers even if the underlying prediction quality is the same. A model with 100,000-token vocabulary might achieve perplexity 5 on the same text where a model with 32,000-token vocabulary achieves perplexity 8, simply because the larger vocabulary tokenizes less aggressively. When comparing language models from different organizations, always check that the evaluation setup controls for tokenization differences.
Limitations and Impact
Causal language modeling has revolutionized NLP, but it comes with important limitations that shape how we use these models in practice. Understanding these limitations is not just academic. They determine which applications work well, which require engineering workarounds, and which are fundamentally unsuited to CLM-based systems.
The unidirectional constraint means CLM models cannot naturally incorporate future context. For tasks like filling in the middle of a sentence or bidirectional understanding, this is a fundamental limitation. Models like BERT use masked language modeling to capture bidirectional dependencies, trading generation capability for richer representations. In practice, many applications now use CLM models with careful prompting to work around this constraint. However, the constraint also creates an important advantage: CLM models can generate text of arbitrary length without architectural changes, because each new token only requires attending to the growing context window behind it.
Training on next-token prediction creates models that are excellent at mimicking patterns in training data but may struggle with factual accuracy. A model can fluently generate text about events that never happened or facts that are simply wrong. The objective optimizes for plausibility, not truth. This has led to significant research in retrieval augmentation and grounding techniques that anchor model outputs in verified information. The core problem is that a confident, fluent sentence and a confident, fluent lie are indistinguishable from the perspective of the CLM loss function. Both are rewarded equally if the tokens flow naturally from the preceding context.
The context length limitation is a real practical constraint. Standard transformer attention grows quadratically with sequence length, making very long contexts expensive. A sequence of 4,096 tokens requires roughly 16 times as much memory for attention than a sequence of 1,024 tokens. This means early CLM models could not maintain coherent reasoning over very long documents. Modern research on efficient attention (sparse attention, sliding window attention, linear attention variants) aims to extend this horizon, and recent models like Gemini 1.5 have pushed context lengths to millions of tokens, though at significant engineering cost.
The compute requirements are staggering. Training frontier models costs tens of millions of dollars and consumes megawatt-hours of electricity. This concentrates capability in a few well-resourced organizations and raises sustainability concerns. Techniques like distillation and quantization, along with efficient architectures, aim to democratize access, but the gap between frontier and accessible models remains wide. Training GPT-3 was estimated to cost over 4 million dollars in cloud compute in 2020, and frontier models since then have cost substantially more.
Despite these limitations, CLM has enabled capabilities that seemed impossible a decade ago. Modern LLMs can write code, translate languages, answer questions, and engage in open-ended conversation. They serve as foundations for instruction-following and reasoning, as well as tool-using agents. The simplicity of the objective belies the complexity of what emerges from optimizing it at scale. The same trained model supports both zero-shot prompting (where you simply ask the model to do something) and few-shot learning (where you demonstrate the task with examples), making it flexible across downstream applications.
Key Parameters
When training causal language models, a number of parameters significantly impact performance. Some are architectural, fixed at model design time. Others are optimization hyperparameters that you tune during training. Understanding what each controls helps you reason about tradeoffs without needing to memorize specific values.
The architectural parameters control model capacity:
- d_model: The hidden dimension of the transformer. Larger values (512, 768, 1024) increase capacity but require more compute. Our tiny model used 64. The hidden dimension determines the richness of the internal representations: a larger d_model gives the model more "space" to encode different aspects of meaning.
- n_heads: Number of attention heads. Should divide d_model evenly. More heads allow the model to attend to different aspects of context simultaneously. Each head learns a different pattern: one might track syntactic structure, another co-reference, another topical continuity.
- n_layers: Depth of the transformer stack. Deeper models can learn more complex patterns but are slower to train. Production models use 12-96 layers. Early layers tend to capture local syntactic patterns, while later layers capture more abstract semantic relationships.
The training hyperparameters control optimization:
- learning_rate: Typically 1e-4 to 6e-4 for transformers. Higher rates speed training but risk instability. Warmup schedules help stabilize early training by gradually increasing the learning rate from near zero over the first few thousand steps, allowing the model to settle into a stable optimization trajectory before taking large gradient steps.
- batch_size: Larger batches provide more stable gradients but require more memory. Modern LLMs use effective batch sizes in the millions of tokens, often achieved through gradient accumulation across many smaller physical batches when GPU memory is insufficient to hold a large batch directly.
- seq_len (context length): Maximum sequence length the model can process. Longer contexts enable better understanding but increase memory quadratically with attention. For a model with context length 4,096, each forward pass processes up to 4,096 tokens, but attention requires storing a 4,096 by 4,096 matrix per layer.
The generation-time parameter controls sampling:
- temperature: Controls randomness during generation. Values near 0 produce deterministic, repetitive output. Values near 1 produce diverse, sometimes incoherent text. Typical range: 0.7-1.0. The temperature divides the logits before the softmax: lower temperature sharpens the distribution toward the top prediction, while higher temperature flattens it, giving unlikely tokens a greater chance of being sampled.
Summary
Causal language modeling trains models to predict the next token given all previous tokens. This chapter covered the key concepts:
- Autoregressive factorization decomposes sequence probability into a product of conditional probabilities, each predicting one token from its left context
- The CLM objective minimizes cross-entropy loss between predicted and actual next tokens. This gives dense gradient signals from every position
- Causal masking in attention layers enforces the left-to-right information flow, preventing the model from seeing future tokens during training
- Training data quality and scale directly determine model capabilities, with modern LLMs consuming trillions of curated tokens
- Scaling laws show predictable improvements as compute and data, along with parameters, increase, following power-law relationships
- Perplexity measures model quality as the exponential of average loss, with lower values indicating better predictions
The next chapter explores masked language modeling, a bidirectional alternative that trades generation capability for richer contextual representations.
Quiz
Ready to test your understanding? Take this quick quiz to reinforce what you've learned about causal language modeling and next-token prediction.
Causal Language Modeling
Reference
Citation details
Cite or share this article.
Continue with the full handbook
This chapter is part of Language AI Handbook. Use the handbook page to browse the complete table of contents and continue reading in sequence.
Explore Language AI HandbookStay up to date
Get articles, book updates, and news delivered to your inbox.
No spam, unsubscribe anytime.
Join the community
Sign in to remove popups, track your reading progress, and join the discussion.

Comments
No comments yet. Be the first to share your thoughts!